{"openapi":"3.1.0","info":{"title":"Final Frame API","version":"1.0.0","description":"The media supply chain platform, as an API. One key belongs to one organisation. Every list is a page of at most 100 with a cursor. Every write takes an Idempotency-Key. Media never passes through: downloads are signed URLs that expire."},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Organisation settings, Integrations. Send it as Authorization: Bearer <key>."},"oauth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"An access token obtained through sign-in (OAuth 2.1 with PKCE), as MCP clients and connectors do."}},"schemas":{"Error":{"type":"object","description":"Every error: a code from the catalogue, what to do next, where the code is explained, and whether the same request can succeed after a wait.","required":["error","message","docs","retryable"],"properties":{"error":{"type":"string","enum":["unauthenticated","forbidden","not_found","invalid_input","conflict","delivery_doubt","qc_failed","invalid_credentials","access_ended","content_refused","rate_limited","internal","unavailable"],"description":"Stable: branch on this."},"message":{"type":"string","description":"For a person. Its wording may change."},"detail":{"type":"object","additionalProperties":true,"description":"What a client needs to act on some refusals, such as the rows a QC gate refused."},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true,"example":{"error":"forbidden","message":"This credential does not include audit.view.","docs":"https://www.final-frame.com/docs/troubleshooting#forbidden","retryable":false}}}},"paths":{"/v1/proposals/{proposalId}":{"get":{"summary":"Where a held write stands","tags":["Approvals"],"description":"When the organisation has a person approve agent writes, a write answers 202 with a proposal and this address. pending: waiting. approved: run, with the status and body the write answered. failed: approved, then refused when it ran, with the reason. declined and expired: nothing was written. Only the credential that made the proposal can read it.\n\nPermission: none beyond a valid credential of the organisation.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"proposalId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","running","approved","failed","declined","expired"]},"channel":{"type":"string"},"tool":{"type":"string"},"createdAt":{"type":"string"},"expiresAt":{"type":"string"},"decidedAt":{"type":["null","string"]},"note":{"type":["null","string"],"description":"The approver’s note, if they left one."},"message":{"type":["null","string"],"description":"What happened, in words, once the proposal is closed without a write."},"result":{"type":"object","description":"The write’s own answer, once approved or failed.","properties":{"status":{"type":"integer"},"body":{}},"additionalProperties":true}},"additionalProperties":true}}}},"401":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getProposal","x-permission":"none","x-permissions":[]}},"/v1/me":{"get":{"summary":"Who is calling","tags":["Access"],"description":"The organisation this credential belongs to, who it acts as, and the permissions it carries, and the environment answering. Use it to check a key before anything else.\n\nPermission: none beyond a valid credential of the organisation.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"environment":{"type":"string","enum":["sandbox","qa","production","local"],"description":"Where this answer came from; the same value as the X-FF-Environment header."},"organisation":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}},"additionalProperties":true},"org":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string","description":"The same value as the X-FF-Org header."},"name":{"type":"string"},"sandbox":{"type":"boolean","description":"True in the Sandbox: fictional data, reset on a schedule."}},"additionalProperties":true},"actor":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string"}},"additionalProperties":true},"credential":{"type":"object","properties":{"kind":{"type":"string","enum":["api_key","oauth","session"]},"id":{"type":["string","null"]}},"additionalProperties":true},"permissions":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}}}},"401":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getMe","x-permission":"none","x-permissions":[]}},"/v1/reference/rights":{"get":{"summary":"The rights vocabulary","tags":["Reference"],"description":"The rights taxonomy: a tree of categories, groups and leaves with stable codes. Granting a node grants its descendants. Also the exclusivity kinds, the legacy flat list every stored deal still uses, and the territory regions with their member countries.\n\nPermission: none beyond a valid credential of the organisation.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"taxonomy":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"parent":{"type":["string","null"]},"level":{"type":"string","enum":["category","group","leaf"]},"definition":{"type":"string"},"synonyms":{"type":"array","items":{"type":"string"}},"ema":{"type":["string","null"],"description":"The EMA Avails LicenseType, where one exists."},"era":{"type":"string","enum":["traditional","digital","emerging"]}},"additionalProperties":true}},"categories":{"type":"array","items":{"type":"string"}},"legacy":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"note":{"type":"string"}},"additionalProperties":true}},"exclusivity":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"note":{"type":"string"}},"additionalProperties":true}},"regions":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"members":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getRightsVocabulary","x-permission":"none","x-permissions":[]}},"/v1/rights/works":{"get":{"summary":"Every work and its rights position","tags":["Rights"],"description":"One row per work across the organisation’s deals, with what is held, and, when a territory and window are given, a cell per right saying whether it is available there and then. Rights come from the vocabulary; ?rights= is a comma list of codes, or \"categories\" for the sixteen top-level ones.\n\nPermission: none beyond a valid credential of the organisation.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"q","required":false},{"schema":{"type":"string","enum":["owned","licensed_in","mixed","not_recorded"]},"in":"query","name":"position","required":false,"description":"What the organisation holds in the work: owned, licensed in, both, or nothing recorded."},{"schema":{"type":"string","maxLength":20},"in":"query","name":"territory","required":false,"description":"A country code, a region code or WORLD. Default WORLD."},{"schema":{"type":"string"},"in":"query","name":"from","required":false,"description":"ISO date. Default today."},{"schema":{"type":"string"},"in":"query","name":"to","required":false,"description":"ISO date. Default open."},{"schema":{"type":"string","maxLength":1200},"in":"query","name":"rights","required":false},{"schema":{"type":"boolean"},"in":"query","name":"exclusive","required":false,"description":"Ask for exclusive availability."},{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"identity":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"},"productionYear":{"type":["number","null"]},"position":{"type":"string","enum":["owned","licensed_in","mixed","not_recorded"],"description":"What the organisation holds in the work: owned, licensed in, both, or nothing recorded."},"dealCount":{"type":"integer"},"cells":{"type":"object","description":"Keyed by right code.","properties":{},"additionalProperties":true}},"additionalProperties":true}},"next_cursor":{"type":["string","null"]},"where":{"type":"object","properties":{"territory":{"type":"string"},"starts":{"type":"string"},"ends":{"type":["string","null"]}},"additionalProperties":true}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listRightsWorks","x-permission":"none","x-permissions":[]}},"/v1/rights/works/{identity}":{"get":{"summary":"One work’s rights position","tags":["Rights"],"description":"What the organisation holds in the work, what its deals have licensed out, and what narrows either. The identity is as the list gives it, URL-encoded.\n\nPermission: none beyond a valid credential of the organisation.","parameters":[{"schema":{"type":"string","maxLength":400},"in":"path","name":"identity","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"identity":{"type":"string","description":"The work’s key across deals: eidr:, imdb:, work: or name:|year."},"name":{"type":"string"},"kind":{"type":"string"},"productionYear":{"type":["number","null"]},"identifiers":{"type":"object","properties":{"eidr":{"type":["string","null"]},"imdb":{"type":["string","null"]}},"additionalProperties":true},"position":{"type":"string","enum":["owned","licensed_in","mixed","not_recorded"],"description":"What the organisation holds in the work: owned, licensed in, both, or nothing recorded."},"deals":{"type":"array","items":{"type":"object","properties":{"projectId":{"type":"string","format":"uuid"},"projectName":{"type":"string"},"reference":{"type":["string","null"]},"direction":{"type":["string","null"]},"status":{"type":"string"},"party":{"type":["string","null"]}},"additionalProperties":true}},"holdings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"source":{"type":"string"},"counterparty":{"type":["string","null"]},"rights":{"type":"array","items":{"type":"string"}},"territories":{"type":"array","items":{"type":"string"}},"starts":{"type":["string","null"]},"ends":{"type":["string","null"]},"endKind":{"type":"string"},"exclusive":{"type":"boolean"},"derived":{"type":"boolean","description":"True when implied by an inbound deal rather than recorded by hand."}},"additionalProperties":true}},"grants":{"type":"array","items":{"type":"object","description":"Rights out: what the organisation’s deals have licensed.","properties":{"projectId":{"type":"string","format":"uuid"},"projectName":{"type":"string"},"licensee":{"type":["string","null"]},"right":{"type":"string"},"territories":{"type":"array","items":{"type":"string"}},"starts":{"type":["string","null"]},"ends":{"type":["string","null"]},"exclusive":{"type":"boolean"}},"additionalProperties":true}},"restrictions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string"},"rights":{"type":"array","items":{"type":"string"}},"territories":{"type":"array","items":{"type":"string"}},"starts":{"type":["string","null"]},"ends":{"type":["string","null"]},"note":{"type":["string","null"]}},"additionalProperties":true}}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getRightsWork","x-permission":"none","x-permissions":[]}},"/v1/rights/works/{identity}/children":{"get":{"summary":"A show’s seasons and episodes, each with its rights","tags":["Rights"],"description":"The seasons of a show and the episodes in each, every one with its own position and a cell per right. What the show holds and licenses applies to all of them; a deal that carries some seasons binds those seasons only; a record kept against one season or episode applies there alone. Season identities are the show’s plus \"#s2\", episodes \"#s2e3\", and every rights endpoint accepts them.\n\nPermission: none beyond a valid credential of the organisation.","parameters":[{"schema":{"type":"string","maxLength":20},"in":"query","name":"territory","required":false,"description":"A country code, a region code or WORLD. Default WORLD."},{"schema":{"type":"string"},"in":"query","name":"from","required":false,"description":"ISO date. Default today."},{"schema":{"type":"string"},"in":"query","name":"to","required":false,"description":"ISO date. Default open."},{"schema":{"type":"string","maxLength":1200},"in":"query","name":"rights","required":false},{"schema":{"type":"boolean"},"in":"query","name":"exclusive","required":false},{"schema":{"type":"integer","minimum":0},"in":"query","name":"season","required":false,"description":"The season whose episodes to work out. Without it every season comes with an episodeCount and no episode rows."},{"schema":{"type":"string","maxLength":400},"in":"path","name":"identity","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"identity":{"type":"string"},"name":{"type":"string"},"seasons":{"type":"array","items":{"type":"object","properties":{"identity":{"type":"string"},"number":{"type":"integer"},"name":{"type":["string","null"]},"position":{"type":"string","enum":["owned","licensed_in","mixed","not_recorded"],"description":"What the organisation holds in the work: owned, licensed in, both, or nothing recorded."},"cells":{"type":"object","description":"Keyed by right code.","properties":{},"additionalProperties":true},"episodeCount":{"type":"integer"},"episodes":{"type":"array","items":{"type":"object","properties":{"identity":{"type":"string"},"number":{"type":"integer"},"name":{"type":["string","null"]},"position":{"type":"string","enum":["owned","licensed_in","mixed","not_recorded"],"description":"What the organisation holds in the work: owned, licensed in, both, or nothing recorded."},"cells":{"type":"object","description":"Keyed by right code.","properties":{},"additionalProperties":true}},"additionalProperties":true}}},"additionalProperties":true}}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listRightsWorkChildren","x-permission":"none","x-permissions":[]}},"/v1/rights/map":{"get":{"summary":"One right across the world","tags":["Rights"],"description":"For every country: how many works are available there in the window, how many are licensed out (shared or exclusively) and to whom, how many are held back or not held. With ?identity=, one work’s state in every country instead. Country codes are ISO 3166-1 alpha-2.\n\nPermission: none beyond a valid credential of the organisation.","parameters":[{"schema":{"type":"string","maxLength":60},"in":"query","name":"right","required":true},{"schema":{"type":"string"},"in":"query","name":"from","required":false,"description":"ISO date. Default today."},{"schema":{"type":"string"},"in":"query","name":"to","required":false,"description":"ISO date. Default open."},{"schema":{"type":"boolean"},"in":"query","name":"exclusive","required":false},{"schema":{"type":"string","enum":["film","series","extras"]},"in":"query","name":"kind","required":false},{"schema":{"type":"string","enum":["owned","licensed_in","mixed","not_recorded"]},"in":"query","name":"position","required":false,"description":"What the organisation holds in the work: owned, licensed in, both, or nothing recorded."},{"schema":{"type":"string","maxLength":200},"in":"query","name":"q","required":false},{"schema":{"type":"string","maxLength":400},"in":"query","name":"identity","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"right":{"type":"string"},"mode":{"type":"string","enum":["library","work"]},"works":{"type":"integer"},"where":{"type":"object","properties":{"starts":{"type":"string"},"ends":{"type":["string","null"]},"exclusive":{"type":"boolean"}},"additionalProperties":true},"countries":{"type":"object","description":"Keyed by country code: total, available, shared, partial, exclusive, heldBack, notHeld, notRecorded, unknown, licensees [{name, count}], and for one work its state and label.","properties":{},"additionalProperties":true}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getRightsMap","x-permission":"none","x-permissions":[]}},"/v1/rights/availability":{"get":{"summary":"Is this right available, here, then?","tags":["Rights"],"description":"One answer with its reasons: rights in, less exclusive rights out, less restrictions, over the window. match decides how the window must be met: cover (the default, the whole window), overlap (any of it), start_within, end_within.\n\nPermission: none beyond a valid credential of the organisation.","parameters":[{"schema":{"type":"string","maxLength":400},"in":"query","name":"identity","required":true},{"schema":{"type":"string","maxLength":60},"in":"query","name":"right","required":true},{"schema":{"type":"string","maxLength":20},"in":"query","name":"territory","required":true},{"schema":{"type":"string"},"in":"query","name":"from","required":true},{"schema":{"type":"string"},"in":"query","name":"to","required":false},{"schema":{"type":"string","enum":["cover","overlap","start_within","end_within"]},"in":"query","name":"match","required":false},{"schema":{"type":"boolean"},"in":"query","name":"exclusive","required":false},{"schema":{"type":"integer","minimum":0,"maximum":3650},"in":"query","name":"minWindowDays","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"work":{"type":"object","properties":{"identity":{"type":"string"},"name":{"type":"string"},"position":{"type":"string","enum":["owned","licensed_in","mixed","not_recorded"],"description":"What the organisation holds in the work: owned, licensed in, both, or nothing recorded."}},"additionalProperties":true},"answer":{"type":"object","properties":{"state":{"type":"string","enum":["available","available_non_exclusive","partial","licensed_exclusive","held_back","not_held","not_held_exclusively","not_recorded","assumed","unknown"]},"label":{"type":"string"},"licensees":{"type":"array","items":{"type":"string"}},"intervals":{"type":"array","items":{"type":"object","description":"Where the right is still available inside the window.","properties":{"starts":{"type":["string","null"]},"ends":{"type":["string","null"]}},"additionalProperties":true}},"reasons":{"type":"array","items":{"type":"string"}},"coverage":{"type":"object","description":"How many leaf-and-country cells the answer covers, and how many are available.","properties":{"total":{"type":"integer"},"available":{"type":"integer"}},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"checkAvailability","x-permission":"none","x-permissions":[]}},"/v1/projects":{"get":{"summary":"List projects","tags":["Projects"],"description":"Every deal the credential may see, newest first.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"A project is one deal: the work agreed, in one direction, with one counterparty.","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":["string","null"],"description":"The deal reference people quote, e.g. FM-7KQ2-P9XA."},"name":{"type":"string"},"status":{"type":"string"},"direction":{"type":["string","null"],"description":"outbound: this Org is sending. inbound: this Org is receiving."},"group":{"type":"object","description":"The Group the deal hangs off, or null at the Org root.","properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]}},"additionalProperties":true},"client":{"type":["string","null"]},"distributor":{"type":["string","null"]},"counterparty":{"type":"object","properties":{"orgId":{"type":["string","null"]},"name":{"type":["string","null"]}},"additionalProperties":true},"dealReference":{"type":["string","null"],"description":"The counterparty’s own reference."},"deliveryStarts":{"type":["string","null"],"description":"ISO date."},"deliveryEnds":{"type":["string","null"],"description":"ISO date."},"contentTier":{"type":["string","null"]},"licence":{"type":"object","properties":{"starts":{"type":["string","null"]},"ends":{"type":["string","null"]},"months":{"type":["number","null"]},"fee":{"type":["string","null"],"description":"A decimal string, never a float."},"currency":{"type":["string","null"]}},"additionalProperties":true},"rights":{"type":"object","properties":{"granted":{"type":"array","items":{"type":"string"}},"reserved":{"type":"array","items":{"type":"string"}},"exclusivity":{"type":["string","null"],"description":"exclusive, first_window or non_exclusive."},"territories":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true},"example":{"data":[{"id":"7d1f4c2a-3b5e-4f6a-9c8d-1e2f3a4b5c6d","reference":"FF-0142","name":"Nordic SVOD licence 2027","status":"active","direction":"outbound","group":{"id":null,"name":null},"client":"Northlight Streaming","distributor":null,"counterparty":{"orgId":null,"name":"Northlight Streaming"},"dealReference":"NLS-2027-014","deliveryStarts":"2027-02-01","deliveryEnds":"2027-03-31","contentTier":null,"licence":{"starts":"2027-04-01","ends":"2029-03-31","months":24,"fee":"250000.00","currency":"EUR"},"rights":{"granted":["svod"],"reserved":["tvod"],"exclusivity":"exclusive","territories":["DK","FI","NO","SE"]},"createdAt":"2027-01-15T10:30:00.000Z","updatedAt":"2027-01-15T10:30:00.000Z"}],"next_cursor":null}}}},"401":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listProjects","x-permission":"project.view","x-permissions":["project.view"]},"post":{"summary":"Create a project","tags":["Projects"],"description":"One deal, in one direction. It inherits the organisation’s house format, and its markets are proposed from its territories, as a deal made in the product does. Needs project.create at the organisation, or at the Group named by groupId. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: project.create.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"client":{"type":["string","null"],"maxLength":200,"description":"The client, as a name."},"distributor":{"type":["string","null"],"maxLength":200},"dealReference":{"type":["string","null"],"maxLength":120,"description":"The counterparty’s own reference for the deal."},"deliveryStarts":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"ISO date, or null to clear."},"deliveryEnds":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"ISO date, or null to clear."},"contentTier":{"type":["string","null"],"maxLength":60,"description":"A term key from the organisation’s tiers taxonomy. Setting it on the deal sets it on every title."},"rightsGranted":{"type":"array","maxItems":30,"items":{"type":"string","maxLength":40},"description":"Right codes from GET /v1/reference/rights."},"rightsReserved":{"type":"array","maxItems":30,"items":{"type":"string","maxLength":40},"description":"Right codes from GET /v1/reference/rights."},"exclusivity":{"type":["string","null"],"maxLength":40,"description":"exclusive, first_window or non_exclusive."},"licenceStarts":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"ISO date, or null to clear."},"licenceEnds":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"ISO date, or null to clear."},"licenceMonths":{"type":["integer","null"],"minimum":0,"maximum":1200},"licenceFee":{"anyOf":[{"type":"string","maxLength":30},{"type":"number"},{"type":"null"}],"description":"Prefer a decimal string."},"licenceCurrency":{"type":["string","null"],"minLength":3,"maxLength":3,"description":"ISO 4217."},"territories":{"type":"array","maxItems":300,"items":{"type":"string","minLength":2,"maxLength":20},"description":"Territory codes: ISO 3166-1 alpha-2, a region code or WORLD. The deal’s markets are proposed from them."},"groupId":{"type":["string","null"],"format":"uuid","description":"The Group the deal hangs off. Absent or null: the organisation itself."},"direction":{"type":"string","enum":["outbound","inbound"],"default":"outbound","description":"outbound: this organisation sends. inbound: it receives."}}},"example":{"name":"Nordic SVOD licence 2027","direction":"outbound","client":"Northlight Streaming","dealReference":"NLS-2027-014","deliveryStarts":"2027-02-01","deliveryEnds":"2027-03-31","rightsGranted":["svod"],"exclusivity":"exclusive","territories":["DK","FI","NO","SE"]}}}},"parameters":[{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"A project is one deal: the work agreed, in one direction, with one counterparty.","content":{"application/json":{"schema":{"type":"object","description":"A project is one deal: the work agreed, in one direction, with one counterparty.","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":["string","null"],"description":"The deal reference people quote, e.g. FM-7KQ2-P9XA."},"name":{"type":"string"},"status":{"type":"string"},"direction":{"type":["string","null"],"description":"outbound: this Org is sending. inbound: this Org is receiving."},"group":{"type":"object","description":"The Group the deal hangs off, or null at the Org root.","properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]}},"additionalProperties":true},"client":{"type":["string","null"]},"distributor":{"type":["string","null"]},"counterparty":{"type":"object","properties":{"orgId":{"type":["string","null"]},"name":{"type":["string","null"]}},"additionalProperties":true},"dealReference":{"type":["string","null"],"description":"The counterparty’s own reference."},"deliveryStarts":{"type":["string","null"],"description":"ISO date."},"deliveryEnds":{"type":["string","null"],"description":"ISO date."},"contentTier":{"type":["string","null"]},"licence":{"type":"object","properties":{"starts":{"type":["string","null"]},"ends":{"type":["string","null"]},"months":{"type":["number","null"]},"fee":{"type":["string","null"],"description":"A decimal string, never a float."},"currency":{"type":["string","null"]}},"additionalProperties":true},"rights":{"type":"object","properties":{"granted":{"type":"array","items":{"type":"string"}},"reserved":{"type":"array","items":{"type":"string"}},"exclusivity":{"type":["string","null"],"description":"exclusive, first_window or non_exclusive."},"territories":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"additionalProperties":true},"example":{"id":"7d1f4c2a-3b5e-4f6a-9c8d-1e2f3a4b5c6d","reference":"FF-0142","name":"Nordic SVOD licence 2027","status":"active","direction":"outbound","group":{"id":null,"name":null},"client":"Northlight Streaming","distributor":null,"counterparty":{"orgId":null,"name":"Northlight Streaming"},"dealReference":"NLS-2027-014","deliveryStarts":"2027-02-01","deliveryEnds":"2027-03-31","contentTier":null,"licence":{"starts":"2027-04-01","ends":"2029-03-31","months":24,"fee":"250000.00","currency":"EUR"},"rights":{"granted":["svod"],"reserved":["tvod"],"exclusivity":"exclusive","territories":["DK","FI","NO","SE"]},"createdAt":"2027-01-15T10:30:00.000Z","updatedAt":"2027-01-15T10:30:00.000Z"}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createProject","x-permission":"project.create","x-permissions":["project.create"],"x-idempotent":true}},"/v1/projects/{projectId}":{"get":{"summary":"One project","tags":["Projects"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true}],"responses":{"200":{"description":"A project is one deal: the work agreed, in one direction, with one counterparty.","content":{"application/json":{"schema":{"type":"object","description":"A project is one deal: the work agreed, in one direction, with one counterparty.","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":["string","null"],"description":"The deal reference people quote, e.g. FM-7KQ2-P9XA."},"name":{"type":"string"},"status":{"type":"string"},"direction":{"type":["string","null"],"description":"outbound: this Org is sending. inbound: this Org is receiving."},"group":{"type":"object","description":"The Group the deal hangs off, or null at the Org root.","properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]}},"additionalProperties":true},"client":{"type":["string","null"]},"distributor":{"type":["string","null"]},"counterparty":{"type":"object","properties":{"orgId":{"type":["string","null"]},"name":{"type":["string","null"]}},"additionalProperties":true},"dealReference":{"type":["string","null"],"description":"The counterparty’s own reference."},"deliveryStarts":{"type":["string","null"],"description":"ISO date."},"deliveryEnds":{"type":["string","null"],"description":"ISO date."},"contentTier":{"type":["string","null"]},"licence":{"type":"object","properties":{"starts":{"type":["string","null"]},"ends":{"type":["string","null"]},"months":{"type":["number","null"]},"fee":{"type":["string","null"],"description":"A decimal string, never a float."},"currency":{"type":["string","null"]}},"additionalProperties":true},"rights":{"type":"object","properties":{"granted":{"type":"array","items":{"type":"string"}},"reserved":{"type":"array","items":{"type":"string"}},"exclusivity":{"type":["string","null"],"description":"exclusive, first_window or non_exclusive."},"territories":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"additionalProperties":true},"example":{"id":"7d1f4c2a-3b5e-4f6a-9c8d-1e2f3a4b5c6d","reference":"FF-0142","name":"Nordic SVOD licence 2027","status":"active","direction":"outbound","group":{"id":null,"name":null},"client":"Northlight Streaming","distributor":null,"counterparty":{"orgId":null,"name":"Northlight Streaming"},"dealReference":"NLS-2027-014","deliveryStarts":"2027-02-01","deliveryEnds":"2027-03-31","contentTier":null,"licence":{"starts":"2027-04-01","ends":"2029-03-31","months":24,"fee":"250000.00","currency":"EUR"},"rights":{"granted":["svod"],"reserved":["tvod"],"exclusivity":"exclusive","territories":["DK","FI","NO","SE"]},"createdAt":"2027-01-15T10:30:00.000Z","updatedAt":"2027-01-15T10:30:00.000Z"}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getProject","x-permission":"project.view","x-permissions":["project.view"],"description":"Permission: project.view."},"patch":{"summary":"Change a project","tags":["Projects"],"description":"Only the fields sent change. territories replaces the list; a territory already on the deal keeps its languages. deliverySpecIds names the organisation’s own or industry delivery specs the deal delivers against. On a deal received from another organisation, the sender’s handoff permissions decide what may change. Needs project.configure. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: project.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"client":{"type":["string","null"],"maxLength":200,"description":"The client, as a name."},"distributor":{"type":["string","null"],"maxLength":200},"dealReference":{"type":["string","null"],"maxLength":120,"description":"The counterparty’s own reference for the deal."},"deliveryStarts":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"ISO date, or null to clear."},"deliveryEnds":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"ISO date, or null to clear."},"contentTier":{"type":["string","null"],"maxLength":60,"description":"A term key from the organisation’s tiers taxonomy. Setting it on the deal sets it on every title."},"rightsGranted":{"type":"array","maxItems":30,"items":{"type":"string","maxLength":40},"description":"Right codes from GET /v1/reference/rights."},"rightsReserved":{"type":"array","maxItems":30,"items":{"type":"string","maxLength":40},"description":"Right codes from GET /v1/reference/rights."},"exclusivity":{"type":["string","null"],"maxLength":40,"description":"exclusive, first_window or non_exclusive."},"licenceStarts":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"ISO date, or null to clear."},"licenceEnds":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"ISO date, or null to clear."},"licenceMonths":{"type":["integer","null"],"minimum":0,"maximum":1200},"licenceFee":{"anyOf":[{"type":"string","maxLength":30},{"type":"number"},{"type":"null"}],"description":"Prefer a decimal string."},"licenceCurrency":{"type":["string","null"],"minLength":3,"maxLength":3,"description":"ISO 4217."},"territories":{"type":"array","maxItems":300,"items":{"type":"string","minLength":2,"maxLength":20},"description":"Territory codes: ISO 3166-1 alpha-2, a region code or WORLD. The deal’s markets are proposed from them."},"notes":{"type":["string","null"],"maxLength":2000},"deliverySpecIds":{"type":"array","maxItems":50,"items":{"type":"string","format":"uuid"},"description":"Delivery spec ids, from GET /v1/delivery-specs or GET /v1/industry-specs."}}},"example":{"deliveryEnds":"2027-04-15"}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"A project is one deal: the work agreed, in one direction, with one counterparty.","content":{"application/json":{"schema":{"type":"object","description":"A project is one deal: the work agreed, in one direction, with one counterparty.","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":["string","null"],"description":"The deal reference people quote, e.g. FM-7KQ2-P9XA."},"name":{"type":"string"},"status":{"type":"string"},"direction":{"type":["string","null"],"description":"outbound: this Org is sending. inbound: this Org is receiving."},"group":{"type":"object","description":"The Group the deal hangs off, or null at the Org root.","properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]}},"additionalProperties":true},"client":{"type":["string","null"]},"distributor":{"type":["string","null"]},"counterparty":{"type":"object","properties":{"orgId":{"type":["string","null"]},"name":{"type":["string","null"]}},"additionalProperties":true},"dealReference":{"type":["string","null"],"description":"The counterparty’s own reference."},"deliveryStarts":{"type":["string","null"],"description":"ISO date."},"deliveryEnds":{"type":["string","null"],"description":"ISO date."},"contentTier":{"type":["string","null"]},"licence":{"type":"object","properties":{"starts":{"type":["string","null"]},"ends":{"type":["string","null"]},"months":{"type":["number","null"]},"fee":{"type":["string","null"],"description":"A decimal string, never a float."},"currency":{"type":["string","null"]}},"additionalProperties":true},"rights":{"type":"object","properties":{"granted":{"type":"array","items":{"type":"string"}},"reserved":{"type":"array","items":{"type":"string"}},"exclusivity":{"type":["string","null"],"description":"exclusive, first_window or non_exclusive."},"territories":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"additionalProperties":true},"example":{"id":"7d1f4c2a-3b5e-4f6a-9c8d-1e2f3a4b5c6d","reference":"FF-0142","name":"Nordic SVOD licence 2027","status":"active","direction":"outbound","group":{"id":null,"name":null},"client":"Northlight Streaming","distributor":null,"counterparty":{"orgId":null,"name":"Northlight Streaming"},"dealReference":"NLS-2027-014","deliveryStarts":"2027-02-01","deliveryEnds":"2027-04-15","contentTier":null,"licence":{"starts":"2027-04-01","ends":"2029-03-31","months":24,"fee":"250000.00","currency":"EUR"},"rights":{"granted":["svod"],"reserved":["tvod"],"exclusivity":"exclusive","territories":["DK","FI","NO","SE"]},"createdAt":"2027-01-15T10:30:00.000Z","updatedAt":"2027-01-15T10:30:00.000Z"}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"updateProject","x-permission":"project.configure","x-permissions":["project.configure"],"x-idempotent":true},"delete":{"summary":"Delete a project","tags":["Projects"],"description":"Deletes the deal and everything on it. Its files are removed from storage by the worker and can be recovered for thirty days. A deal handed to another organisation, who work on its files, cannot be deleted (409). The action log keeps the deletion. Needs project.delete. Idempotent on the Idempotency-Key: a retry answers the first deletion.\n\nPermission: project.delete.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"},"files":{"type":"string","enum":["purge_queued"],"description":"The worker removes the deal’s files; recoverable for thirty days."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"deleteProject","x-permission":"project.delete","x-permissions":["project.delete"],"x-idempotent":true}},"/v1/projects/{projectId}/rights":{"get":{"summary":"The rights a deal grants","tags":["Rights"],"description":"The deal’s commercial terms as grants: one per right, with territories, window and exclusivity, plus its holdbacks and what it reserves. Read from the deal memo where one was read.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"projectId":{"type":"string","format":"uuid"},"direction":{"type":["string","null"]},"exclusivity":{"type":["string","null"]},"territories":{"type":"array","items":{"type":"string"}},"window":{"type":"object","properties":{"starts":{"type":["string","null"]},"ends":{"type":["string","null"]},"months":{"type":["number","null"]}},"additionalProperties":true},"grants":{"type":"array","items":{"type":"object","description":"One right, in some territories, for some window, exclusive or not.","properties":{"right":{"type":"string","description":"A right code from the vocabulary (GET /v1/reference/rights)."},"territories":{"type":"array","items":{"type":"string"}},"starts":{"type":["string","null"],"description":"ISO date, or null when the deal does not say."},"ends":{"type":["string","null"],"description":"ISO date, or null when open-ended or unknown."},"exclusive":{"type":"boolean"}},"additionalProperties":true}},"reserved":{"type":"array","items":{"type":"string"}},"holdbacks":{"type":"array","items":{"type":"object","properties":{"right":{"type":"string"},"months":{"type":["number","null"]},"from":{"type":["string","null"]},"note":{"type":["string","null"]}},"additionalProperties":true}}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getProjectRights","x-permission":"project.view","x-permissions":["project.view"]}},"/v1/projects/{projectId}/titles":{"get":{"summary":"The deal’s catalogue","tags":["Titles"],"description":"Films, shows, seasons and episodes in this deal, in catalogue order.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"One row of the deal’s catalogue: a film, a show, a season or an episode.","properties":{"id":{"type":"string","format":"uuid"},"titleId":{"type":"string","format":"uuid"},"level":{"type":"string","enum":["title","season","episode"]},"name":{"type":"string"},"kind":{"type":"string"},"seasonNumber":{"type":["number","null"]},"episodeNumber":{"type":["number","null"]},"episodeCode":{"type":["string","null"]},"productionYear":{"type":["number","null"]},"durationMinutes":{"type":["number","null"]},"deliveryDate":{"type":["string","null"]},"deliverables":{"type":"object","properties":{"total":{"type":"integer"},"settled":{"type":"integer"}},"additionalProperties":true}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true},"example":{"data":[{"id":"a3c9e1b2-4d5f-4a6b-8c7d-9e0f1a2b3c4d","reference":"T-0007","name":"The Long Harbour","kind":"feature"}],"next_cursor":null}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listTitles","x-permission":"project.view","x-permissions":["project.view"]},"post":{"summary":"Add a title, a season or an episode","tags":["Titles"],"description":"level title (the default) adds a film or a show: name and kind are needed, and nameLanguage files the name under that language too. level season adds a season to a show: titleId (the show) and number. level episode adds an episode: titleId (the show), seasonNumber and number; a season not yet recorded is made. Needs project.configure. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: project.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"level":{"type":"string","enum":["title","season","episode"],"default":"title"},"name":{"type":["string","null"],"maxLength":300},"kind":{"type":"string","enum":["feature","series","supplemental","other"],"description":"For a title: feature, series, supplemental or other."},"originalLanguage":{"type":["string","null"],"maxLength":35,"description":"BCP 47."},"nameLanguage":{"type":["string","null"],"maxLength":35,"description":"The language the name is in, BCP 47."},"titleId":{"type":"string","format":"uuid","description":"For a season or an episode: the show."},"seasonNumber":{"type":"integer","minimum":0,"maximum":9999},"number":{"type":"integer","minimum":0,"maximum":99999,"description":"The season’s or the episode’s number."}}},"example":{"level":"title","name":"The Long Harbour","kind":"feature","originalLanguage":"en"}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"A title as this deal holds it. Ask for the heavier parts with ?include=metadata,credits,certificates,companies.","content":{"application/json":{"schema":{"type":"object","description":"A title as this deal holds it. Ask for the heavier parts with ?include=metadata,credits,certificates,companies.","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":"string","description":"The Final Frame Title ID."},"name":{"type":"string"},"kind":{"type":"string"},"subtype":{"type":["string","null"]},"number":{"type":["number","null"]},"originalLanguage":{"type":["string","null"]},"productionYear":{"type":["number","null"]},"runtimeMinutes":{"type":["number","null"]},"countryOfOrigin":{"type":["string","null"]},"contentTier":{"type":["string","null"]},"identifiers":{"type":"object","properties":{"eidr":{"type":["string","null"]},"imdb":{"type":["string","null"]},"tmdb":{"type":["string","null"]},"commonSense":{"type":["string","null"]},"providerHouse":{"type":["string","null"]},"receiverHouse":{"type":["string","null"]},"wikidata":{"type":["string","null"]}},"additionalProperties":true},"fields":{"type":"object","description":"The tenant’s own fields, keyed by their schema keys. Stable keys, open bag.","properties":{},"additionalProperties":true},"metadata":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string"},"name":{"type":["string","null"]},"tagline":{"type":["string","null"]},"synopsis120":{"type":["string","null"]},"synopsis240":{"type":["string","null"]},"synopsis500":{"type":["string","null"]},"synopsis1000":{"type":["string","null"]},"genres":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}},"credits":{"type":"array","items":{"type":"object","properties":{"person":{"type":"string"},"credit":{"type":"string"},"characterName":{"type":["string","null"]},"ranking":{"type":"integer"}},"additionalProperties":true}},"certificates":{"type":"array","items":{"type":"object","properties":{"territory":{"type":"string"},"body":{"type":["string","null"]},"value":{"type":["string","null"]}},"additionalProperties":true}},"companies":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"role":{"type":"string"}},"additionalProperties":true}}},"additionalProperties":true},"example":{"id":"a3c9e1b2-4d5f-4a6b-8c7d-9e0f1a2b3c4d","reference":"T-0007","name":"The Long Harbour","kind":"feature","subtype":null,"number":null,"originalLanguage":"en","productionYear":2026,"runtimeMinutes":112,"countryOfOrigin":"GB","contentTier":null,"identifiers":{"eidr":"10.5240/1F2A-3B4C-5D6E-7F80-9A1B-C","imdb":"tt9876543","tmdb":null,"commonSense":null,"providerHouse":null,"receiverHouse":null,"wikidata":null},"fields":{},"metadata":[],"credits":[],"certificates":[],"companies":[]}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createTitle","x-permission":"project.configure","x-permissions":["project.configure"],"x-idempotent":true}},"/v1/projects/{projectId}/titles/{titleId}":{"get":{"summary":"One title","tags":["Titles"],"parameters":[{"schema":{"type":"string"},"in":"query","name":"include","required":false,"description":"Comma-separated: metadata, credits, certificates, companies. Narrow by default."},{"schema":{"type":"string"},"in":"query","name":"lang","required":false,"description":"A BCP 47 tag; keeps only that language’s editorial record."},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"titleId","required":true}],"responses":{"200":{"description":"A title as this deal holds it. Ask for the heavier parts with ?include=metadata,credits,certificates,companies.","content":{"application/json":{"schema":{"type":"object","description":"A title as this deal holds it. Ask for the heavier parts with ?include=metadata,credits,certificates,companies.","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":"string","description":"The Final Frame Title ID."},"name":{"type":"string"},"kind":{"type":"string"},"subtype":{"type":["string","null"]},"number":{"type":["number","null"]},"originalLanguage":{"type":["string","null"]},"productionYear":{"type":["number","null"]},"runtimeMinutes":{"type":["number","null"]},"countryOfOrigin":{"type":["string","null"]},"contentTier":{"type":["string","null"]},"identifiers":{"type":"object","properties":{"eidr":{"type":["string","null"]},"imdb":{"type":["string","null"]},"tmdb":{"type":["string","null"]},"commonSense":{"type":["string","null"]},"providerHouse":{"type":["string","null"]},"receiverHouse":{"type":["string","null"]},"wikidata":{"type":["string","null"]}},"additionalProperties":true},"fields":{"type":"object","description":"The tenant’s own fields, keyed by their schema keys. Stable keys, open bag.","properties":{},"additionalProperties":true},"metadata":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string"},"name":{"type":["string","null"]},"tagline":{"type":["string","null"]},"synopsis120":{"type":["string","null"]},"synopsis240":{"type":["string","null"]},"synopsis500":{"type":["string","null"]},"synopsis1000":{"type":["string","null"]},"genres":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}},"credits":{"type":"array","items":{"type":"object","properties":{"person":{"type":"string"},"credit":{"type":"string"},"characterName":{"type":["string","null"]},"ranking":{"type":"integer"}},"additionalProperties":true}},"certificates":{"type":"array","items":{"type":"object","properties":{"territory":{"type":"string"},"body":{"type":["string","null"]},"value":{"type":["string","null"]}},"additionalProperties":true}},"companies":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"role":{"type":"string"}},"additionalProperties":true}}},"additionalProperties":true},"example":{"id":"a3c9e1b2-4d5f-4a6b-8c7d-9e0f1a2b3c4d","reference":"T-0007","name":"The Long Harbour","kind":"feature","subtype":null,"number":null,"originalLanguage":"en","productionYear":2026,"runtimeMinutes":112,"countryOfOrigin":"GB","contentTier":null,"identifiers":{"eidr":"10.5240/1F2A-3B4C-5D6E-7F80-9A1B-C","imdb":"tt9876543","tmdb":null,"commonSense":null,"providerHouse":null,"receiverHouse":null,"wikidata":null},"fields":{},"metadata":[],"credits":[],"certificates":[],"companies":[]}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getTitle","x-permission":"project.view","x-permissions":["project.view"],"description":"Permission: project.view."},"patch":{"summary":"Change a title’s identifiers and facts","tags":["Titles"],"description":"EIDR, IMDb, TMDB, Common Sense, both house ids and the Wikidata id, with the production year, runtime, country of origin and original language. Only the fields sent change; null clears one. A Wikidata id links the title to that catalogue work and is refused when no work carries it. Identifying a title that was not identified picks its enrichment up again, when the deal asked for enrichment. The name is changed through PUT /metadata in the organisation’s default English. Needs project.configure. Idempotent on the Idempotency-Key.\n\nPermission: project.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"identifiers":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"eidr":{"type":["string","null"],"maxLength":120},"imdb":{"type":["string","null"],"maxLength":40},"tmdb":{"type":["string","null"],"maxLength":20},"commonSense":{"type":["string","null"],"maxLength":60},"providerHouse":{"type":["string","null"],"maxLength":120,"description":"The sending side’s own id."},"receiverHouse":{"type":["string","null"],"maxLength":120,"description":"The receiving side’s own id."},"wikidata":{"type":["string","null"],"maxLength":24,"description":"A Q-id, e.g. Q11424."}}},"productionYear":{"type":["integer","null"],"minimum":1870,"maximum":2200},"runtimeMinutes":{"type":["integer","null"],"minimum":0,"maximum":6000},"countryOfOrigin":{"type":["string","null"],"maxLength":12},"originalLanguage":{"type":["string","null"],"minLength":2,"maxLength":35,"description":"BCP 47."}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"titleId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"A title as this deal holds it. Ask for the heavier parts with ?include=metadata,credits,certificates,companies.","content":{"application/json":{"schema":{"type":"object","description":"A title as this deal holds it. Ask for the heavier parts with ?include=metadata,credits,certificates,companies.","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":"string","description":"The Final Frame Title ID."},"name":{"type":"string"},"kind":{"type":"string"},"subtype":{"type":["string","null"]},"number":{"type":["number","null"]},"originalLanguage":{"type":["string","null"]},"productionYear":{"type":["number","null"]},"runtimeMinutes":{"type":["number","null"]},"countryOfOrigin":{"type":["string","null"]},"contentTier":{"type":["string","null"]},"identifiers":{"type":"object","properties":{"eidr":{"type":["string","null"]},"imdb":{"type":["string","null"]},"tmdb":{"type":["string","null"]},"commonSense":{"type":["string","null"]},"providerHouse":{"type":["string","null"]},"receiverHouse":{"type":["string","null"]},"wikidata":{"type":["string","null"]}},"additionalProperties":true},"fields":{"type":"object","description":"The tenant’s own fields, keyed by their schema keys. Stable keys, open bag.","properties":{},"additionalProperties":true},"metadata":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string"},"name":{"type":["string","null"]},"tagline":{"type":["string","null"]},"synopsis120":{"type":["string","null"]},"synopsis240":{"type":["string","null"]},"synopsis500":{"type":["string","null"]},"synopsis1000":{"type":["string","null"]},"genres":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}},"credits":{"type":"array","items":{"type":"object","properties":{"person":{"type":"string"},"credit":{"type":"string"},"characterName":{"type":["string","null"]},"ranking":{"type":"integer"}},"additionalProperties":true}},"certificates":{"type":"array","items":{"type":"object","properties":{"territory":{"type":"string"},"body":{"type":["string","null"]},"value":{"type":["string","null"]}},"additionalProperties":true}},"companies":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"role":{"type":"string"}},"additionalProperties":true}}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"updateTitle","x-permission":"project.configure","x-permissions":["project.configure"],"x-idempotent":true}},"/v1/projects/{projectId}/deliverables":{"get":{"summary":"List deliverables","tags":["Deliverables"],"description":"Every required output in the deal, each saying what is on it (presence). Filter by family, kind, status, presence, title, language or territory; sort by family, status, dueDate or title. ?presence=empty&titleId= lists the placeholders still waiting for one title. The cursor is a position in the sorted list.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false},{"schema":{"type":"string","maxLength":20},"in":"query","name":"family","required":false,"description":"video, audio, subtitles, artwork, documents or metadata."},{"schema":{"type":"string","maxLength":60},"in":"query","name":"kind","required":false,"description":"A deliverable kind: picture_master, dub_audio, subtitle, key_art, editorial_metadata, credits, classification and so on."},{"schema":{"type":"string","maxLength":60},"in":"query","name":"status","required":false,"description":"A status key from the organisation’s taxonomy."},{"schema":{"type":"string","enum":["present","empty"]},"in":"query","name":"presence","required":false,"description":"present: a file or data is on the row. empty: a placeholder still waiting."},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"titleId","required":false,"description":"A title, season or episode: the row’s own record or any record under it."},{"schema":{"type":"string","maxLength":35},"in":"query","name":"language","required":false,"description":"A BCP 47 tag."},{"schema":{"type":"string","maxLength":40},"in":"query","name":"territory","required":false,"description":"A territory code."},{"schema":{"type":"string","enum":["family","status","dueDate","title"]},"in":"query","name":"sort","required":false,"description":"Default: catalogue order (season, episode, kind, territory, language)."},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"One required output for one destination under one spec: a placeholder until a file or data fills it.","properties":{"id":{"type":"string","format":"uuid"},"level":{"type":"string","enum":["title","season","episode"]},"titleId":{"type":["string","null"],"description":"The show or film the row belongs to."},"recordId":{"type":["string","null"],"description":"The most specific record the row hangs off: the title, season or episode."},"titleName":{"type":["string","null"]},"showName":{"type":["string","null"]},"seasonNumber":{"type":["number","null"]},"episodeNumber":{"type":["number","null"]},"episodeCode":{"type":["string","null"]},"family":{"type":"string","description":"video, audio, subtitles, artwork, documents, metadata."},"kind":{"type":"string"},"label":{"type":"string"},"territory":{"type":["string","null"]},"language":{"type":["string","null"]},"purpose":{"type":["string","null"]},"status":{"type":"string","description":"A key from the organisation’s status taxonomy. Metadata, credits and classification rows derive theirs from the data on the title."},"dueDate":{"type":["string","null"]},"notes":{"type":["string","null"]},"presence":{"type":"object","description":"What is on the row. A placeholder has neither file nor data.","properties":{"file":{"type":"boolean","description":"A file is filed on the row."},"filename":{"type":["string","null"]},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"receivedAt":{"type":["string","null"]},"data":{"type":"boolean","description":"For metadata, credits and classification rows: the data has been written, in part or in full."},"withheld":{"type":"array","items":{"type":"object","properties":{"by":{"type":["string","null"],"description":"The party that supplied it."},"filename":{"type":["string","null"]},"at":{"type":["string","null"]},"state":{"type":"string","enum":["withheld","releasing"]}},"additionalProperties":true},"description":"Files the other party of a shared deal supplied on this row and keeps until a buyback. Not available here: no download, never delivered. releasing: bought back, being filed."}},"additionalProperties":true},"partner":{"type":"boolean","description":"True for the sending party’s rows on an inbound deal: readable, never writable here."},"qc":{"type":"object","description":"The latest QC verdict and its findings, when QC has run.","properties":{},"additionalProperties":true}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true},"example":{"data":[{"id":"c5e7a9b1-2d3f-4e5a-8b6c-7d8e9f0a1b2c","level":"title","titleId":"a3c9e1b2-4d5f-4a6b-8c7d-9e0f1a2b3c4d","recordId":"a3c9e1b2-4d5f-4a6b-8c7d-9e0f1a2b3c4d","titleName":"The Long Harbour","showName":null,"seasonNumber":null,"episodeNumber":null,"episodeCode":null,"family":"subtitles","kind":"subtitle","label":"Subtitles","territory":null,"language":"da-DK","purpose":null,"status":"received","dueDate":"2027-03-01","notes":null,"presence":{"file":true,"filename":"long-harbour.da-DK.srt","byteSize":84211,"contentType":"application/x-subrip","receivedAt":"2027-01-15T10:30:00.000Z","data":false,"withheld":[]},"partner":false}],"next_cursor":null}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listDeliverables","x-permission":"project.view","x-permissions":["project.view"]},"post":{"summary":"Add a placeholder","tags":["Deliverables"],"description":"One required output, by hand: an amendment, or something the memo did not spell out. titleId names the title, season or episode it hangs off; absent, the deal’s own title. An artwork placeholder is given its IMG reference. Set notes, status and due date afterwards with PATCH. Needs project.configure. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: project.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["kind","label"],"additionalProperties":false,"properties":{"kind":{"type":"string","enum":["picture_master","picture_derivative","sdr_trim","mezzanine","textless_elements","audio_immersive","audio_surround","audio_stereo","audio_me","audio_stems","dub_audio","subtitle","caption_sdh","forced_narrative","audio_description","dub_card","localised_graphic","dubbing_script","metadata","title_metadata","editorial_metadata","credits","key_art","still","trailer","qc_report","pse_certificate","checksums","classification","classification_certificate","chain_of_title","certificate_of_origin","eo_insurance","music_cue_sheet","licences","releases","as_broadcast_script","restrictions_statement","other"],"description":"picture_master, dub_audio, subtitle, key_art, editorial_metadata and the rest."},"label":{"type":"string","minLength":1,"maxLength":300},"titleId":{"type":"string","format":"uuid","description":"A title, season or episode in this deal."},"territory":{"type":["string","null"],"maxLength":40},"language":{"type":["string","null"],"maxLength":35,"description":"BCP 47."},"purpose":{"type":["string","null"],"maxLength":40},"dueDate":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"ISO date, or null to clear."}}},"example":{"titleId":"a3c9e1b2-4d5f-4a6b-8c7d-9e0f1a2b3c4d","kind":"subtitle","label":"Subtitles","language":"da-DK"}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"One deliverable in full.","content":{"application/json":{"schema":{"type":"object","description":"One deliverable in full.","properties":{"id":{"type":"string","format":"uuid"},"level":{"type":"string","enum":["title","season","episode"]},"titleId":{"type":["string","null"],"description":"The show or film the row belongs to."},"recordId":{"type":["string","null"],"description":"The most specific record the row hangs off: the title, season or episode."},"titleName":{"type":["string","null"]},"showName":{"type":["string","null"]},"seasonNumber":{"type":["number","null"]},"episodeNumber":{"type":["number","null"]},"episodeCode":{"type":["string","null"]},"family":{"type":"string","description":"video, audio, subtitles, artwork, documents, metadata."},"kind":{"type":"string"},"label":{"type":"string"},"territory":{"type":["string","null"]},"language":{"type":["string","null"]},"purpose":{"type":["string","null"]},"status":{"type":"string","description":"A key from the organisation’s status taxonomy. Metadata, credits and classification rows derive theirs from the data on the title."},"dueDate":{"type":["string","null"]},"notes":{"type":["string","null"]},"presence":{"type":"object","description":"What is on the row. A placeholder has neither file nor data.","properties":{"file":{"type":"boolean","description":"A file is filed on the row."},"filename":{"type":["string","null"]},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"receivedAt":{"type":["string","null"]},"data":{"type":"boolean","description":"For metadata, credits and classification rows: the data has been written, in part or in full."},"withheld":{"type":"array","items":{"type":"object","properties":{"by":{"type":["string","null"],"description":"The party that supplied it."},"filename":{"type":["string","null"]},"at":{"type":["string","null"]},"state":{"type":"string","enum":["withheld","releasing"]}},"additionalProperties":true},"description":"Files the other party of a shared deal supplied on this row and keeps until a buyback. Not available here: no download, never delivered. releasing: bought back, being filed."}},"additionalProperties":true},"partner":{"type":"boolean","description":"True for the sending party’s rows on an inbound deal: readable, never writable here."},"qc":{"type":"object","description":"The latest QC verdict and its findings, when QC has run.","properties":{},"additionalProperties":true},"file":{"type":"object","description":"The delivered file’s own facts, or null until one is filed. Never a storage path.","properties":{"filename":{"type":["string","null"]},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"receivedAt":{"type":["string","null"]},"durationSeconds":{"type":["number","null"]},"width":{"type":["number","null"]},"height":{"type":["number","null"]},"codec":{"type":["string","null"]},"frameRate":{"type":["string","null"]},"pixelFormat":{"type":["string","null"]},"loudness":{},"proxyStatus":{"type":["string","null"]},"generatedBy":{"type":["string","null"]},"generatedFrom":{"type":["string","null"]}},"additionalProperties":true},"transfer":{"type":"object","description":"An upload in flight on the row, or null.","properties":{"status":{"type":["string","null"]},"startedAt":{"type":["string","null"]},"bytesSent":{"type":["number","null"]},"bytesTotal":{"type":["number","null"]},"by":{"type":["string","null"]}},"additionalProperties":true},"requirement":{"type":"object","description":"What the destination asked for, as the spec records it: plain values, keyed as the spec keys them.","properties":{},"additionalProperties":true}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createDeliverable","x-permission":"project.configure","x-permissions":["project.configure"],"x-idempotent":true}},"/v1/projects/{projectId}/deliverables/{deliverableId}":{"get":{"summary":"One deliverable","tags":["Deliverables"],"description":"The row in full: what is on it, the delivered file’s own facts (name, size, duration, codec), any upload in flight, and the requirement as the spec records it. Never a storage path or a signed URL.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"deliverableId","required":true}],"responses":{"200":{"description":"One deliverable in full.","content":{"application/json":{"schema":{"type":"object","description":"One deliverable in full.","properties":{"id":{"type":"string","format":"uuid"},"level":{"type":"string","enum":["title","season","episode"]},"titleId":{"type":["string","null"],"description":"The show or film the row belongs to."},"recordId":{"type":["string","null"],"description":"The most specific record the row hangs off: the title, season or episode."},"titleName":{"type":["string","null"]},"showName":{"type":["string","null"]},"seasonNumber":{"type":["number","null"]},"episodeNumber":{"type":["number","null"]},"episodeCode":{"type":["string","null"]},"family":{"type":"string","description":"video, audio, subtitles, artwork, documents, metadata."},"kind":{"type":"string"},"label":{"type":"string"},"territory":{"type":["string","null"]},"language":{"type":["string","null"]},"purpose":{"type":["string","null"]},"status":{"type":"string","description":"A key from the organisation’s status taxonomy. Metadata, credits and classification rows derive theirs from the data on the title."},"dueDate":{"type":["string","null"]},"notes":{"type":["string","null"]},"presence":{"type":"object","description":"What is on the row. A placeholder has neither file nor data.","properties":{"file":{"type":"boolean","description":"A file is filed on the row."},"filename":{"type":["string","null"]},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"receivedAt":{"type":["string","null"]},"data":{"type":"boolean","description":"For metadata, credits and classification rows: the data has been written, in part or in full."},"withheld":{"type":"array","items":{"type":"object","properties":{"by":{"type":["string","null"],"description":"The party that supplied it."},"filename":{"type":["string","null"]},"at":{"type":["string","null"]},"state":{"type":"string","enum":["withheld","releasing"]}},"additionalProperties":true},"description":"Files the other party of a shared deal supplied on this row and keeps until a buyback. Not available here: no download, never delivered. releasing: bought back, being filed."}},"additionalProperties":true},"partner":{"type":"boolean","description":"True for the sending party’s rows on an inbound deal: readable, never writable here."},"qc":{"type":"object","description":"The latest QC verdict and its findings, when QC has run.","properties":{},"additionalProperties":true},"file":{"type":"object","description":"The delivered file’s own facts, or null until one is filed. Never a storage path.","properties":{"filename":{"type":["string","null"]},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"receivedAt":{"type":["string","null"]},"durationSeconds":{"type":["number","null"]},"width":{"type":["number","null"]},"height":{"type":["number","null"]},"codec":{"type":["string","null"]},"frameRate":{"type":["string","null"]},"pixelFormat":{"type":["string","null"]},"loudness":{},"proxyStatus":{"type":["string","null"]},"generatedBy":{"type":["string","null"]},"generatedFrom":{"type":["string","null"]}},"additionalProperties":true},"transfer":{"type":"object","description":"An upload in flight on the row, or null.","properties":{"status":{"type":["string","null"]},"startedAt":{"type":["string","null"]},"bytesSent":{"type":["number","null"]},"bytesTotal":{"type":["number","null"]},"by":{"type":["string","null"]}},"additionalProperties":true},"requirement":{"type":"object","description":"What the destination asked for, as the spec records it: plain values, keyed as the spec keys them.","properties":{},"additionalProperties":true}},"additionalProperties":true},"example":{"id":"c5e7a9b1-2d3f-4e5a-8b6c-7d8e9f0a1b2c","level":"title","titleId":"a3c9e1b2-4d5f-4a6b-8c7d-9e0f1a2b3c4d","recordId":"a3c9e1b2-4d5f-4a6b-8c7d-9e0f1a2b3c4d","titleName":"The Long Harbour","showName":null,"seasonNumber":null,"episodeNumber":null,"episodeCode":null,"family":"subtitles","kind":"subtitle","label":"Subtitles","territory":null,"language":"da-DK","purpose":null,"status":"received","dueDate":"2027-03-01","notes":null,"presence":{"file":true,"filename":"long-harbour.da-DK.srt","byteSize":84211,"contentType":"application/x-subrip","receivedAt":"2027-01-15T10:30:00.000Z","data":false,"withheld":[]},"partner":false}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getDeliverable","x-permission":"project.view","x-permissions":["project.view"]},"patch":{"summary":"Change a deliverable’s status, notes or due date","tags":["Deliverables"],"description":"status is a key from the organisation’s deliverable status taxonomy (GET /v1/config lists it under taxonomies.deliverableStatuses); an unknown key is refused with the list. Needs project.configure. Safe to retry: the same body gives the same row. Written to the action log; a status change raises deliverable.status_changed. A move past QC (passed, in review, review passed, ready, or the organisation’s final status) on a row whose latest QC verdict is Fail is refused with 409 qc_failed until the failure is waived (POST …/qc-waiver), when the organisation’s “A failed QC blocks delivery” setting is on, as it is by default.\n\nPermission: project.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"status":{"type":"string","minLength":1,"maxLength":60},"notes":{"type":["string","null"],"maxLength":2000},"dueDate":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"ISO date, or null to clear."}}},"example":{"status":"approved","notes":"Checked against the spec."}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"deliverableId","required":true}],"responses":{"200":{"description":"One deliverable in full.","content":{"application/json":{"schema":{"type":"object","description":"One deliverable in full.","properties":{"id":{"type":"string","format":"uuid"},"level":{"type":"string","enum":["title","season","episode"]},"titleId":{"type":["string","null"],"description":"The show or film the row belongs to."},"recordId":{"type":["string","null"],"description":"The most specific record the row hangs off: the title, season or episode."},"titleName":{"type":["string","null"]},"showName":{"type":["string","null"]},"seasonNumber":{"type":["number","null"]},"episodeNumber":{"type":["number","null"]},"episodeCode":{"type":["string","null"]},"family":{"type":"string","description":"video, audio, subtitles, artwork, documents, metadata."},"kind":{"type":"string"},"label":{"type":"string"},"territory":{"type":["string","null"]},"language":{"type":["string","null"]},"purpose":{"type":["string","null"]},"status":{"type":"string","description":"A key from the organisation’s status taxonomy. Metadata, credits and classification rows derive theirs from the data on the title."},"dueDate":{"type":["string","null"]},"notes":{"type":["string","null"]},"presence":{"type":"object","description":"What is on the row. A placeholder has neither file nor data.","properties":{"file":{"type":"boolean","description":"A file is filed on the row."},"filename":{"type":["string","null"]},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"receivedAt":{"type":["string","null"]},"data":{"type":"boolean","description":"For metadata, credits and classification rows: the data has been written, in part or in full."},"withheld":{"type":"array","items":{"type":"object","properties":{"by":{"type":["string","null"],"description":"The party that supplied it."},"filename":{"type":["string","null"]},"at":{"type":["string","null"]},"state":{"type":"string","enum":["withheld","releasing"]}},"additionalProperties":true},"description":"Files the other party of a shared deal supplied on this row and keeps until a buyback. Not available here: no download, never delivered. releasing: bought back, being filed."}},"additionalProperties":true},"partner":{"type":"boolean","description":"True for the sending party’s rows on an inbound deal: readable, never writable here."},"qc":{"type":"object","description":"The latest QC verdict and its findings, when QC has run.","properties":{},"additionalProperties":true},"file":{"type":"object","description":"The delivered file’s own facts, or null until one is filed. Never a storage path.","properties":{"filename":{"type":["string","null"]},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"receivedAt":{"type":["string","null"]},"durationSeconds":{"type":["number","null"]},"width":{"type":["number","null"]},"height":{"type":["number","null"]},"codec":{"type":["string","null"]},"frameRate":{"type":["string","null"]},"pixelFormat":{"type":["string","null"]},"loudness":{},"proxyStatus":{"type":["string","null"]},"generatedBy":{"type":["string","null"]},"generatedFrom":{"type":["string","null"]}},"additionalProperties":true},"transfer":{"type":"object","description":"An upload in flight on the row, or null.","properties":{"status":{"type":["string","null"]},"startedAt":{"type":["string","null"]},"bytesSent":{"type":["number","null"]},"bytesTotal":{"type":["number","null"]},"by":{"type":["string","null"]}},"additionalProperties":true},"requirement":{"type":"object","description":"What the destination asked for, as the spec records it: plain values, keyed as the spec keys them.","properties":{},"additionalProperties":true}},"additionalProperties":true},"example":{"id":"c5e7a9b1-2d3f-4e5a-8b6c-7d8e9f0a1b2c","level":"title","titleId":"a3c9e1b2-4d5f-4a6b-8c7d-9e0f1a2b3c4d","recordId":"a3c9e1b2-4d5f-4a6b-8c7d-9e0f1a2b3c4d","titleName":"The Long Harbour","showName":null,"seasonNumber":null,"episodeNumber":null,"episodeCode":null,"family":"subtitles","kind":"subtitle","label":"Subtitles","territory":null,"language":"da-DK","purpose":null,"status":"approved","dueDate":"2027-03-01","notes":"Checked against the spec.","presence":{"file":true,"filename":"long-harbour.da-DK.srt","byteSize":84211,"contentType":"application/x-subrip","receivedAt":"2027-01-15T10:30:00.000Z","data":false,"withheld":[]},"partner":false}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"The deliverable’s latest QC verdict is Fail and nobody has waived it. Fix it and run QC again, or POST a reason to the waive endpoint named here, then retry.","content":{"application/json":{"schema":{"type":"object","description":"The deliverable’s latest QC verdict is Fail and nobody has waived it. Fix it and run QC again, or POST a reason to the waive endpoint named here, then retry.","properties":{"error":{"type":"string","enum":["qc_failed"]},"message":{"type":"string"},"detail":{"type":"object","properties":{"deliverableIds":{"type":"array","items":{"type":"string"}},"blocked":{"type":"integer","description":"How many rows failed QC; the lists name the first hundred."},"waive":{"type":"array","items":{"type":"object","properties":{"deliverableId":{"type":"string"},"method":{"type":"string"},"path":{"type":"string"}},"additionalProperties":true}}},"additionalProperties":true},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"updateDeliverable","x-permission":"project.configure","x-permissions":["project.configure"],"x-idempotent":true}},"/v1/projects/{projectId}/deliverables/{deliverableId}/qc-waiver":{"post":{"summary":"Waive a deliverable’s failed QC","tags":["Deliverables"],"description":"Records a decision to let a deliverable whose latest QC verdict is Fail move past QC anyway: who, why (reason, required) and which verdict, on the row (qc.waiver) and the action log, and raises deliverable.qc_waived. The next QC run replaces the verdict and the waiver with it. Needs project.configure and qc.execute. Safe to retry: a row already waived for this verdict answers with that waiver (replayed: true). 409 conflict when the row has no failed verdict, or QC ran again meanwhile.\n\nPermission: project.configure and qc.execute.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["reason"],"properties":{"reason":{"type":"string","minLength":3,"maxLength":2000,"description":"Why the failure is being waived."}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"deliverableId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"waiver":{"type":"object","properties":{"by":{"type":["string","null"]},"byName":{"type":["string","null"]},"reason":{"type":"string"},"at":{"type":"string"},"verdictRanAt":{"type":["string","null"]}},"additionalProperties":true},"replayed":{"type":"boolean"},"deliverable":{"type":"object","description":"One deliverable in full.","properties":{"id":{"type":"string","format":"uuid"},"level":{"type":"string","enum":["title","season","episode"]},"titleId":{"type":["string","null"],"description":"The show or film the row belongs to."},"recordId":{"type":["string","null"],"description":"The most specific record the row hangs off: the title, season or episode."},"titleName":{"type":["string","null"]},"showName":{"type":["string","null"]},"seasonNumber":{"type":["number","null"]},"episodeNumber":{"type":["number","null"]},"episodeCode":{"type":["string","null"]},"family":{"type":"string","description":"video, audio, subtitles, artwork, documents, metadata."},"kind":{"type":"string"},"label":{"type":"string"},"territory":{"type":["string","null"]},"language":{"type":["string","null"]},"purpose":{"type":["string","null"]},"status":{"type":"string","description":"A key from the organisation’s status taxonomy. Metadata, credits and classification rows derive theirs from the data on the title."},"dueDate":{"type":["string","null"]},"notes":{"type":["string","null"]},"presence":{"type":"object","description":"What is on the row. A placeholder has neither file nor data.","properties":{"file":{"type":"boolean","description":"A file is filed on the row."},"filename":{"type":["string","null"]},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"receivedAt":{"type":["string","null"]},"data":{"type":"boolean","description":"For metadata, credits and classification rows: the data has been written, in part or in full."},"withheld":{"type":"array","items":{"type":"object","properties":{"by":{"type":["string","null"],"description":"The party that supplied it."},"filename":{"type":["string","null"]},"at":{"type":["string","null"]},"state":{"type":"string","enum":["withheld","releasing"]}},"additionalProperties":true},"description":"Files the other party of a shared deal supplied on this row and keeps until a buyback. Not available here: no download, never delivered. releasing: bought back, being filed."}},"additionalProperties":true},"partner":{"type":"boolean","description":"True for the sending party’s rows on an inbound deal: readable, never writable here."},"qc":{"type":"object","description":"The latest QC verdict and its findings, when QC has run.","properties":{},"additionalProperties":true},"file":{"type":"object","description":"The delivered file’s own facts, or null until one is filed. Never a storage path.","properties":{"filename":{"type":["string","null"]},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"receivedAt":{"type":["string","null"]},"durationSeconds":{"type":["number","null"]},"width":{"type":["number","null"]},"height":{"type":["number","null"]},"codec":{"type":["string","null"]},"frameRate":{"type":["string","null"]},"pixelFormat":{"type":["string","null"]},"loudness":{},"proxyStatus":{"type":["string","null"]},"generatedBy":{"type":["string","null"]},"generatedFrom":{"type":["string","null"]}},"additionalProperties":true},"transfer":{"type":"object","description":"An upload in flight on the row, or null.","properties":{"status":{"type":["string","null"]},"startedAt":{"type":["string","null"]},"bytesSent":{"type":["number","null"]},"bytesTotal":{"type":["number","null"]},"by":{"type":["string","null"]}},"additionalProperties":true},"requirement":{"type":"object","description":"What the destination asked for, as the spec records it: plain values, keyed as the spec keys them.","properties":{},"additionalProperties":true}},"additionalProperties":true}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"waiveDeliverableQc","x-permission":"project.configure qc.execute","x-permissions":["project.configure","qc.execute"],"x-idempotent":true}},"/v1/projects/{projectId}/deliverables/{deliverableId}/uploads":{"post":{"summary":"Start putting a file in a placeholder","tags":["Deliverables"],"description":"Opens the same multipart session the product and the Mac app use and returns signed PUT URLs for the first fifty parts (POST /uploads/{sessionId}/parts for the rest). The bytes go straight to the organisation’s storage and never through the API. When every part is up, POST /uploads/{sessionId}/complete with each part’s ETag. Needs asset.upload. Idempotent: a retry with the same Idempotency-Key returns the same session instead of opening a second. A picture or sound file that looks wrong for the row (already delivered elsewhere, or far from its siblings’ size) is refused with delivery_doubt until confirmed is sent.\n\nPermission: asset.upload.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["filename","contentType","byteSize"],"additionalProperties":false,"properties":{"filename":{"type":"string","minLength":1,"maxLength":200},"contentType":{"type":"string","pattern":"^(?!.*(?:html|javascript|ecmascript|svg))(?!(?!application/vnd\\.openxmlformats-officedocument\\.(?:wordprocessingml|spreadsheetml|presentationml)\\.[a-z.]+$).*xml)(image/|video/|audio/|text/|application/)[a-zA-Z0-9.+-]+$","maxLength":100,"description":"image/*, video/*, audio/*, text/* or application/*; nothing scriptable."},"byteSize":{"type":"integer","minimum":1,"maximum":5497558138880,"description":"The exact size of the file. The assembled parts must add up to it."},"confirmed":{"type":"boolean","description":"Send true after a delivery_doubt to go ahead anyway."}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"deliverableId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"An open upload session. PUT the bytes of part n (from offset (n-1)*partSize, partSize long) to its URL, keep each response’s ETag header, then complete.","content":{"application/json":{"schema":{"type":"object","description":"An open upload session. PUT the bytes of part n (from offset (n-1)*partSize, partSize long) to its URL, keep each response’s ETag header, then complete.","properties":{"sessionId":{"type":"string","description":"Carry this to /parts and /complete."},"partSize":{"type":"integer","description":"Bytes per part. Every part is this size except the last."},"partCount":{"type":"integer"},"parts":{"type":"array","items":{"type":"object","properties":{"partNumber":{"type":"integer"},"url":{"type":"string","description":"A signed PUT URL for this part, good for an hour. A credential: never log it."}},"additionalProperties":true}},"expiresInSeconds":{"type":"integer"}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"startDeliverableUpload","x-permission":"asset.upload","x-permissions":["asset.upload"],"x-idempotent":true}},"/v1/projects/{projectId}/deliverables/{deliverableId}/uploads/{sessionId}/parts":{"post":{"summary":"More signed part URLs for an open upload","tags":["Deliverables"],"description":"Signed PUT URLs for parts from..to of the session, up to two hundred at a time, each good for an hour. Mint them near the moment they are used. Nothing is written, so no Idempotency-Key is needed.\n\nPermission: asset.upload.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["from","to"],"additionalProperties":false,"properties":{"from":{"type":"integer","minimum":1,"maximum":10000},"to":{"type":"integer","minimum":1,"maximum":10000}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"deliverableId","required":true},{"schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"sessionId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"parts":{"type":"array","items":{"type":"object","properties":{"partNumber":{"type":"integer"},"url":{"type":"string","description":"A signed PUT URL for this part, good for an hour. A credential: never log it."}},"additionalProperties":true}},"expiresInSeconds":{"type":"integer"}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createDeliverableUploadPartUrls","x-permission":"asset.upload","x-permissions":["asset.upload"],"x-idempotent":true}},"/v1/projects/{projectId}/deliverables/{deliverableId}/uploads/{sessionId}/complete":{"post":{"summary":"Finish an upload and file it on the placeholder","tags":["Deliverables"],"description":"Assembles the parts in storage, checks the size against what was declared, files the object on the row (status received), queues the streaming proxy for picture and sound and the automatic QC checks (their runs: GET /v1/processes?subject={deliverableId}, named in the Link header as rel=monitor), writes the action log and raises asset.received and deliverable.status_changed. List every part with the ETag its PUT returned: a missing part is refused, never joined. Needs asset.upload. Idempotent on the Idempotency-Key.\n\nPermission: asset.upload.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["parts"],"additionalProperties":false,"properties":{"parts":{"type":"array","minItems":1,"maxItems":10000,"items":{"type":"object","required":["partNumber","etag"],"additionalProperties":false,"properties":{"partNumber":{"type":"integer","minimum":1,"maximum":10000},"etag":{"type":"string","minLength":1,"maxLength":200}}}}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"deliverableId","required":true},{"schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"One deliverable in full.","content":{"application/json":{"schema":{"type":"object","description":"One deliverable in full.","properties":{"id":{"type":"string","format":"uuid"},"level":{"type":"string","enum":["title","season","episode"]},"titleId":{"type":["string","null"],"description":"The show or film the row belongs to."},"recordId":{"type":["string","null"],"description":"The most specific record the row hangs off: the title, season or episode."},"titleName":{"type":["string","null"]},"showName":{"type":["string","null"]},"seasonNumber":{"type":["number","null"]},"episodeNumber":{"type":["number","null"]},"episodeCode":{"type":["string","null"]},"family":{"type":"string","description":"video, audio, subtitles, artwork, documents, metadata."},"kind":{"type":"string"},"label":{"type":"string"},"territory":{"type":["string","null"]},"language":{"type":["string","null"]},"purpose":{"type":["string","null"]},"status":{"type":"string","description":"A key from the organisation’s status taxonomy. Metadata, credits and classification rows derive theirs from the data on the title."},"dueDate":{"type":["string","null"]},"notes":{"type":["string","null"]},"presence":{"type":"object","description":"What is on the row. A placeholder has neither file nor data.","properties":{"file":{"type":"boolean","description":"A file is filed on the row."},"filename":{"type":["string","null"]},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"receivedAt":{"type":["string","null"]},"data":{"type":"boolean","description":"For metadata, credits and classification rows: the data has been written, in part or in full."},"withheld":{"type":"array","items":{"type":"object","properties":{"by":{"type":["string","null"],"description":"The party that supplied it."},"filename":{"type":["string","null"]},"at":{"type":["string","null"]},"state":{"type":"string","enum":["withheld","releasing"]}},"additionalProperties":true},"description":"Files the other party of a shared deal supplied on this row and keeps until a buyback. Not available here: no download, never delivered. releasing: bought back, being filed."}},"additionalProperties":true},"partner":{"type":"boolean","description":"True for the sending party’s rows on an inbound deal: readable, never writable here."},"qc":{"type":"object","description":"The latest QC verdict and its findings, when QC has run.","properties":{},"additionalProperties":true},"file":{"type":"object","description":"The delivered file’s own facts, or null until one is filed. Never a storage path.","properties":{"filename":{"type":["string","null"]},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"receivedAt":{"type":["string","null"]},"durationSeconds":{"type":["number","null"]},"width":{"type":["number","null"]},"height":{"type":["number","null"]},"codec":{"type":["string","null"]},"frameRate":{"type":["string","null"]},"pixelFormat":{"type":["string","null"]},"loudness":{},"proxyStatus":{"type":["string","null"]},"generatedBy":{"type":["string","null"]},"generatedFrom":{"type":["string","null"]}},"additionalProperties":true},"transfer":{"type":"object","description":"An upload in flight on the row, or null.","properties":{"status":{"type":["string","null"]},"startedAt":{"type":["string","null"]},"bytesSent":{"type":["number","null"]},"bytesTotal":{"type":["number","null"]},"by":{"type":["string","null"]}},"additionalProperties":true},"requirement":{"type":"object","description":"What the destination asked for, as the spec records it: plain values, keyed as the spec keys them.","properties":{},"additionalProperties":true}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"completeDeliverableUpload","x-permission":"asset.upload","x-permissions":["asset.upload"],"x-idempotent":true}},"/v1/projects/{projectId}/deliverables/{deliverableId}/download":{"get":{"summary":"A download link for the delivered file","tags":["Deliverables"],"description":"A signed URL for the file on this row, valid for fifteen minutes. The bytes never pass through the API. Needs asset.download. 404 when nothing has been delivered on the row; for a data row, use /data.\n\nPermission: asset.download.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"deliverableId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"filename":{"type":["string","null"]},"expiresInSeconds":{"type":"integer"}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getDeliverableDownload","x-permission":"asset.download","x-permissions":["asset.download"]}},"/v1/projects/{projectId}/deliverables/{deliverableId}/data":{"get":{"summary":"The data a data row holds","tags":["Deliverables"],"description":"For metadata, credits and classification rows: the structured data the row summarises, read from the title it hangs off. A metadata row gives its language’s editorial record; title_metadata the title’s own facts and identifiers; credits the cast and crew; classification the certificates for its territory. For a row that takes a file, ask for /download.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"deliverableId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string"},"titleId":{"type":["string","null"],"description":"The record the data is on."},"language":{"type":["string","null"]},"territory":{"type":["string","null"]},"presence":{"type":"object","description":"What is on the row. A placeholder has neither file nor data.","properties":{"file":{"type":"boolean","description":"A file is filed on the row."},"filename":{"type":["string","null"]},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"receivedAt":{"type":["string","null"]},"data":{"type":"boolean","description":"For metadata, credits and classification rows: the data has been written, in part or in full."},"withheld":{"type":"array","items":{"type":"object","properties":{"by":{"type":["string","null"],"description":"The party that supplied it."},"filename":{"type":["string","null"]},"at":{"type":["string","null"]},"state":{"type":"string","enum":["withheld","releasing"]}},"additionalProperties":true},"description":"Files the other party of a shared deal supplied on this row and keeps until a buyback. Not available here: no download, never delivered. releasing: bought back, being filed."}},"additionalProperties":true},"data":{"type":"object","description":"metadata: [...]; or title: {...}; or credits: [...]; or certificates: [...], with kind saying which.","properties":{},"additionalProperties":true}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getDeliverableData","x-permission":"project.view","x-permissions":["project.view"]}},"/v1/projects/{projectId}/titles/{titleId}/metadata":{"get":{"summary":"A title’s editorial metadata","tags":["Titles"],"description":"Every language’s record, or one with ?lang=. This is the data the metadata placeholders summarise.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","maxLength":35},"in":"query","name":"lang","required":false,"description":"A BCP 47 tag."},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"titleId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"titleId":{"type":"string","format":"uuid"},"data":{"type":"array","items":{"type":"object","description":"One language’s editorial record for a title: what the metadata placeholders summarise.","properties":{"titleId":{"type":"string","format":"uuid"},"language":{"type":"string"},"name":{"type":["string","null"]},"tagline":{"type":["string","null"]},"synopsis120":{"type":["string","null"]},"synopsis240":{"type":["string","null"]},"synopsis500":{"type":["string","null"]},"synopsis1000":{"type":["string","null"]},"genres":{"type":"array","items":{"type":"string"}},"additionalGenres":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getTitleMetadata","x-permission":"project.view","x-permissions":["project.view"]},"put":{"summary":"Write a title’s editorial metadata for one language","tags":["Titles"],"description":"Creates or replaces the record for the language in the body: name, tagline, the four synopsis lengths, up to two genres and keywords. A field left out is cleared, except synopsis1000, which is kept unless sent. This is what fills a metadata placeholder: its status moves to partial, then in_review once all five prose fields are written. Needs project.configure. Safe to retry.\n\nPermission: project.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["language"],"additionalProperties":false,"properties":{"language":{"type":"string","minLength":2,"maxLength":35,"description":"A BCP 47 tag, e.g. en-GB, fr-FR."},"name":{"type":["string","null"],"maxLength":300},"tagline":{"type":["string","null"],"maxLength":500},"synopsis120":{"type":["string","null"],"maxLength":1000},"synopsis240":{"type":["string","null"],"maxLength":2000},"synopsis500":{"type":["string","null"],"maxLength":4000},"synopsis1000":{"type":["string","null"],"maxLength":8000},"genres":{"type":"array","maxItems":2,"items":{"type":"string","maxLength":40},"description":"The primary genre first, then at most one more."},"additionalGenres":{"type":"array","maxItems":40,"items":{"type":"string","maxLength":80}},"keywords":{"type":"array","maxItems":40,"items":{"type":"string","maxLength":60}}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"titleId","required":true}],"responses":{"200":{"description":"One language’s editorial record for a title: what the metadata placeholders summarise.","content":{"application/json":{"schema":{"type":"object","description":"One language’s editorial record for a title: what the metadata placeholders summarise.","properties":{"titleId":{"type":"string","format":"uuid"},"language":{"type":"string"},"name":{"type":["string","null"]},"tagline":{"type":["string","null"]},"synopsis120":{"type":["string","null"]},"synopsis240":{"type":["string","null"]},"synopsis500":{"type":["string","null"]},"synopsis1000":{"type":["string","null"]},"genres":{"type":"array","items":{"type":"string"}},"additionalGenres":{"type":"array","items":{"type":"string"}},"keywords":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"putTitleMetadata","x-permission":"project.configure","x-permissions":["project.configure"],"x-idempotent":true}},"/v1/projects/{projectId}/work-orders":{"get":{"summary":"List work orders","tags":["Work orders"],"parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"Work raised where demand exceeds supply.","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"kind":{"type":"string"},"responsibleParty":{"type":"string"},"executor":{"type":"string"},"status":{"type":"string"},"assignee":{"type":["string","null"]},"vendorName":{"type":["string","null"]},"estimatedCost":{"type":["number","null"]},"estimatedCurrency":{"type":["string","null"]},"estimatedDays":{"type":["number","null"]},"dueDate":{"type":["string","null"]},"finalCost":{"type":["number","null"]},"finalCurrency":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listWorkOrders","x-permission":"workorder.view","x-permissions":["workorder.view"],"description":"Permission: workorder.view."},"post":{"summary":"Raise a work order","tags":["Work orders"],"description":"Idempotent: send an Idempotency-Key, and a retry returns the first answer instead of a second order.\n\nPermission: workorder.create.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","kind"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"kind":{"type":"string","minLength":1,"maxLength":60,"description":"A work kind from the Org’s taxonomy, e.g. dubbing, subtitling, transcode."},"description":{"type":["string","null"],"maxLength":4000},"executor":{"type":"string","enum":["internal","vendor","automation"],"default":"internal"},"status":{"type":"string","maxLength":40,"default":"open"},"dueDate":{"type":["string","null"],"maxLength":10},"estimatedCost":{"type":["number","null"]},"estimatedCurrency":{"type":["string","null"],"minLength":3,"maxLength":3},"estimatedDays":{"type":["integer","null"],"minimum":0}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"Work raised where demand exceeds supply.","content":{"application/json":{"schema":{"type":"object","description":"Work raised where demand exceeds supply.","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"kind":{"type":"string"},"responsibleParty":{"type":"string"},"executor":{"type":"string"},"status":{"type":"string"},"assignee":{"type":["string","null"]},"vendorName":{"type":["string","null"]},"estimatedCost":{"type":["number","null"]},"estimatedCurrency":{"type":["string","null"]},"estimatedDays":{"type":["number","null"]},"dueDate":{"type":["string","null"]},"finalCost":{"type":["number","null"]},"finalCurrency":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createWorkOrder","x-permission":"workorder.create","x-permissions":["workorder.create"],"x-idempotent":true}},"/v1/projects/{projectId}/closeout":{"get":{"summary":"The deal’s close-out","tags":["Projects"],"description":"What was delivered, by whom, when, and what it cost: the record both parties sign off on, and the deal summarised. Computed live from the deal, never stored, so it always agrees with it. `state` says how far to trust the figures: `final` once the deal is closed; `reopened` when a closed deal was opened again, so the figures may still change until it closes again; `in_progress` for a deal never closed. `history` lists every close and reopen. Webhooks `deal.closed` and `deal.reopened` carry the same `summary`.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true}],"responses":{"200":{"description":"The close-out: the deal summarised, then the detail the Ending tab draws.","content":{"application/json":{"schema":{"type":"object","description":"The close-out: the deal summarised, then the detail the Ending tab draws.","properties":{"state":{"type":"string","enum":["in_progress","final","reopened"],"description":"final: closed. reopened: closed once, open again, figures provisional. in_progress: never closed."},"asOf":{"type":"string","format":"date-time","description":"When these figures were read."},"history":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"event":{"type":"string","enum":["closed","reopened"]},"by":{"type":["string","null"],"description":"Who did it."}},"additionalProperties":true}},"summary":{"type":"object","description":"The headline figures.","properties":{"titles":{"type":"integer"},"territories":{"type":"integer"},"languages":{"type":"integer","description":"Distinct languages across this Org’s deliverables."},"deliverables":{"type":"object","properties":{"total":{"type":"integer"},"delivered":{"type":"integer"},"outstanding":{"type":"integer"},"percent":{"type":["number","null"],"description":"Delivered as a share of the total, to one decimal place."},"byFamily":{"type":"array","items":{"type":"object","properties":{"family":{"type":"string"},"total":{"type":"integer"},"delivered":{"type":"integer"}},"additionalProperties":true}}},"additionalProperties":true},"partner":{"type":"object","description":"The other party’s side, counted. Null when the deal was never shared.","properties":{"deliverables":{"type":"integer"},"delivered":{"type":"integer"},"uploads":{"type":"integer"},"people":{"type":"integer"}},"additionalProperties":true},"uploads":{"type":"integer"},"people":{"type":"integer","description":"People on this Org who worked on the deal."},"workOrders":{"type":"object","properties":{"total":{"type":"integer"},"finished":{"type":"integer","description":"Delivered or signed off."},"ours":{"type":"integer"},"theirs":{"type":"integer"}},"additionalProperties":true},"costs":{"type":"array","items":{"type":"object","description":"This Org’s work orders, per currency.","properties":{"currency":{"type":"string"},"estimated":{"type":["number","null"]},"final":{"type":["number","null"]}},"additionalProperties":true}},"licence":{"type":"object","description":"Null when the deal records no fee.","properties":{"fee":{"type":["number","null"]},"currency":{"type":"string"}},"additionalProperties":true},"compute":{"type":"object","description":"AI and processing calls made for the deal.","properties":{"calls":{"type":"integer"},"gbp":{"type":["number","null"]}},"additionalProperties":true},"durationDays":{"type":"integer","description":"From acceptance (else creation) to the close, or to now while open."},"onTime":{"type":["boolean","null"],"description":"Closed by the end of the delivery window. Null while open or without a window."}},"additionalProperties":true},"deal":{"type":"object","properties":{"name":{"type":"string"},"direction":{"type":"string","enum":["inbound","outbound"]},"status":{"type":"string"},"ourOrgName":{"type":"string"},"counterpartyName":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"offeredAt":{"type":["string","null"]},"acceptedAt":{"type":["string","null"]},"completedAt":{"type":["string","null"],"description":"The last close. Kept when a deal is reopened."},"deliveryStarts":{"type":["string","null"]},"deliveryEnds":{"type":["string","null"]},"licenceFee":{"type":["number","null"]},"licenceCurrency":{"type":["string","null"]},"territories":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"titles":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"kind":{"type":"string"},"year":{"type":["number","null"]},"runtime":{"type":["number","null"],"description":"Minutes."}},"additionalProperties":true}},"ours":{"type":"object","properties":{"deliverables":{"type":"array","items":{"type":"object","properties":{"family":{"type":"string"},"purpose":{"type":["string","null"]},"language":{"type":["string","null"],"description":"BCP 47 tag, where the deliverable has one."},"total":{"type":"integer"},"delivered":{"type":"integer","description":"Every status but awaited."}},"additionalProperties":true}},"uploads":{"type":"integer"},"firstUpload":{"type":["string","null"]},"lastUpload":{"type":["string","null"]},"people":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"role":{"type":["string","null"]},"uploads":{"type":"integer"},"actions":{"type":"integer"},"first":{"type":"string"},"last":{"type":"string"}},"additionalProperties":true}}},"additionalProperties":true},"theirs":{"type":"object","description":"Null when the deal was never shared with the other party.","properties":{"orgName":{"type":"string"},"completedAt":{"type":["string","null"]},"deliverables":{"type":"array","items":{"type":"object","properties":{"family":{"type":"string"},"purpose":{"type":["string","null"]},"language":{"type":["string","null"],"description":"BCP 47 tag, where the deliverable has one."},"total":{"type":"integer"},"delivered":{"type":"integer","description":"Every status but awaited."}},"additionalProperties":true}},"uploads":{"type":"integer"},"people":{"type":"integer"}},"additionalProperties":true},"work":{"type":"array","items":{"type":"object","properties":{"side":{"type":"string","enum":["ours","theirs"]},"reference":{"type":["string","null"]},"name":{"type":"string"},"kind":{"type":"string"},"status":{"type":"string"},"vendor":{"type":["string","null"]},"languages":{"type":"array","items":{"type":"string"}},"items":{"type":"integer"},"responsibleParty":{"type":["string","null"]},"raisedAt":{"type":["string","null"]},"finishedAt":{"type":["string","null"]},"estimatedCost":{"type":["number","null"],"description":"Ours only."},"estimatedCurrency":{"type":["string","null"]},"finalCost":{"type":["number","null"],"description":"Ours only."},"finalCurrency":{"type":["string","null"]}},"additionalProperties":true}},"compute":{"type":"object","properties":{"calls":{"type":"integer"},"gbp":{"type":["number","null"]}},"additionalProperties":true}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getProjectCloseout","x-permission":"project.view","x-permissions":["project.view"]}},"/v1/projects/{projectId}/audit":{"get":{"summary":"The action log of a deal","tags":["Audit"],"description":"Append-only. Who did what, when, with before and after. Needs audit.view.\n\nPermission: audit.view.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false},{"schema":{"type":"string","maxLength":120},"in":"query","name":"subject","required":false},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"action":{"type":"string"},"subject":{"type":"string"},"actorName":{"type":["string","null"]},"actorEmail":{"type":["string","null"]},"before":{},"after":{},"createdAt":{"type":"string"}},"additionalProperties":true}}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listProjectAudit","x-permission":"audit.view","x-permissions":["audit.view"]}},"/v1/webhooks/events":{"get":{"summary":"The events an endpoint can subscribe to","tags":["Webhooks"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"description":{"type":"string"}},"additionalProperties":true}}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listWebhookEvents","x-permission":"none","x-permissions":[],"description":"Permission: none beyond a valid credential of the organisation."}},"/v1/webhooks":{"get":{"summary":"List webhook endpoints","tags":["Webhooks"],"description":"Every endpoint the organisation has, newest first, with its latest delivery and the last day’s counts. Needs integration.configure.\n\nPermission: integration.configure.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"Where events are sent. Every delivery is signed per Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature). The secret is shown once, when the endpoint is created.","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"description":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"active":{"type":"boolean"},"failures":{"type":"integer","description":"Consecutive failed deliveries. Fifty switch the endpoint off; one delivered resets it."},"disabledAt":{"type":["string","null"]},"disabledReason":{"type":["string","null"]},"createdBy":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastDelivery":{"type":"object","description":"The latest delivery, or null.","properties":{"id":{"type":"string","format":"uuid"},"eventType":{"type":"string"},"status":{"type":"string"},"responseStatus":{"type":["number","null"]},"at":{"type":"string"}},"additionalProperties":true},"last24h":{"type":"object","description":"Deliveries made in the last day, by outcome.","properties":{"delivered":{"type":"integer"},"failed":{"type":"integer"},"dead":{"type":"integer"},"pending":{"type":"integer"}},"additionalProperties":true}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true},"example":{"data":[{"id":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","url":"https://hooks.example.com/final-frame","description":"Deliverable changes to the ops board","events":["deliverable.status_changed","asset.received"],"active":true,"failures":0,"disabledAt":null,"disabledReason":null,"createdAt":"2027-01-15T10:30:00.000Z","updatedAt":"2027-01-15T10:30:00.000Z"}],"next_cursor":null}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listWebhooks","x-permission":"integration.configure","x-permissions":["integration.configure"]},"post":{"summary":"Register a webhook endpoint","tags":["Webhooks"],"description":"The signing secret is in this answer and in no other: store it. Idempotent: send an Idempotency-Key, and a retry returns the first answer instead of a second endpoint.\n\nPermission: integration.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"additionalProperties":false,"properties":{"url":{"type":"string","minLength":12,"maxLength":2000,"description":"https, a public host, no credentials in the URL."},"description":{"type":"string","maxLength":500},"events":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","maxLength":60},"description":"Event types from GET /v1/webhooks/events, or [\"*\"] for every event."}}},"example":{"url":"https://hooks.example.com/final-frame","description":"Deliverable changes to the ops board","events":["deliverable.status_changed","asset.received"]}}}},"parameters":[{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"endpoint":{"type":"object","description":"Where events are sent. Every delivery is signed per Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature). The secret is shown once, when the endpoint is created.","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"description":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"active":{"type":"boolean"},"failures":{"type":"integer","description":"Consecutive failed deliveries. Fifty switch the endpoint off; one delivered resets it."},"disabledAt":{"type":["string","null"]},"disabledReason":{"type":["string","null"]},"createdBy":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastDelivery":{"type":"object","description":"The latest delivery, or null.","properties":{"id":{"type":"string","format":"uuid"},"eventType":{"type":"string"},"status":{"type":"string"},"responseStatus":{"type":["number","null"]},"at":{"type":"string"}},"additionalProperties":true},"last24h":{"type":"object","description":"Deliveries made in the last day, by outcome.","properties":{"delivered":{"type":"integer"},"failed":{"type":"integer"},"dead":{"type":"integer"},"pending":{"type":"integer"}},"additionalProperties":true}},"additionalProperties":true},"secret":{"type":"string","description":"whsec_<base64>. Shown once."}},"additionalProperties":true},"example":{"endpoint":{"id":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","url":"https://hooks.example.com/final-frame","description":"Deliverable changes to the ops board","events":["deliverable.status_changed","asset.received"],"active":true,"failures":0,"disabledAt":null,"disabledReason":null,"createdAt":"2027-01-15T10:30:00.000Z","updatedAt":"2027-01-15T10:30:00.000Z"},"secret":"whsec_<shown once, store it>"}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createWebhook","x-permission":"integration.configure","x-permissions":["integration.configure"],"x-idempotent":true}},"/v1/webhooks/{webhookId}":{"patch":{"summary":"Change a webhook endpoint","tags":["Webhooks"],"description":"Any of url, description, events and active. Switching an endpoint on clears its failure count and the reason it was switched off.\n\nPermission: integration.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"url":{"type":"string","minLength":12,"maxLength":2000,"description":"https, a public host, no credentials in the URL."},"description":{"type":"string","maxLength":500},"events":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","maxLength":60},"description":"Event types from GET /v1/webhooks/events, or [\"*\"] for every event."},"active":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"webhookId","required":true}],"responses":{"200":{"description":"Where events are sent. Every delivery is signed per Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature). The secret is shown once, when the endpoint is created.","content":{"application/json":{"schema":{"type":"object","description":"Where events are sent. Every delivery is signed per Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature). The secret is shown once, when the endpoint is created.","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"description":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"active":{"type":"boolean"},"failures":{"type":"integer","description":"Consecutive failed deliveries. Fifty switch the endpoint off; one delivered resets it."},"disabledAt":{"type":["string","null"]},"disabledReason":{"type":["string","null"]},"createdBy":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastDelivery":{"type":"object","description":"The latest delivery, or null.","properties":{"id":{"type":"string","format":"uuid"},"eventType":{"type":"string"},"status":{"type":"string"},"responseStatus":{"type":["number","null"]},"at":{"type":"string"}},"additionalProperties":true},"last24h":{"type":"object","description":"Deliveries made in the last day, by outcome.","properties":{"delivered":{"type":"integer"},"failed":{"type":"integer"},"dead":{"type":"integer"},"pending":{"type":"integer"}},"additionalProperties":true}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"updateWebhook","x-permission":"integration.configure","x-permissions":["integration.configure"],"x-idempotent":true},"delete":{"summary":"Remove a webhook endpoint","tags":["Webhooks"],"description":"Nothing is sent to it again. Its delivery log is kept.\n\nPermission: integration.configure.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"webhookId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"deleteWebhook","x-permission":"integration.configure","x-permissions":["integration.configure"],"x-idempotent":true}},"/v1/webhooks/{webhookId}/deliveries":{"get":{"summary":"The delivery log of an endpoint","tags":["Webhooks"],"description":"Every delivery to this endpoint, newest first: the payload as sent, the attempt, what came back, when the next try is due.\n\nPermission: integration.configure.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false},{"schema":{"type":"string","enum":["pending","delivered","failed","dead"]},"in":"query","name":"status","required":false},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"webhookId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"One event to one endpoint. A 2xx within ten seconds is delivered; anything else retries after 1m, 5m, 30m, 2h and 12h, then is dead.","properties":{"id":{"type":"string","format":"uuid"},"endpointId":{"type":"string","format":"uuid"},"eventId":{"type":"string","description":"The webhook-id header. The same for every delivery of one event, redeliveries included."},"eventType":{"type":"string"},"subject":{"type":"string","description":"What the event is about: a deliverable id, a handoff id."},"status":{"type":"string","enum":["pending","delivered","failed","dead"]},"attempt":{"type":"integer"},"responseStatus":{"type":["number","null"]},"responseMs":{"type":["number","null"]},"error":{"type":["string","null"]},"nextAttemptAt":{"type":["string","null"]},"deliveredAt":{"type":["string","null"]},"createdAt":{"type":"string"},"payload":{"type":"object","description":"The body as sent: id, type, createdAt, organisationId, projectId, subject, data.","properties":{},"additionalProperties":true}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listWebhookDeliveries","x-permission":"integration.configure","x-permissions":["integration.configure"]}},"/v1/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver":{"post":{"summary":"Send a delivery again","tags":["Webhooks"],"description":"A new delivery of the same event to the same endpoint, with the same webhook-id. The original keeps its record. Idempotent on the Idempotency-Key.\n\nPermission: integration.configure.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"webhookId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"deliveryId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"One event to one endpoint. A 2xx within ten seconds is delivered; anything else retries after 1m, 5m, 30m, 2h and 12h, then is dead.","content":{"application/json":{"schema":{"type":"object","description":"One event to one endpoint. A 2xx within ten seconds is delivered; anything else retries after 1m, 5m, 30m, 2h and 12h, then is dead.","properties":{"id":{"type":"string","format":"uuid"},"endpointId":{"type":"string","format":"uuid"},"eventId":{"type":"string","description":"The webhook-id header. The same for every delivery of one event, redeliveries included."},"eventType":{"type":"string"},"subject":{"type":"string","description":"What the event is about: a deliverable id, a handoff id."},"status":{"type":"string","enum":["pending","delivered","failed","dead"]},"attempt":{"type":"integer"},"responseStatus":{"type":["number","null"]},"responseMs":{"type":["number","null"]},"error":{"type":["string","null"]},"nextAttemptAt":{"type":["string","null"]},"deliveredAt":{"type":["string","null"]},"createdAt":{"type":"string"},"payload":{"type":"object","description":"The body as sent: id, type, createdAt, organisationId, projectId, subject, data.","properties":{},"additionalProperties":true}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"redeliverWebhookDelivery","x-permission":"integration.configure","x-permissions":["integration.configure"],"x-idempotent":true}},"/v1/projects/{projectId}/titles/bulk-edit":{"post":{"summary":"One change across many titles","tags":["Titles"],"description":"Only what is sent is written. countryOfOrigin, productionYear and originalLanguage overwrite. genres replaces the set in language (flowGenres pushes it into every language the title carries). credit is added where the title does not already have it; certificate fills its territory. Titles not in this deal are skipped. Needs project.configure. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: project.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["titleIds","language"],"additionalProperties":false,"properties":{"titleIds":{"type":"array","minItems":1,"maxItems":2000,"items":{"type":"string","format":"uuid"}},"language":{"type":"string","minLength":2,"maxLength":35,"description":"The language genres are written in, BCP 47."},"genres":{"type":"array","maxItems":6,"items":{"type":"string","maxLength":40}},"flowGenres":{"type":"boolean"},"countryOfOrigin":{"type":"string","minLength":2,"maxLength":12},"productionYear":{"type":"integer","minimum":1870,"maximum":2200},"originalLanguage":{"type":"string","minLength":2,"maxLength":35},"credit":{"type":"object","required":["person","credit"],"additionalProperties":false,"properties":{"person":{"type":"string","minLength":1,"maxLength":200},"credit":{"type":"string","minLength":1,"maxLength":80},"characterName":{"type":["string","null"],"maxLength":200}}},"certificate":{"type":"object","required":["territory"],"additionalProperties":false,"properties":{"territory":{"type":"string","minLength":2,"maxLength":12},"body":{"type":["string","null"],"maxLength":60},"value":{"type":["string","null"],"maxLength":40}}}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"How many of each were written.","content":{"application/json":{"schema":{"type":"object","description":"How many of each were written.","properties":{"titles":{"type":"integer"},"genres":{"type":"integer"},"credits":{"type":"integer"},"certificates":{"type":"integer"}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"bulkEditTitles","x-permission":"project.configure","x-permissions":["project.configure"],"x-idempotent":true}},"/v1/rights/holdings":{"post":{"summary":"Record a holding (rights in)","tags":["Rights"],"description":"What the organisation holds in a work: owned, licensed in, co-produced or commissioned; which rights (or \"all\"), where, from when to when (endKind date, open or perpetuity), exclusive or not. Rights are checked against the vocabulary, and an end date is needed when the term ends on a date. Raises rights.holding_recorded. Needs org.settings.edit. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: org.settings.edit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["identity","source","rights"],"additionalProperties":false,"properties":{"identity":{"type":"string","minLength":1,"maxLength":400,"description":"The work’s identity from GET /v1/rights/works: a film or a show, or a season (\"#s2\") or an episode (\"#s2e3\")."},"titleId":{"type":["string","null"],"format":"uuid","description":"The title copy to file it on, for navigation."},"source":{"type":"string","enum":["owned","licensed_in","co_production","commissioned"]},"counterparty":{"type":["string","null"],"maxLength":200,"description":"Who it was licensed from."},"dealReference":{"type":["string","null"],"maxLength":120},"projectId":{"type":["string","null"],"format":"uuid","description":"The inbound deal it came from."},"rights":{"type":"array","maxItems":120,"items":{"type":"string","maxLength":60},"description":"Right codes from GET /v1/reference/rights, or \"all\"."},"rightsExcluded":{"type":"array","maxItems":120,"items":{"type":"string","maxLength":60},"description":"Right codes from GET /v1/reference/rights, or \"all\"."},"territories":{"type":"array","maxItems":300,"items":{"type":"string","maxLength":20},"description":"Country codes (ISO 3166-1 alpha-2), region codes or WORLD."},"territoriesExcluded":{"type":"array","maxItems":300,"items":{"type":"string","maxLength":20},"description":"Country codes (ISO 3166-1 alpha-2), region codes or WORLD."},"languages":{"type":"array","maxItems":60,"items":{"type":"string","maxLength":20}},"starts":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"ends":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"endKind":{"type":"string","enum":["date","open","perpetuity"]},"exclusive":{"type":"boolean"},"sublicensable":{"type":"boolean"},"chainOfTitle":{"type":"string","enum":["verified","incomplete","unknown"]},"note":{"type":["string","null"],"maxLength":2000}}}}}},"parameters":[{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"Rights in: what the organisation holds in a work, where, for how long, and from whom.","content":{"application/json":{"schema":{"type":"object","description":"Rights in: what the organisation holds in a work, where, for how long, and from whom.","properties":{"id":{"type":"string","format":"uuid"},"identity":{"type":"string"},"titleId":{"type":["string","null"],"description":"The title copy it was recorded on."},"source":{"type":"string","enum":["owned","licensed_in","co_production","commissioned"]},"counterparty":{"type":["string","null"]},"dealReference":{"type":["string","null"]},"projectId":{"type":["string","null"],"description":"The inbound deal it came from, when one did."},"rights":{"type":"array","items":{"type":"string"}},"rightsExcluded":{"type":"array","items":{"type":"string"}},"territories":{"type":"array","items":{"type":"string"}},"territoriesExcluded":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string"}},"starts":{"type":["string","null"],"description":"ISO date."},"ends":{"type":["string","null"],"description":"ISO date."},"endKind":{"type":"string","enum":["date","open","perpetuity"]},"exclusive":{"type":"boolean"},"sublicensable":{"type":"boolean"},"chainOfTitle":{"type":"string","enum":["verified","incomplete","unknown"]},"note":{"type":["string","null"]},"recordedBy":{"type":"string"},"recordedByName":{"type":["string","null"]},"recordedAt":{"type":"string"},"revokedAt":{"type":["string","null"],"description":"When it was revoked; null while it stands."}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createRightsHolding","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/rights/holdings/{holdingId}/revoke":{"post":{"summary":"Revoke a holding","tags":["Rights"],"description":"The holding stops counting, and its record stays, with who recorded it and who revoked it. Revoking one already revoked changes nothing. Needs org.settings.edit. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: org.settings.edit.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"holdingId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Rights in: what the organisation holds in a work, where, for how long, and from whom.","content":{"application/json":{"schema":{"type":"object","description":"Rights in: what the organisation holds in a work, where, for how long, and from whom.","properties":{"id":{"type":"string","format":"uuid"},"identity":{"type":"string"},"titleId":{"type":["string","null"],"description":"The title copy it was recorded on."},"source":{"type":"string","enum":["owned","licensed_in","co_production","commissioned"]},"counterparty":{"type":["string","null"]},"dealReference":{"type":["string","null"]},"projectId":{"type":["string","null"],"description":"The inbound deal it came from, when one did."},"rights":{"type":"array","items":{"type":"string"}},"rightsExcluded":{"type":"array","items":{"type":"string"}},"territories":{"type":"array","items":{"type":"string"}},"territoriesExcluded":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string"}},"starts":{"type":["string","null"],"description":"ISO date."},"ends":{"type":["string","null"],"description":"ISO date."},"endKind":{"type":"string","enum":["date","open","perpetuity"]},"exclusive":{"type":"boolean"},"sublicensable":{"type":"boolean"},"chainOfTitle":{"type":"string","enum":["verified","incomplete","unknown"]},"note":{"type":["string","null"]},"recordedBy":{"type":"string"},"recordedByName":{"type":["string","null"]},"recordedAt":{"type":"string"},"revokedAt":{"type":["string","null"],"description":"When it was revoked; null while it stands."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"revokeRightsHolding","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/rights/restrictions":{"post":{"summary":"Record a restriction","tags":["Rights"],"description":"Something that narrows a work’s rights: a holdback, a clearance, a talent restriction, a blackout, an approval. Which rights (none means all), where (none means WORLD), and the window. Rights are checked against the vocabulary. Needs org.settings.edit. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: org.settings.edit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["identity","kind"],"additionalProperties":false,"properties":{"identity":{"type":"string","minLength":1,"maxLength":400,"description":"The work’s identity from GET /v1/rights/works: a film or a show, or a season (\"#s2\") or an episode (\"#s2e3\")."},"kind":{"type":"string","enum":["holdback","clearance","talent","blackout","approval","other"]},"rights":{"type":"array","maxItems":120,"items":{"type":"string","maxLength":60},"description":"Right codes from GET /v1/reference/rights, or \"all\"."},"territories":{"type":"array","maxItems":300,"items":{"type":"string","maxLength":20},"description":"Country codes (ISO 3166-1 alpha-2), region codes or WORLD."},"territoriesExcluded":{"type":"array","maxItems":300,"items":{"type":"string","maxLength":20},"description":"Country codes (ISO 3166-1 alpha-2), region codes or WORLD."},"languages":{"type":"array","maxItems":60,"items":{"type":"string","maxLength":20}},"starts":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"ends":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"sourceProjectId":{"type":["string","null"],"format":"uuid","description":"The deal that imposes it."},"sourceHoldingId":{"type":["string","null"],"format":"uuid","description":"The holding that carries it."},"note":{"type":["string","null"],"maxLength":2000}}}}}},"parameters":[{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"What narrows a work’s rights: a holdback, a clearance, a talent restriction, a blackout.","content":{"application/json":{"schema":{"type":"object","description":"What narrows a work’s rights: a holdback, a clearance, a talent restriction, a blackout.","properties":{"id":{"type":"string","format":"uuid"},"identity":{"type":"string"},"kind":{"type":"string","enum":["holdback","clearance","talent","blackout","approval","other"]},"rights":{"type":"array","items":{"type":"string"}},"territories":{"type":"array","items":{"type":"string"}},"territoriesExcluded":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string"}},"starts":{"type":["string","null"],"description":"ISO date."},"ends":{"type":["string","null"],"description":"ISO date."},"sourceProjectId":{"type":["string","null"]},"sourceHoldingId":{"type":["string","null"]},"note":{"type":["string","null"]},"recordedBy":{"type":"string"},"recordedByName":{"type":["string","null"]},"recordedAt":{"type":"string"},"revokedAt":{"type":["string","null"],"description":"When it was revoked; null while it stands."}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createRightsRestriction","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/rights/restrictions/{restrictionId}/revoke":{"post":{"summary":"Revoke a restriction","tags":["Rights"],"description":"The restriction stops counting, and its record stays. Not found when there is no standing restriction with that id. Needs org.settings.edit. Idempotent on the Idempotency-Key: a retry returns the first answer. Written to the action log.\n\nPermission: org.settings.edit.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"restrictionId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"revoked":{"type":"boolean"}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"revokeRightsRestriction","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/rights/avails/ema":{"get":{"summary":"Export EMA Avails","tags":["Rights"],"description":"Availability as an EMA Avails Excel 1.7.3 workbook: a Movies sheet for films and extras, a TV sheet at episode level (season and series fields, identities \"#s2e3\"). One row per work, ISO 3166-1 alpha-2 country, EMA LicenseType and free interval, from the same availability answer as GET /v1/rights/works, so it matches the Rights grid. Our rights map to LicenseType through a fixed table (EST, VOD, SVOD, AVOD, FVOD, POEST); rights EMA has no type for are skipped and counted. include=licensed writes the organisation's licensed-out grants instead (Licensee, ContractID, Exclusive), include=both writes both. Title, release, rating, credit and company columns come from the title records and the release plan; a licensed row adds its deal's fee, format, languages and the licensee's own ids. Columns with no source stay blank. For a large catalogue, POST /v1/rights/avails/ema/exports builds the same workbook in the background. The X-FF-Avails-Summary header carries the counts as JSON: rows, skippedRights, skippedGrants, undatedGrants, seriesWithoutEpisodes, truncated (past 1,000 works or 100,000 rows). Needs org.view.\n\nPermission: org.view.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"q","required":false},{"schema":{"type":"string","enum":["film","series","extras"]},"in":"query","name":"kind","required":false},{"schema":{"type":"string","enum":["owned","licensed_in","mixed","not_recorded"]},"in":"query","name":"position","required":false},{"schema":{"type":"string","pattern":"^[A-Za-z_]{2,20}$"},"in":"query","name":"territory","required":false,"description":"A country code, a region code or WORLD. Default WORLD. Rows are written per country."},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"in":"query","name":"from","required":false,"description":"ISO date. Default today."},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"in":"query","name":"to","required":false,"description":"ISO date. Default open."},{"schema":{"type":"string","maxLength":2400},"in":"query","name":"rights","required":false,"description":"A comma list of right codes from GET /v1/reference/rights, or \"categories\". Default the seven digital and TV rights."},{"schema":{"type":"boolean"},"in":"query","name":"exclusive","required":false,"description":"Offer only what is available exclusively."},{"schema":{"type":"string","enum":["available","licensed","both"],"default":"available"},"in":"query","name":"include","required":false},{"schema":{"type":"array","maxItems":200,"items":{"type":"string","maxLength":400}},"in":"query","name":"identity","required":false,"description":"Particular works, repeated in a query. A season or episode identity means its show."}],"responses":{"200":{"description":"EMA Avails Excel 1.7.3.","content":{"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"exportEmaAvails","x-permission":"org.view","x-permissions":["org.view"]}},"/v1/rights/avails/ema/exports":{"post":{"summary":"Start an EMA Avails export","tags":["Rights"],"description":"The same workbook as GET /v1/rights/avails/ema, built in the background for a large catalogue. Answers 202 with the export's id at once and a Location at its run (GET /v1/processes/{id}); poll that, or GET /v1/rights/avails/ema/exports/{id}, for the percent and, when done, a 15-minute signed download link. The body takes the same filters as the GET's query. Needs org.view and an Idempotency-Key.\n\nPermission: org.view.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"q":{"type":"string","maxLength":200},"kind":{"type":"string","enum":["film","series","extras"]},"position":{"type":"string","enum":["owned","licensed_in","mixed","not_recorded"]},"territory":{"type":"string","pattern":"^[A-Za-z_]{2,20}$","description":"A country code, a region code or WORLD. Default WORLD. Rows are written per country."},"from":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"ISO date. Default today."},"to":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"ISO date. Default open."},"rights":{"type":"string","maxLength":2400,"description":"A comma list of right codes from GET /v1/reference/rights, or \"categories\". Default the seven digital and TV rights."},"exclusive":{"type":"boolean","description":"Offer only what is available exclusively."},"include":{"type":"string","enum":["available","licensed","both"],"default":"available"},"identity":{"type":"array","maxItems":200,"items":{"type":"string","maxLength":400},"description":"Particular works, repeated in a query. A season or episode identity means its show."},"search":{"type":"object","required":["rights"],"additionalProperties":false,"properties":{"rights":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","maxLength":60}},"territories":{"type":"array","maxItems":20,"items":{"type":"string","maxLength":20}},"from":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"to":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"exclusive":{"type":"boolean"},"match":{"type":"string","enum":["full","partial","any"]},"q":{"type":["string","null"],"maxLength":200},"kind":{"type":["string","null"],"enum":["film","series","extras",null]}},"description":"An avails search (as GET /v1/rights/search takes it, with rights and territories as arrays). When given, the workbook holds the works it matches, its rights, the countries of its territories and its window; the other filters are ignored."}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"202":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The export, which is also its run on the processing log."},"statusUrl":{"type":"string","description":"GET /v1/processes/{id}: the run. Also the Location header."},"exportUrl":{"type":"string","description":"GET /v1/rights/avails/ema/exports/{id}: the percent and, when done, the download link."}},"required":["id"]}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"startEmaAvailsExport","x-permission":"org.view","x-permissions":["org.view"],"x-idempotent":true}},"/v1/rights/avails/ema/exports/{id}":{"get":{"summary":"Read an EMA Avails export","tags":["Rights"],"description":"How far an export has got, in percent and in words, and once it is done a signed link to the workbook. Only the caller that started an export can read it. Needs org.view.\n\nPermission: org.view.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"How far an export has got. Poll until status is done, failed or cancelled.","content":{"application/json":{"schema":{"type":"object","description":"How far an export has got. Poll until status is done, failed or cancelled.","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["running","done","failed","cancelled"]},"percent":{"type":"integer","minimum":0,"maximum":100},"phrase":{"type":"string","description":"What it is doing, in words: \"Reading rights for 400 of 586 works\"."},"filename":{"type":["string","null"]},"summary":{"type":["object","null"],"description":"The same counts as X-FF-Avails-Summary, once done."},"downloadUrl":{"type":["string","null"],"description":"A signed link to the workbook, good for 15 minutes. Ask again for a fresh one."},"expiresAt":{"type":["string","null"],"format":"date-time"},"error":{"type":["string","null"]}}}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getEmaAvailsExport","x-permission":"org.view","x-permissions":["org.view"]}},"/v1/rights/search":{"get":{"summary":"Search avails: what can I sell?","tags":["Rights"],"description":"Every work in the library against several rights and territories over one window. Each comes back fully available (every right free in every territory for the whole window), partly available (with the gaps: per right and territory, the countries not free grouped by state and licensee, and the free intervals), or unavailable (with the reasons). Pencilled windows count as free and name the prospect. match: full returns fully available works only, partial (the default) adds partly available ones, any adds unavailable ones too. rights and territories are comma lists, at most 20 and 20. The same availability function as GET /v1/rights/works. A library past 1,000 works answers for the first thousand and says truncated; narrow with q or kind. Needs org.view.\n\nPermission: org.view.","parameters":[{"schema":{"type":"string","maxLength":1200},"in":"query","name":"rights","required":true,"description":"Right codes from GET /v1/reference/rights, comma separated."},{"schema":{"type":"string","maxLength":600},"in":"query","name":"territories","required":false,"description":"Country or region codes, or WORLD, comma separated. Default WORLD."},{"schema":{"type":"string"},"in":"query","name":"from","required":false,"description":"ISO date. Default today."},{"schema":{"type":"string"},"in":"query","name":"to","required":false,"description":"ISO date. Default open."},{"schema":{"type":"boolean"},"in":"query","name":"exclusive","required":false,"description":"Exclusive availability wanted."},{"schema":{"type":"string","enum":["full","partial","any"],"default":"partial"},"in":"query","name":"match","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"q","required":false},{"schema":{"type":"string","enum":["film","series","extras"]},"in":"query","name":"kind","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"identity":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"},"productionYear":{"type":["integer","null"]},"verdict":{"type":"string","enum":["full","partial","none"]},"verdictLabel":{"type":"string"},"freePairs":{"type":"integer"},"pairCount":{"type":"integer"},"pencils":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}},"pairs":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}}},"additionalProperties":true}},"next_cursor":{"type":["string","null"]},"total":{"type":"integer"},"counts":{"type":"object","properties":{"full":{"type":"integer"},"partial":{"type":"integer"},"none":{"type":"integer"}},"additionalProperties":true},"works":{"type":"integer"},"truncated":{"type":"boolean"}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"searchAvails","x-permission":"org.view","x-permissions":["org.view"]}},"/v1/rights/pencils":{"get":{"summary":"List pencils","tags":["Rights"],"description":"Pencils, soonest to lapse first. state live (the default: held and not past the last day), lapsed, closed (released or converted) or all; identity for one work with its seasons and episodes; expiresBy for the ones lapsing by a date. Needs org.view.\n\nPermission: org.view.","parameters":[{"schema":{"type":"string","maxLength":400},"in":"query","name":"identity","required":false},{"schema":{"type":"string","enum":["live","lapsed","closed","all"]},"in":"query","name":"state","required":false},{"schema":{"type":"string"},"in":"query","name":"expiresBy","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"prospect","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"A pencil: a right, territory and window on a work held for a named prospect until a date. Availability reads it as pencilled, never as sold.","properties":{"id":{"type":"string","format":"uuid"},"identity":{"type":"string"},"titleId":{"type":["string","null"]},"workName":{"type":["string","null"]},"scope":{"type":["string","null"],"description":"\"Season 2\" or \"S2 E3\" for a pencil below the show."},"prospect":{"type":"string","description":"Who it is pencilled for."},"rights":{"type":"array","items":{"type":"string"}},"territories":{"type":"array","items":{"type":"string"}},"territoriesExcluded":{"type":"array","items":{"type":"string"}},"starts":{"type":"string"},"ends":{"type":["string","null"],"description":"ISO date; null is open."},"exclusive":{"type":"boolean"},"expiresOn":{"type":"string","description":"The last day the pencil stands."},"status":{"type":"string","enum":["held","lapsed","released","converted"]},"note":{"type":["string","null"]},"convertedProjectId":{"type":["string","null"],"description":"The deal it became."},"convertedProjectName":{"type":["string","null"]},"recordedByName":{"type":["string","null"]},"recordedAt":{"type":"string"},"closedByName":{"type":["string","null"]},"closedAt":{"type":["string","null"]}},"additionalProperties":true}},"next_cursor":{"type":["string","null"]},"total":{"type":"integer"}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listPencils","x-permission":"org.view","x-permissions":["org.view"]},"post":{"summary":"Pencil a right","tags":["Rights"],"description":"Holds a right, territory and window on a work for a named prospect until expiresOn. Availability then reads those cells as pencilled (available, with the prospect named), never as sold. Rights as codes (none or \"all\" means every right), territories default WORLD. Answers with the pencil and warnings: where the window is not free today (sold, held back, not held), with the reasons. Raises rights.pencil_changed. Needs org.settings.edit. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: org.settings.edit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["identity","prospect","rights","starts","expiresOn"],"additionalProperties":false,"properties":{"identity":{"type":"string","minLength":1,"maxLength":400,"description":"The work’s identity from GET /v1/rights/works; a season is \"#s2\", an episode \"#s2e3\"."},"titleId":{"type":["string","null"],"format":"uuid"},"prospect":{"type":"string","minLength":1,"maxLength":200},"rights":{"type":"array","maxItems":120,"items":{"type":"string","maxLength":60}},"territories":{"type":"array","maxItems":300,"items":{"type":"string","maxLength":20},"description":"Country codes (ISO 3166-1 alpha-2), region codes or WORLD."},"territoriesExcluded":{"type":"array","maxItems":300,"items":{"type":"string","maxLength":20},"description":"Country codes (ISO 3166-1 alpha-2), region codes or WORLD."},"starts":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"ends":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"exclusive":{"type":"boolean","default":true},"expiresOn":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"note":{"type":["string","null"],"maxLength":2000}}}}}},"parameters":[{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"pencil":{"type":"object","description":"A pencil: a right, territory and window on a work held for a named prospect until a date. Availability reads it as pencilled, never as sold.","properties":{"id":{"type":"string","format":"uuid"},"identity":{"type":"string"},"titleId":{"type":["string","null"]},"workName":{"type":["string","null"]},"scope":{"type":["string","null"],"description":"\"Season 2\" or \"S2 E3\" for a pencil below the show."},"prospect":{"type":"string","description":"Who it is pencilled for."},"rights":{"type":"array","items":{"type":"string"}},"territories":{"type":"array","items":{"type":"string"}},"territoriesExcluded":{"type":"array","items":{"type":"string"}},"starts":{"type":"string"},"ends":{"type":["string","null"],"description":"ISO date; null is open."},"exclusive":{"type":"boolean"},"expiresOn":{"type":"string","description":"The last day the pencil stands."},"status":{"type":"string","enum":["held","lapsed","released","converted"]},"note":{"type":["string","null"]},"convertedProjectId":{"type":["string","null"],"description":"The deal it became."},"convertedProjectName":{"type":["string","null"]},"recordedByName":{"type":["string","null"]},"recordedAt":{"type":"string"},"closedByName":{"type":["string","null"]},"closedAt":{"type":["string","null"]}},"additionalProperties":true},"warnings":{"type":"array","items":{"type":"object","properties":{"right":{"type":"string"},"territory":{"type":"string"},"state":{"type":"string"},"label":{"type":"string"},"reasons":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createPencil","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/rights/pencils/{pencilId}/extend":{"post":{"summary":"Extend a pencil","tags":["Rights"],"description":"A new last day for a held pencil, from today on. A lapsed pencil comes back into force. Refused for a released or converted one. Raises rights.pencil_changed. Needs org.settings.edit. Idempotent on the Idempotency-Key.\n\nPermission: org.settings.edit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["expiresOn"],"additionalProperties":false,"properties":{"expiresOn":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"pencilId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"A pencil: a right, territory and window on a work held for a named prospect until a date. Availability reads it as pencilled, never as sold.","content":{"application/json":{"schema":{"type":"object","description":"A pencil: a right, territory and window on a work held for a named prospect until a date. Availability reads it as pencilled, never as sold.","properties":{"id":{"type":"string","format":"uuid"},"identity":{"type":"string"},"titleId":{"type":["string","null"]},"workName":{"type":["string","null"]},"scope":{"type":["string","null"],"description":"\"Season 2\" or \"S2 E3\" for a pencil below the show."},"prospect":{"type":"string","description":"Who it is pencilled for."},"rights":{"type":"array","items":{"type":"string"}},"territories":{"type":"array","items":{"type":"string"}},"territoriesExcluded":{"type":"array","items":{"type":"string"}},"starts":{"type":"string"},"ends":{"type":["string","null"],"description":"ISO date; null is open."},"exclusive":{"type":"boolean"},"expiresOn":{"type":"string","description":"The last day the pencil stands."},"status":{"type":"string","enum":["held","lapsed","released","converted"]},"note":{"type":["string","null"]},"convertedProjectId":{"type":["string","null"],"description":"The deal it became."},"convertedProjectName":{"type":["string","null"]},"recordedByName":{"type":["string","null"]},"recordedAt":{"type":"string"},"closedByName":{"type":["string","null"]},"closedAt":{"type":["string","null"]}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"extendPencil","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/rights/pencils/{pencilId}/release":{"post":{"summary":"Release a pencil","tags":["Rights"],"description":"Lets the hold go: the cells read as they are beneath it again. Its record stays. Refused for one already released or converted. Raises rights.pencil_changed. Needs org.settings.edit. Idempotent on the Idempotency-Key.\n\nPermission: org.settings.edit.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"pencilId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"A pencil: a right, territory and window on a work held for a named prospect until a date. Availability reads it as pencilled, never as sold.","content":{"application/json":{"schema":{"type":"object","description":"A pencil: a right, territory and window on a work held for a named prospect until a date. Availability reads it as pencilled, never as sold.","properties":{"id":{"type":"string","format":"uuid"},"identity":{"type":"string"},"titleId":{"type":["string","null"]},"workName":{"type":["string","null"]},"scope":{"type":["string","null"],"description":"\"Season 2\" or \"S2 E3\" for a pencil below the show."},"prospect":{"type":"string","description":"Who it is pencilled for."},"rights":{"type":"array","items":{"type":"string"}},"territories":{"type":"array","items":{"type":"string"}},"territoriesExcluded":{"type":"array","items":{"type":"string"}},"starts":{"type":"string"},"ends":{"type":["string","null"],"description":"ISO date; null is open."},"exclusive":{"type":"boolean"},"expiresOn":{"type":"string","description":"The last day the pencil stands."},"status":{"type":"string","enum":["held","lapsed","released","converted"]},"note":{"type":["string","null"]},"convertedProjectId":{"type":["string","null"],"description":"The deal it became."},"convertedProjectName":{"type":["string","null"]},"recordedByName":{"type":["string","null"]},"recordedAt":{"type":"string"},"closedByName":{"type":["string","null"]},"closedAt":{"type":["string","null"]}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"releasePencil","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/rights/pencils/{pencilId}/convert":{"post":{"summary":"Convert a pencil into a deal","tags":["Rights"],"description":"Marks a held pencil as become the named outbound deal (create the deal first with POST /v1/projects, with the work on it). The pencil stops counting; the deal’s own grants are what availability reads from then on. Raises rights.pencil_changed. Needs org.settings.edit. Idempotent on the Idempotency-Key.\n\nPermission: org.settings.edit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["projectId"],"additionalProperties":false,"properties":{"projectId":{"type":"string","format":"uuid"}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"pencilId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"A pencil: a right, territory and window on a work held for a named prospect until a date. Availability reads it as pencilled, never as sold.","content":{"application/json":{"schema":{"type":"object","description":"A pencil: a right, territory and window on a work held for a named prospect until a date. Availability reads it as pencilled, never as sold.","properties":{"id":{"type":"string","format":"uuid"},"identity":{"type":"string"},"titleId":{"type":["string","null"]},"workName":{"type":["string","null"]},"scope":{"type":["string","null"],"description":"\"Season 2\" or \"S2 E3\" for a pencil below the show."},"prospect":{"type":"string","description":"Who it is pencilled for."},"rights":{"type":"array","items":{"type":"string"}},"territories":{"type":"array","items":{"type":"string"}},"territoriesExcluded":{"type":"array","items":{"type":"string"}},"starts":{"type":"string"},"ends":{"type":["string","null"],"description":"ISO date; null is open."},"exclusive":{"type":"boolean"},"expiresOn":{"type":"string","description":"The last day the pencil stands."},"status":{"type":"string","enum":["held","lapsed","released","converted"]},"note":{"type":["string","null"]},"convertedProjectId":{"type":["string","null"],"description":"The deal it became."},"convertedProjectName":{"type":["string","null"]},"recordedByName":{"type":["string","null"]},"recordedAt":{"type":"string"},"closedByName":{"type":["string","null"]},"closedAt":{"type":["string","null"]}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"convertPencil","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/delivery-specs":{"get":{"summary":"The organisation’s delivery specs","tags":["Delivery specs"],"description":"Every spec in the organisation’s library, newest first. A deal names the ones it delivers against. Needs org.view.\n\nPermission: org.view.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"A destination’s technical and editorial requirements.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"destination":{"type":["string","null"],"description":"The platform in a market the spec is for."},"description":{"type":["string","null"]},"doc":{"type":"object","description":"The composition: format sections at their values, metadata demands, image templates.","properties":{"formats":{"type":"object","description":"Keyed by format section (GET /v1/config, formats): the values the destination demands.","properties":{},"additionalProperties":true},"metadata":{"type":"object","description":"Keyed by metadata field: { required, limit }.","properties":{},"additionalProperties":true},"images":{"type":"object","description":"Keyed by image template: { set }.","properties":{},"additionalProperties":true},"rails":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"sections":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"description":"Either/or groups over the sections, e.g. HDR IMF or SDR ProRes."}},"additionalProperties":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listDeliverySpecs","x-permission":"org.view","x-permissions":["org.view"]},"post":{"summary":"Create a delivery spec","tags":["Delivery specs"],"description":"A new spec in the organisation’s library. Every key under doc.formats names a format section the organisation has (built in, or its own template); a rail groups sections the spec includes. Needs org.settings.edit. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: org.settings.edit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"destination":{"type":["string","null"],"maxLength":200},"description":{"type":["string","null"],"maxLength":2000},"doc":{"type":"object","properties":{"formats":{"type":"object","maxProperties":120},"metadata":{"type":"object","maxProperties":200,"additionalProperties":{"type":"object","properties":{"required":{"type":"boolean"},"limit":{"type":"number","minimum":1,"maximum":20000}},"additionalProperties":false}},"images":{"type":"object","maxProperties":500,"additionalProperties":{"type":"object","properties":{"set":{"type":"string","maxLength":120}},"additionalProperties":false}},"rails":{"type":"array","maxItems":20,"items":{"type":"object","required":["label","sections"],"properties":{"label":{"type":"string","minLength":1,"maxLength":120},"sections":{"type":"array","minItems":2,"maxItems":8,"items":{"type":"string","minLength":1,"maxLength":64}}},"additionalProperties":false}}},"additionalProperties":false}}}}}},"parameters":[{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"A destination’s technical and editorial requirements.","content":{"application/json":{"schema":{"type":"object","description":"A destination’s technical and editorial requirements.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"destination":{"type":["string","null"],"description":"The platform in a market the spec is for."},"description":{"type":["string","null"]},"doc":{"type":"object","description":"The composition: format sections at their values, metadata demands, image templates.","properties":{"formats":{"type":"object","description":"Keyed by format section (GET /v1/config, formats): the values the destination demands.","properties":{},"additionalProperties":true},"metadata":{"type":"object","description":"Keyed by metadata field: { required, limit }.","properties":{},"additionalProperties":true},"images":{"type":"object","description":"Keyed by image template: { set }.","properties":{},"additionalProperties":true},"rails":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"sections":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"description":"Either/or groups over the sections, e.g. HDR IMF or SDR ProRes."}},"additionalProperties":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createDeliverySpec","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/industry-specs":{"get":{"summary":"The industry’s published specs","tags":["Delivery specs"],"description":"The published demands of the platforms, kept by Final Frame and readable by every organisation. Duplicate one into the library to change it, or name it on a deal as it stands.\n\nPermission: none beyond a valid credential of the organisation.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"A platform’s published spec, as Final Frame keeps it.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"destination":{"type":["string","null"]},"description":{"type":["string","null"]},"doc":{"type":"object","description":"The composition: format sections at their values, metadata demands, image templates.","properties":{"formats":{"type":"object","description":"Keyed by format section (GET /v1/config, formats): the values the destination demands.","properties":{},"additionalProperties":true},"metadata":{"type":"object","description":"Keyed by metadata field: { required, limit }.","properties":{},"additionalProperties":true},"images":{"type":"object","description":"Keyed by image template: { set }.","properties":{},"additionalProperties":true},"rails":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"sections":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"description":"Either/or groups over the sections, e.g. HDR IMF or SDR ProRes."}},"additionalProperties":true},"sourceUrl":{"type":["string","null"],"description":"Where the platform publishes it."},"sourceNote":{"type":["string","null"],"description":"A caveat, where one is owed."},"updatedAt":{"type":"string"}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listIndustrySpecs","x-permission":"none","x-permissions":[]}},"/v1/delivery-specs/{specId}":{"patch":{"summary":"Change a delivery spec","tags":["Delivery specs"],"description":"Only the fields sent change; doc, when sent, replaces the whole composition. The same checks as creating one. Deals that name the spec follow it. Needs org.settings.edit. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: org.settings.edit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"destination":{"type":["string","null"],"maxLength":200},"description":{"type":["string","null"],"maxLength":2000},"doc":{"type":"object","properties":{"formats":{"type":"object","maxProperties":120},"metadata":{"type":"object","maxProperties":200,"additionalProperties":{"type":"object","properties":{"required":{"type":"boolean"},"limit":{"type":"number","minimum":1,"maximum":20000}},"additionalProperties":false}},"images":{"type":"object","maxProperties":500,"additionalProperties":{"type":"object","properties":{"set":{"type":"string","maxLength":120}},"additionalProperties":false}},"rails":{"type":"array","maxItems":20,"items":{"type":"object","required":["label","sections"],"properties":{"label":{"type":"string","minLength":1,"maxLength":120},"sections":{"type":"array","minItems":2,"maxItems":8,"items":{"type":"string","minLength":1,"maxLength":64}}},"additionalProperties":false}}},"additionalProperties":false}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"specId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"A destination’s technical and editorial requirements.","content":{"application/json":{"schema":{"type":"object","description":"A destination’s technical and editorial requirements.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"destination":{"type":["string","null"],"description":"The platform in a market the spec is for."},"description":{"type":["string","null"]},"doc":{"type":"object","description":"The composition: format sections at their values, metadata demands, image templates.","properties":{"formats":{"type":"object","description":"Keyed by format section (GET /v1/config, formats): the values the destination demands.","properties":{},"additionalProperties":true},"metadata":{"type":"object","description":"Keyed by metadata field: { required, limit }.","properties":{},"additionalProperties":true},"images":{"type":"object","description":"Keyed by image template: { set }.","properties":{},"additionalProperties":true},"rails":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"sections":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"description":"Either/or groups over the sections, e.g. HDR IMF or SDR ProRes."}},"additionalProperties":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"updateDeliverySpec","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/industry-specs/{specId}/duplicate":{"post":{"summary":"Copy an industry spec into the library","tags":["Delivery specs"],"description":"The copy is the organisation’s own, named after its origin and the day it was taken (Netflix Global Delivery (Org) - 5 Oct 2026), so an edited copy never passes for the industry’s current word. Needs org.settings.edit. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: org.settings.edit.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"specId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"A destination’s technical and editorial requirements.","content":{"application/json":{"schema":{"type":"object","description":"A destination’s technical and editorial requirements.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"destination":{"type":["string","null"],"description":"The platform in a market the spec is for."},"description":{"type":["string","null"]},"doc":{"type":"object","description":"The composition: format sections at their values, metadata demands, image templates.","properties":{"formats":{"type":"object","description":"Keyed by format section (GET /v1/config, formats): the values the destination demands.","properties":{},"additionalProperties":true},"metadata":{"type":"object","description":"Keyed by metadata field: { required, limit }.","properties":{},"additionalProperties":true},"images":{"type":"object","description":"Keyed by image template: { set }.","properties":{},"additionalProperties":true},"rails":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"sections":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"description":"Either/or groups over the sections, e.g. HDR IMF or SDR ProRes."}},"additionalProperties":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"duplicateIndustrySpec","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/rights/works/{identity}/windows":{"get":{"summary":"A work’s release plan","tags":["Rights"],"description":"The windows the organisation has written down for the work, its release date and status, and the plan the date projects under its profile. An unedited work still answers with a projection, marked projected.\n\nPermission: org.view.","parameters":[{"schema":{"type":"string","maxLength":400},"in":"path","name":"identity","required":true}],"responses":{"200":{"description":"A work’s release plan: what is written down, and what the release date projects.","content":{"application/json":{"schema":{"type":"object","description":"A work’s release plan: what is written down, and what the release date projects.","properties":{"work":{"type":"object","properties":{"identity":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"},"titleId":{"type":["string","null"]}},"additionalProperties":true},"release":{"type":"object","description":"The release facts, or null until they are set.","properties":{"releaseDate":{"type":["string","null"],"description":"ISO date."},"releaseStatus":{"type":"string","enum":["released","pre_release","in_production","announced"]},"profile":{"type":["string","null"],"description":"The work’s own profile, or null for the organisation’s."},"note":{"type":["string","null"]}},"additionalProperties":true},"windows":{"type":"array","items":{"type":"object","description":"One window: a right in a territory, from a start to an end, with its standing.","properties":{"id":{"type":["string","null"]},"windowKind":{"type":"string","description":"theatrical, pvod, est, tvod, physical, airline, hotel, pay1, pay1_second, pay1_return, pay2, free_tv, library, avod, fast, or a tv_ kind for a series."},"rightCode":{"type":"string","description":"A code from GET /v1/reference/rights."},"territory":{"type":"string","description":"A country, a region or WORLD."},"starts":{"type":"string","description":"ISO date."},"ends":{"type":["string","null"],"description":"ISO date, or null while the window stays open."},"status":{"type":"string","enum":["projected","proposed","confirmed","actual","cancelled"]},"source":{"type":"string","enum":["projected","manual","deal","rights"],"description":"projected from a release date, manual, deal, or rights when drawn from the rights held (the acquired profile)."},"note":{"type":["string","null"]}},"additionalProperties":true}},"projection":{"type":"array","items":{"type":"object","description":"One window: a right in a territory, from a start to an end, with its standing.","properties":{"id":{"type":["string","null"]},"windowKind":{"type":"string","description":"theatrical, pvod, est, tvod, physical, airline, hotel, pay1, pay1_second, pay1_return, pay2, free_tv, library, avod, fast, or a tv_ kind for a series."},"rightCode":{"type":"string","description":"A code from GET /v1/reference/rights."},"territory":{"type":"string","description":"A country, a region or WORLD."},"starts":{"type":"string","description":"ISO date."},"ends":{"type":["string","null"],"description":"ISO date, or null while the window stays open."},"status":{"type":"string","enum":["projected","proposed","confirmed","actual","cancelled"]},"source":{"type":"string","enum":["projected","manual","deal","rights"],"description":"projected from a release date, manual, deal, or rights when drawn from the rights held (the acquired profile)."},"note":{"type":["string","null"]}},"additionalProperties":true}},"profile":{"type":"object","description":"Which profile projected the plan: the work’s own, else tv_series for a series, else the organisation’s setting rights.window_profile.","properties":{"effective":{"type":"string"},"work":{"type":["string","null"]},"org":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getReleaseWindows","x-permission":"org.view","x-permissions":["org.view"]},"put":{"summary":"Replace a work’s windows","tags":["Rights"],"description":"The whole plan at once: one row per window kind and territory. Rows matching an existing kind and territory are updated in place; the rest are removed. Needs org.settings.edit and an Idempotency-Key.\n\nPermission: org.settings.edit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["windows"],"additionalProperties":false,"properties":{"windows":{"type":"array","maxItems":200,"items":{"type":"object","required":["windowKind","rightCode","starts"],"additionalProperties":false,"properties":{"windowKind":{"type":"string","maxLength":40},"rightCode":{"type":"string","maxLength":60},"territory":{"type":"string","maxLength":20},"starts":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"ends":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"status":{"type":"string","enum":["projected","proposed","confirmed","actual","cancelled"]},"source":{"type":"string","enum":["projected","manual","deal","rights"]},"projectId":{"type":["string","null"],"format":"uuid"},"note":{"type":["string","null"],"maxLength":2000}}}}}}}}},"parameters":[{"schema":{"type":"string","maxLength":400},"in":"path","name":"identity","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"A work’s release plan: what is written down, and what the release date projects.","content":{"application/json":{"schema":{"type":"object","description":"A work’s release plan: what is written down, and what the release date projects.","properties":{"work":{"type":"object","properties":{"identity":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"},"titleId":{"type":["string","null"]}},"additionalProperties":true},"release":{"type":"object","description":"The release facts, or null until they are set.","properties":{"releaseDate":{"type":["string","null"],"description":"ISO date."},"releaseStatus":{"type":"string","enum":["released","pre_release","in_production","announced"]},"profile":{"type":["string","null"],"description":"The work’s own profile, or null for the organisation’s."},"note":{"type":["string","null"]}},"additionalProperties":true},"windows":{"type":"array","items":{"type":"object","description":"One window: a right in a territory, from a start to an end, with its standing.","properties":{"id":{"type":["string","null"]},"windowKind":{"type":"string","description":"theatrical, pvod, est, tvod, physical, airline, hotel, pay1, pay1_second, pay1_return, pay2, free_tv, library, avod, fast, or a tv_ kind for a series."},"rightCode":{"type":"string","description":"A code from GET /v1/reference/rights."},"territory":{"type":"string","description":"A country, a region or WORLD."},"starts":{"type":"string","description":"ISO date."},"ends":{"type":["string","null"],"description":"ISO date, or null while the window stays open."},"status":{"type":"string","enum":["projected","proposed","confirmed","actual","cancelled"]},"source":{"type":"string","enum":["projected","manual","deal","rights"],"description":"projected from a release date, manual, deal, or rights when drawn from the rights held (the acquired profile)."},"note":{"type":["string","null"]}},"additionalProperties":true}},"projection":{"type":"array","items":{"type":"object","description":"One window: a right in a territory, from a start to an end, with its standing.","properties":{"id":{"type":["string","null"]},"windowKind":{"type":"string","description":"theatrical, pvod, est, tvod, physical, airline, hotel, pay1, pay1_second, pay1_return, pay2, free_tv, library, avod, fast, or a tv_ kind for a series."},"rightCode":{"type":"string","description":"A code from GET /v1/reference/rights."},"territory":{"type":"string","description":"A country, a region or WORLD."},"starts":{"type":"string","description":"ISO date."},"ends":{"type":["string","null"],"description":"ISO date, or null while the window stays open."},"status":{"type":"string","enum":["projected","proposed","confirmed","actual","cancelled"]},"source":{"type":"string","enum":["projected","manual","deal","rights"],"description":"projected from a release date, manual, deal, or rights when drawn from the rights held (the acquired profile)."},"note":{"type":["string","null"]}},"additionalProperties":true}},"profile":{"type":"object","description":"Which profile projected the plan: the work’s own, else tv_series for a series, else the organisation’s setting rights.window_profile.","properties":{"effective":{"type":"string"},"work":{"type":["string","null"]},"org":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"replaceReleaseWindows","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/rights/works/{identity}/release":{"put":{"summary":"Set a work’s release date and status","tags":["Rights"],"description":"The date the plan is measured from, where the work stands, and the profile that projects it (omit for the organisation’s default). Needs org.settings.edit and an Idempotency-Key.\n\nPermission: org.settings.edit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["releaseStatus"],"additionalProperties":false,"properties":{"releaseDate":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"releaseStatus":{"type":"string","enum":["released","pre_release","in_production","announced"]},"profile":{"type":["string","null"],"maxLength":40},"note":{"type":["string","null"],"maxLength":2000}}}}}},"parameters":[{"schema":{"type":"string","maxLength":400},"in":"path","name":"identity","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"A work’s release plan: what is written down, and what the release date projects.","content":{"application/json":{"schema":{"type":"object","description":"A work’s release plan: what is written down, and what the release date projects.","properties":{"work":{"type":"object","properties":{"identity":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"},"titleId":{"type":["string","null"]}},"additionalProperties":true},"release":{"type":"object","description":"The release facts, or null until they are set.","properties":{"releaseDate":{"type":["string","null"],"description":"ISO date."},"releaseStatus":{"type":"string","enum":["released","pre_release","in_production","announced"]},"profile":{"type":["string","null"],"description":"The work’s own profile, or null for the organisation’s."},"note":{"type":["string","null"]}},"additionalProperties":true},"windows":{"type":"array","items":{"type":"object","description":"One window: a right in a territory, from a start to an end, with its standing.","properties":{"id":{"type":["string","null"]},"windowKind":{"type":"string","description":"theatrical, pvod, est, tvod, physical, airline, hotel, pay1, pay1_second, pay1_return, pay2, free_tv, library, avod, fast, or a tv_ kind for a series."},"rightCode":{"type":"string","description":"A code from GET /v1/reference/rights."},"territory":{"type":"string","description":"A country, a region or WORLD."},"starts":{"type":"string","description":"ISO date."},"ends":{"type":["string","null"],"description":"ISO date, or null while the window stays open."},"status":{"type":"string","enum":["projected","proposed","confirmed","actual","cancelled"]},"source":{"type":"string","enum":["projected","manual","deal","rights"],"description":"projected from a release date, manual, deal, or rights when drawn from the rights held (the acquired profile)."},"note":{"type":["string","null"]}},"additionalProperties":true}},"projection":{"type":"array","items":{"type":"object","description":"One window: a right in a territory, from a start to an end, with its standing.","properties":{"id":{"type":["string","null"]},"windowKind":{"type":"string","description":"theatrical, pvod, est, tvod, physical, airline, hotel, pay1, pay1_second, pay1_return, pay2, free_tv, library, avod, fast, or a tv_ kind for a series."},"rightCode":{"type":"string","description":"A code from GET /v1/reference/rights."},"territory":{"type":"string","description":"A country, a region or WORLD."},"starts":{"type":"string","description":"ISO date."},"ends":{"type":["string","null"],"description":"ISO date, or null while the window stays open."},"status":{"type":"string","enum":["projected","proposed","confirmed","actual","cancelled"]},"source":{"type":"string","enum":["projected","manual","deal","rights"],"description":"projected from a release date, manual, deal, or rights when drawn from the rights held (the acquired profile)."},"note":{"type":["string","null"]}},"additionalProperties":true}},"profile":{"type":"object","description":"Which profile projected the plan: the work’s own, else tv_series for a series, else the organisation’s setting rights.window_profile.","properties":{"effective":{"type":"string"},"work":{"type":["string","null"]},"org":{"type":"string"}},"additionalProperties":true}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"setReleaseDate","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/projects/{projectId}/deliverables/{deliverableId}/transfer-grants":{"post":{"summary":"Open a long download of a shared file","tags":["Deliverables"],"description":"On a deal handed to this organisation, a row the sending party shares (partner: true) holds its file in the sender’s storage. A grant lets the caller download it for hours, in parts, each on a fresh ten-minute link from GET /v1/transfer-grants/{grantId}/url. It lasts the hours asked for, cut to the sending party’s ceiling (24 by default), and ends at once if either party revokes it or the handoff ends. Both organisations’ audit trails record it. Needs asset.download. Idempotent on the Idempotency-Key.\n\nPermission: asset.download.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"object":{"type":"string","minLength":1,"maxLength":500,"description":"An object the row names. The row’s file when absent."},"hours":{"type":"integer","minimum":1,"maximum":168,"description":"How long the download may take. The ceiling when absent."}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"deliverableId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"grant":{"type":"object","description":"A transfer grant: one person’s access to a shared file for as long as the download takes, by fresh short links.","properties":{"id":{"type":"string","format":"uuid"},"side":{"type":"string","enum":["sender","receiver"],"description":"Which party this organisation is."},"projectId":{"type":"string","description":"This organisation’s own copy of the deal."},"deliverableId":{"type":"string","description":"The sending party’s row."},"objects":{"type":"array","items":{"type":"string"}},"purpose":{"type":"string","enum":["download","job"]},"issuedAt":{"type":"string"},"expiresAt":{"type":"string","description":"When it ends at the latest: the hours asked for, cut to the sending party’s ceiling."},"status":{"type":"string","enum":["live","revoked","finished","ended","expired"]},"endReason":{"type":["string","null"]},"endedAt":{"type":["string","null"]},"signs":{"type":"integer","description":"Links issued under it so far."}},"additionalProperties":true}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createTransferGrant","x-permission":"asset.download","x-permissions":["asset.download"],"x-idempotent":true}},"/v1/projects/{projectId}/transfer-grants":{"get":{"summary":"The transfer grants on a deal","tags":["Deliverables"],"description":"Newest first, from either side of the handoff: who downloaded what, how many links, and how each ended. Needs project.view.\n\nPermission: project.view.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"A transfer grant: one person’s access to a shared file for as long as the download takes, by fresh short links.","properties":{"id":{"type":"string","format":"uuid"},"side":{"type":"string","enum":["sender","receiver"],"description":"Which party this organisation is."},"projectId":{"type":"string","description":"This organisation’s own copy of the deal."},"deliverableId":{"type":"string","description":"The sending party’s row."},"objects":{"type":"array","items":{"type":"string"}},"purpose":{"type":"string","enum":["download","job"]},"issuedAt":{"type":"string"},"expiresAt":{"type":"string","description":"When it ends at the latest: the hours asked for, cut to the sending party’s ceiling."},"status":{"type":"string","enum":["live","revoked","finished","ended","expired"]},"endReason":{"type":["string","null"]},"endedAt":{"type":["string","null"]},"signs":{"type":"integer","description":"Links issued under it so far."}},"additionalProperties":true}},"next_cursor":{"type":["string","null"]}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listTransferGrants","x-permission":"project.view","x-permissions":["project.view"]}},"/v1/transfer-grants/{grantId}/url":{"get":{"summary":"A fresh link under a grant","tags":["Deliverables"],"description":"Ask again whenever the link held has expired or a part is refused. Each answer is checked against the grant and the handoff first: 410 access_ended once either has ended, after which stop asking. Only the caller who opened the grant may ask.\n\nPermission: asset.download.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":500},"in":"query","name":"object","required":false,"description":"One of the grant’s objects. The first when absent."},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"grantId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"A signed GET, good for expiresInSeconds. Send Range for a part."},"object":{"type":"string"},"expiresInSeconds":{"type":"integer","description":"Ten minutes, or less when the grant ends sooner."},"grantExpiresAt":{"type":"string"},"filename":{"type":["string","null"]}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"410":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getTransferGrantUrl","x-permission":"asset.download","x-permissions":["asset.download"]}},"/v1/transfer-grants/{grantId}/file":{"get":{"summary":"The file under a grant, as a redirect","tags":["Deliverables"],"description":"A 302 to a fresh link, so a downloader that follows redirects and resumes (curl -L -C -) keeps going for as long as the grant stands.\n\nPermission: asset.download.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":500},"in":"query","name":"object","required":false,"description":"One of the grant’s objects. The first when absent."},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"grantId","required":true}],"responses":{"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"410":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getTransferGrantFile","x-permission":"asset.download","x-permissions":["asset.download"]}},"/v1/transfer-grants/{grantId}":{"delete":{"summary":"End a grant","tags":["Deliverables"],"description":"Revoke it, from either side: the caller who opened it, or one holding project.handoff on its own side of the deal. With finished=1, the caller who opened it says the download is done. The next link is refused at once; a link already issued lasts at most ten minutes. Ending an ended grant changes nothing.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","enum":["1"]},"in":"query","name":"finished","required":false},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"grantId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"grant":{"type":"object","description":"A transfer grant: one person’s access to a shared file for as long as the download takes, by fresh short links.","properties":{"id":{"type":"string","format":"uuid"},"side":{"type":"string","enum":["sender","receiver"],"description":"Which party this organisation is."},"projectId":{"type":"string","description":"This organisation’s own copy of the deal."},"deliverableId":{"type":"string","description":"The sending party’s row."},"objects":{"type":"array","items":{"type":"string"}},"purpose":{"type":"string","enum":["download","job"]},"issuedAt":{"type":"string"},"expiresAt":{"type":"string","description":"When it ends at the latest: the hours asked for, cut to the sending party’s ceiling."},"status":{"type":"string","enum":["live","revoked","finished","ended","expired"]},"endReason":{"type":["string","null"]},"endedAt":{"type":["string","null"]},"signs":{"type":"integer","description":"Links issued under it so far."}},"additionalProperties":true}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"endTransferGrant","x-permission":"project.view","x-permissions":["project.view"],"x-idempotent":true}},"/v1/config":{"get":{"summary":"Export the organisation’s set-up","tags":["Configuration"],"description":"Delivery specs, house formats, the metadata schema, every taxonomy, webhook endpoints, the group tree, members, the agent-approvals setting with its trusted keys, and storage connections, as one document you can keep in a repository and apply to this or another organisation. Without ?sections=, every section the caller may read; a section asked for by name needs its own permission. Webhook secrets and storage credentials are never exported.\n\nPermission: org.view.","parameters":[{"schema":{"type":"string","maxLength":300},"in":"query","name":"sections","required":false,"description":"Comma-separated: deliverySpecs, formats, metadataSchema, taxonomies, webhooks, groups, members, agentApprovals, storageConnections."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"version":{"type":"integer","description":"The document format: 1."},"exportedAt":{"type":"string","description":"When it was exported. Read, never stored."},"deliverySpecs":{"type":"array","maxItems":500,"description":"Matched by name, case-insensitive. Never deleted from here.","items":{"type":"object","required":["name"],"additionalProperties":true,"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"destination":{"type":["string","null"],"maxLength":2000},"description":{"type":["string","null"],"maxLength":2000},"doc":{"type":"object","additionalProperties":true}}}},"formats":{"type":"object","additionalProperties":true,"description":"The house formats. Replaced whole when given."},"metadataSchema":{"type":"object","additionalProperties":true,"description":"The organisation’s metadata fields. Replaced whole when given."},"taxonomies":{"type":"object","maxProperties":20,"description":"Keyed by set. Terms matched by termKey; retire one with \"active\": false.","additionalProperties":{"type":"object","additionalProperties":true,"properties":{"terms":{"type":"array","maxItems":1000,"items":{"type":"object","required":["termKey"],"additionalProperties":true,"properties":{"termKey":{"type":"string","minLength":1,"maxLength":120},"ordering":{"type":"integer"},"active":{"type":"boolean"},"labels":{"type":"object","additionalProperties":{"type":"string","maxLength":300}},"attributes":{"type":"object","additionalProperties":true}}}}}}},"webhooks":{"type":"array","maxItems":50,"description":"Endpoints, matched by URL. Signing secrets are never exported; a new endpoint’s is in the apply answer once.","items":{"type":"object","required":["url","events"],"additionalProperties":false,"properties":{"url":{"type":"string","minLength":12,"maxLength":2000},"events":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","maxLength":60}},"description":{"type":"string","maxLength":500},"active":{"type":"boolean"}}}},"groups":{"type":"array","maxItems":1000,"description":"The tree beneath the top-level group, each group by its path of names. A path’s parents are implied.","items":{"type":"object","required":["path"],"additionalProperties":false,"properties":{"path":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","minLength":1,"maxLength":200}},"discoverable":{"type":"boolean"}}}},"members":{"type":"array","maxItems":2000,"description":"One membership each: a person by email, a role and a scope. A missing one is invited; nobody’s password or sign-in is touched.","items":{"type":"object","required":["email","role"],"additionalProperties":false,"properties":{"email":{"type":"string","format":"email","maxLength":320},"name":{"type":"string","maxLength":200,"description":"Used only when inviting someone new."},"role":{"type":"string","enum":["admin","user","guest"]},"scope":{"type":"object","required":["type"],"additionalProperties":false,"description":"org (the default), a group by path, or a project by id.","properties":{"type":{"type":"string","enum":["org","group","project"]},"path":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","minLength":1,"maxLength":200}},"projectId":{"type":"string","format":"uuid"}}}}}},"agentApprovals":{"type":"object","additionalProperties":false,"description":"Whether agent writes wait for a person, and the keys trusted to write without. Keys are named, never created here.","properties":{"on":{"type":"boolean"},"trustedKeys":{"type":"array","maxItems":200,"items":{"type":"string","minLength":1,"maxLength":200}}}},"storageConnections":{"type":"array","maxItems":100,"description":"Buckets of the organisation’s own, matched by name. A credential is only ever a reference to a connection that already holds one.","items":{"type":"object","required":["name","provider","bucket"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"provider":{"type":"string","enum":["gcs","s3","azure"]},"bucket":{"type":"string","minLength":3,"maxLength":255},"prefix":{"type":"string","maxLength":1024},"region":{"type":["string","null"],"maxLength":100},"endpoint":{"type":["string","null"],"maxLength":500},"access":{"type":"string","enum":["read","read_write"]},"enabled":{"type":"boolean"},"authKind":{"type":"string","maxLength":40,"description":"Exported for reading. Ignored on apply."},"credential":{"type":"object","required":["connectionId"],"additionalProperties":false,"description":"Use the credential this connection of the organisation already holds. Needed to create a connection.","properties":{"connectionId":{"type":"string","format":"uuid"}}}}}}}}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"exportConfig","x-permission":"org.view","x-permissions":["org.view"]},"put":{"summary":"Apply a set-up document","tags":["Configuration"],"description":"The plan, then each change through the product’s own operation and permission. Refused whole, before anything is written, when the plan finds a problem. Upserts by name and key; nothing is deleted unless prune names the section. Storage connections are disabled, not deleted; a document that would remove your own membership is refused. Each section needs its own permission: org.settings.edit for specs, formats, schema and taxonomies; integration.configure for webhooks, approvals and storage; the member and group permissions where each change lands. One audit row records every change. A new webhook endpoint’s signing secret is in this answer only. Needs an Idempotency-Key.\n\nPermission: org.settings.edit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"version":{"type":"integer","description":"The document format: 1."},"exportedAt":{"type":"string","description":"When it was exported. Read, never stored."},"deliverySpecs":{"type":"array","maxItems":500,"description":"Matched by name, case-insensitive. Never deleted from here.","items":{"type":"object","required":["name"],"additionalProperties":true,"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"destination":{"type":["string","null"],"maxLength":2000},"description":{"type":["string","null"],"maxLength":2000},"doc":{"type":"object","additionalProperties":true}}}},"formats":{"type":"object","additionalProperties":true,"description":"The house formats. Replaced whole when given."},"metadataSchema":{"type":"object","additionalProperties":true,"description":"The organisation’s metadata fields. Replaced whole when given."},"taxonomies":{"type":"object","maxProperties":20,"description":"Keyed by set. Terms matched by termKey; retire one with \"active\": false.","additionalProperties":{"type":"object","additionalProperties":true,"properties":{"terms":{"type":"array","maxItems":1000,"items":{"type":"object","required":["termKey"],"additionalProperties":true,"properties":{"termKey":{"type":"string","minLength":1,"maxLength":120},"ordering":{"type":"integer"},"active":{"type":"boolean"},"labels":{"type":"object","additionalProperties":{"type":"string","maxLength":300}},"attributes":{"type":"object","additionalProperties":true}}}}}}},"webhooks":{"type":"array","maxItems":50,"description":"Endpoints, matched by URL. Signing secrets are never exported; a new endpoint’s is in the apply answer once.","items":{"type":"object","required":["url","events"],"additionalProperties":false,"properties":{"url":{"type":"string","minLength":12,"maxLength":2000},"events":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","maxLength":60}},"description":{"type":"string","maxLength":500},"active":{"type":"boolean"}}}},"groups":{"type":"array","maxItems":1000,"description":"The tree beneath the top-level group, each group by its path of names. A path’s parents are implied.","items":{"type":"object","required":["path"],"additionalProperties":false,"properties":{"path":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","minLength":1,"maxLength":200}},"discoverable":{"type":"boolean"}}}},"members":{"type":"array","maxItems":2000,"description":"One membership each: a person by email, a role and a scope. A missing one is invited; nobody’s password or sign-in is touched.","items":{"type":"object","required":["email","role"],"additionalProperties":false,"properties":{"email":{"type":"string","format":"email","maxLength":320},"name":{"type":"string","maxLength":200,"description":"Used only when inviting someone new."},"role":{"type":"string","enum":["admin","user","guest"]},"scope":{"type":"object","required":["type"],"additionalProperties":false,"description":"org (the default), a group by path, or a project by id.","properties":{"type":{"type":"string","enum":["org","group","project"]},"path":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","minLength":1,"maxLength":200}},"projectId":{"type":"string","format":"uuid"}}}}}},"agentApprovals":{"type":"object","additionalProperties":false,"description":"Whether agent writes wait for a person, and the keys trusted to write without. Keys are named, never created here.","properties":{"on":{"type":"boolean"},"trustedKeys":{"type":"array","maxItems":200,"items":{"type":"string","minLength":1,"maxLength":200}}}},"storageConnections":{"type":"array","maxItems":100,"description":"Buckets of the organisation’s own, matched by name. A credential is only ever a reference to a connection that already holds one.","items":{"type":"object","required":["name","provider","bucket"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"provider":{"type":"string","enum":["gcs","s3","azure"]},"bucket":{"type":"string","minLength":3,"maxLength":255},"prefix":{"type":"string","maxLength":1024},"region":{"type":["string","null"],"maxLength":100},"endpoint":{"type":["string","null"],"maxLength":500},"access":{"type":"string","enum":["read","read_write"]},"enabled":{"type":"boolean"},"authKind":{"type":"string","maxLength":40,"description":"Exported for reading. Ignored on apply."},"credential":{"type":"object","required":["connectionId"],"additionalProperties":false,"description":"Use the credential this connection of the organisation already holds. Needed to create a connection.","properties":{"connectionId":{"type":"string","format":"uuid"}}}}}},"prune":{"anyOf":[{"type":"array","maxItems":5,"items":{"type":"string","enum":["webhooks","groups","members","trustedKeys","storageConnections"]}},{"type":"boolean"}],"description":"The sections an apply may delete from: anything there that the document lacks goes. Absent or false deletes nothing. Each named section must be in the document."}}}}}},"parameters":[{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"applied":{"type":"boolean"},"changes":{"type":"array","items":{"type":"object","description":"One change.","properties":{"section":{"type":"string"},"action":{"type":"string","enum":["create","update","delete"]},"key":{"type":"string","description":"The item as the document names it: a name, a URL, a path, \"email @ scope\"."},"fields":{"type":"array","items":{"type":"string"}},"before":{},"after":{},"effect":{"type":["string","null"],"description":"Set when a delete is less than one: a storage connection is disabled and kept."}},"additionalProperties":true}},"summary":{"type":"object","additionalProperties":{"type":"object","properties":{"create":{"type":"integer"},"update":{"type":"integer"},"delete":{"type":"integer"}},"additionalProperties":true},"description":"Counts per section."},"prune":{"type":"array","items":{"type":"string"}},"webhookSecrets":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"id":{"type":"string"},"secret":{"type":["string","null"],"description":"whsec_<base64>. Shown once; null on a replay or an approved run, where POST /v1/webhooks/{id}/rotate-secret issues one."}},"additionalProperties":true}},"deliverySpecs":{"type":"object","properties":{"created":{"type":"integer"},"updated":{"type":"integer"}},"additionalProperties":true},"formats":{"type":"boolean"},"metadataSchema":{"type":"boolean"},"taxonomies":{"type":"object","properties":{"added":{"type":"integer"},"updated":{"type":"integer"}},"additionalProperties":true}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"applyConfig","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/config/schema":{"get":{"summary":"The set-up document as JSON Schema","tags":["Configuration"],"description":"Point an editor or a CI check at it. The same schema the plan and apply routes validate against.\n\nPermission: none beyond a valid credential of the organisation.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getConfigSchema","x-permission":"none","x-permissions":[]}},"/v1/config/plan":{"post":{"summary":"Plan a set-up document","tags":["Configuration"],"description":"Every change applying the document would make, item by item (create, update or delete, with the fields that change), and every problem that would stop it, writing nothing. Checked under the same permissions as applying, so a plan that answers is an apply that would be allowed. Deletions appear only for the sections named in prune.\n\nPermission: org.settings.edit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"version":{"type":"integer","description":"The document format: 1."},"exportedAt":{"type":"string","description":"When it was exported. Read, never stored."},"deliverySpecs":{"type":"array","maxItems":500,"description":"Matched by name, case-insensitive. Never deleted from here.","items":{"type":"object","required":["name"],"additionalProperties":true,"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"destination":{"type":["string","null"],"maxLength":2000},"description":{"type":["string","null"],"maxLength":2000},"doc":{"type":"object","additionalProperties":true}}}},"formats":{"type":"object","additionalProperties":true,"description":"The house formats. Replaced whole when given."},"metadataSchema":{"type":"object","additionalProperties":true,"description":"The organisation’s metadata fields. Replaced whole when given."},"taxonomies":{"type":"object","maxProperties":20,"description":"Keyed by set. Terms matched by termKey; retire one with \"active\": false.","additionalProperties":{"type":"object","additionalProperties":true,"properties":{"terms":{"type":"array","maxItems":1000,"items":{"type":"object","required":["termKey"],"additionalProperties":true,"properties":{"termKey":{"type":"string","minLength":1,"maxLength":120},"ordering":{"type":"integer"},"active":{"type":"boolean"},"labels":{"type":"object","additionalProperties":{"type":"string","maxLength":300}},"attributes":{"type":"object","additionalProperties":true}}}}}}},"webhooks":{"type":"array","maxItems":50,"description":"Endpoints, matched by URL. Signing secrets are never exported; a new endpoint’s is in the apply answer once.","items":{"type":"object","required":["url","events"],"additionalProperties":false,"properties":{"url":{"type":"string","minLength":12,"maxLength":2000},"events":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","maxLength":60}},"description":{"type":"string","maxLength":500},"active":{"type":"boolean"}}}},"groups":{"type":"array","maxItems":1000,"description":"The tree beneath the top-level group, each group by its path of names. A path’s parents are implied.","items":{"type":"object","required":["path"],"additionalProperties":false,"properties":{"path":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","minLength":1,"maxLength":200}},"discoverable":{"type":"boolean"}}}},"members":{"type":"array","maxItems":2000,"description":"One membership each: a person by email, a role and a scope. A missing one is invited; nobody’s password or sign-in is touched.","items":{"type":"object","required":["email","role"],"additionalProperties":false,"properties":{"email":{"type":"string","format":"email","maxLength":320},"name":{"type":"string","maxLength":200,"description":"Used only when inviting someone new."},"role":{"type":"string","enum":["admin","user","guest"]},"scope":{"type":"object","required":["type"],"additionalProperties":false,"description":"org (the default), a group by path, or a project by id.","properties":{"type":{"type":"string","enum":["org","group","project"]},"path":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"string","minLength":1,"maxLength":200}},"projectId":{"type":"string","format":"uuid"}}}}}},"agentApprovals":{"type":"object","additionalProperties":false,"description":"Whether agent writes wait for a person, and the keys trusted to write without. Keys are named, never created here.","properties":{"on":{"type":"boolean"},"trustedKeys":{"type":"array","maxItems":200,"items":{"type":"string","minLength":1,"maxLength":200}}}},"storageConnections":{"type":"array","maxItems":100,"description":"Buckets of the organisation’s own, matched by name. A credential is only ever a reference to a connection that already holds one.","items":{"type":"object","required":["name","provider","bucket"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"provider":{"type":"string","enum":["gcs","s3","azure"]},"bucket":{"type":"string","minLength":3,"maxLength":255},"prefix":{"type":"string","maxLength":1024},"region":{"type":["string","null"],"maxLength":100},"endpoint":{"type":["string","null"],"maxLength":500},"access":{"type":"string","enum":["read","read_write"]},"enabled":{"type":"boolean"},"authKind":{"type":"string","maxLength":40,"description":"Exported for reading. Ignored on apply."},"credential":{"type":"object","required":["connectionId"],"additionalProperties":false,"description":"Use the credential this connection of the organisation already holds. Needed to create a connection.","properties":{"connectionId":{"type":"string","format":"uuid"}}}}}},"prune":{"anyOf":[{"type":"array","maxItems":5,"items":{"type":"string","enum":["webhooks","groups","members","trustedKeys","storageConnections"]}},{"type":"boolean"}],"description":"The sections an apply may delete from: anything there that the document lacks goes. Absent or false deletes nothing. Each named section must be in the document."}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"changes":{"type":"array","items":{"type":"object","description":"One change.","properties":{"section":{"type":"string"},"action":{"type":"string","enum":["create","update","delete"]},"key":{"type":"string","description":"The item as the document names it: a name, a URL, a path, \"email @ scope\"."},"fields":{"type":"array","items":{"type":"string"}},"before":{},"after":{},"effect":{"type":["string","null"],"description":"Set when a delete is less than one: a storage connection is disabled and kept."}},"additionalProperties":true}},"summary":{"type":"object","additionalProperties":{"type":"object","properties":{"create":{"type":"integer"},"update":{"type":"integer"},"delete":{"type":"integer"}},"additionalProperties":true},"description":"Counts per section."},"problems":{"type":"array","items":{"type":"object","description":"Something in the document that stops it applying.","properties":{"section":{"type":"string"},"key":{"type":"string"},"message":{"type":"string"}},"additionalProperties":true}},"prune":{"type":"array","items":{"type":"string"}},"wouldApply":{"type":"boolean"}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"planConfig","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"],"x-idempotent":true}},"/v1/keys":{"get":{"summary":"List API keys","tags":["Keys"],"description":"Every key of the organisation, newest first, with integration.configure; without it, a person’s own personal keys. Values are never listed.\n\nPermission: integration.configure.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"An API key. Its value is never listed.","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["org","user"],"description":"org: acts for the organisation. user: a personal key, acting as its owner."},"name":{"type":"string"},"permissions":{"type":"array","items":{"type":"string"}},"owner":{"type":"object","description":"A personal key’s owner; null for an organisation key.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"obfuscated":{"type":"string","description":"The first and last characters, for telling keys apart. Never the value."},"appId":{"type":["string","null"],"description":"The connected app this key belongs to, if any."},"createdBy":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"createdAt":{"type":"string"},"lastUsedAt":{"type":["string","null"]},"revokedAt":{"type":["string","null"]},"expiresAt":{"type":["string","null"]},"writesTrusted":{"type":"boolean","description":"Writes without approval while the organisation holds agent writes for approval."}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true},"example":{"data":[{"id":"f1e2d3c4-b5a6-4978-8a6b-5c4d3e2f1a0b","kind":"org","name":"Delivery robot","permissions":["project.view","asset.upload"],"obfuscated":"ff_live_…9xQ2","appId":null,"createdAt":"2027-01-15T10:30:00.000Z","lastUsedAt":null,"revokedAt":null,"expiresAt":null,"writesTrusted":false}],"next_cursor":null}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listKeys","x-permission":"integration.configure","x-permissions":["integration.configure"]},"post":{"summary":"Mint an API key","tags":["Keys"],"description":"An organisation key with the permissions given, each of which the caller must hold itself. The value is in this answer and in no other: store it. A replay with the same Idempotency-Key returns the key with value null. Needs integration.configure. While the organisation holds agent writes for approval, a key whose writes are held is refused here: a person mints it.\n\nPermission: integration.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","permissions"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":120},"kind":{"type":"string","enum":["org","user"],"default":"org","description":"user: a personal key, minted by a person signed in."},"permissions":{"type":"array","minItems":1,"maxItems":60,"items":{"type":"string","enum":["member.view","member.invite","member.remove","member.role.assign","org.view","org.settings.edit","group.view","group.create","group.rename","group.delete","project.view","project.create","project.configure","project.archive","project.delete","project.handoff","project.end","asset.view","asset.upload","asset.download","job.view","job.create","qc.view","qc.execute","workorder.view","workorder.create","inbox.view","inbox.manage","integration.configure","audit.view"]}}}},"example":{"name":"Delivery robot","kind":"org","permissions":["project.view","asset.upload"]}}}},"parameters":[{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"object","description":"An API key. Its value is never listed.","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["org","user"],"description":"org: acts for the organisation. user: a personal key, acting as its owner."},"name":{"type":"string"},"permissions":{"type":"array","items":{"type":"string"}},"owner":{"type":"object","description":"A personal key’s owner; null for an organisation key.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"obfuscated":{"type":"string","description":"The first and last characters, for telling keys apart. Never the value."},"appId":{"type":["string","null"],"description":"The connected app this key belongs to, if any."},"createdBy":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"createdAt":{"type":"string"},"lastUsedAt":{"type":["string","null"]},"revokedAt":{"type":["string","null"]},"expiresAt":{"type":["string","null"]},"writesTrusted":{"type":"boolean","description":"Writes without approval while the organisation holds agent writes for approval."}},"additionalProperties":true},"value":{"type":["string","null"],"description":"The key itself. Shown once; null on a replay."}},"additionalProperties":true},"example":{"key":{"id":"f1e2d3c4-b5a6-4978-8a6b-5c4d3e2f1a0b","kind":"org","name":"Delivery robot","permissions":["project.view","asset.upload"],"obfuscated":"ff_live_…9xQ2","appId":null,"createdAt":"2027-01-15T10:30:00.000Z","lastUsedAt":null,"revokedAt":null,"expiresAt":null,"writesTrusted":false},"value":"ff_live_<shown once, store it>"}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createKey","x-permission":"integration.configure","x-permissions":["integration.configure"],"x-idempotent":true}},"/v1/keys/{id}":{"delete":{"summary":"Revoke an API key","tags":["Keys"],"description":"The key stops working at once, everywhere. Its record and its actions stay. Needs integration.configure, or the key is your own personal key.\n\nPermission: integration.configure.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"revoked":{"type":"boolean"}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"revokeKey","x-permission":"integration.configure","x-permissions":["integration.configure"],"x-idempotent":true}},"/v1/keys/{id}/trusted":{"put":{"summary":"Trust a key, or stop trusting it","tags":["Keys","Approvals"],"description":"A trusted key writes without approval while the organisation holds agent writes for approval. Any credential with integration.configure may take trust away; giving it takes a person, or a person’s approval of this request. No key changes its own trust.\n\nPermission: integration.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["trusted"],"additionalProperties":false,"properties":{"trusted":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"writesTrusted":{"type":"boolean"}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"setKeyTrusted","x-permission":"integration.configure","x-permissions":["integration.configure"],"x-idempotent":true}},"/v1/roles":{"get":{"summary":"Roles and what they carry","tags":["Members"],"description":"The roles a membership can hold, each with its label and its permissions, and every permission there is.\n\nPermission: none beyond a valid credential of the organisation.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"roles":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","enum":["admin","user","guest"],"description":"admin (Admin), user (Editor) or guest (Viewer). GET /v1/roles lists what each carries."},"label":{"type":"string"},"hint":{"type":"string"},"permissions":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}},"permissions":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listRoles","x-permission":"none","x-permissions":[]}},"/v1/members":{"get":{"summary":"List members","tags":["Members"],"description":"Every membership in the organisation: who, at which scope, in which role. Needs member.view.\n\nPermission: member.view.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"One membership: a person, a scope and a role. The only thing that grants access.","properties":{"id":{"type":"string","format":"uuid"},"userId":{"type":"string","format":"uuid"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"role":{"type":"string","enum":["admin","user","guest"],"description":"admin (Admin), user (Editor) or guest (Viewer). GET /v1/roles lists what each carries."},"roleLabel":{"type":["string","null"]},"scope":{"type":"object","properties":{"type":{"type":"string","enum":["org","group","project"]},"id":{"type":["string","null"],"description":"The group or project; null at the organisation."}},"additionalProperties":true},"createdAt":{"type":["string","null"]},"lastSignInAt":{"type":["string","null"]}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listMembers","x-permission":"member.view","x-permissions":["member.view"]}},"/v1/members/{id}":{"patch":{"summary":"Change a member’s role or scope","tags":["Members"],"description":"A new role, a new scope, or both. Needs member.role.assign where the membership sits and, when it moves, where it is going. The role may carry nothing the caller does not hold there.\n\nPermission: member.role.assign.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"role":{"type":"string","enum":["admin","user","guest"],"description":"admin (Admin), user (Editor) or guest (Viewer). GET /v1/roles lists what each carries."},"scopeType":{"type":"string","enum":["org","group","project"]},"scopeId":{"type":["string","null"],"format":"uuid"}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"One membership: a person, a scope and a role. The only thing that grants access.","content":{"application/json":{"schema":{"type":"object","description":"One membership: a person, a scope and a role. The only thing that grants access.","properties":{"id":{"type":"string","format":"uuid"},"userId":{"type":"string","format":"uuid"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"role":{"type":"string","enum":["admin","user","guest"],"description":"admin (Admin), user (Editor) or guest (Viewer). GET /v1/roles lists what each carries."},"roleLabel":{"type":["string","null"]},"scope":{"type":"object","properties":{"type":{"type":"string","enum":["org","group","project"]},"id":{"type":["string","null"],"description":"The group or project; null at the organisation."}},"additionalProperties":true},"createdAt":{"type":["string","null"]},"lastSignInAt":{"type":["string","null"]}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"updateMember","x-permission":"member.role.assign","x-permissions":["member.role.assign"],"x-idempotent":true},"delete":{"summary":"Remove a member","tags":["Members"],"description":"Ends one membership. The person’s account, their other memberships and their history stay. Needs member.remove where the membership sits.\n\nPermission: member.remove.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"removed":{"type":"boolean"}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"removeMember","x-permission":"member.remove","x-permissions":["member.remove"],"x-idempotent":true}},"/v1/invitations":{"get":{"summary":"List open invitations","tags":["Members"],"description":"Links sent and not yet used, cancelled or expired. Needs member.view.\n\nPermission: member.view.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"An invitation link not yet used.","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string"},"scopeType":{"type":"string","enum":["org","group","project"]},"scopeName":{"type":["string","null"]},"invitedByName":{"type":["string","null"]},"createdAt":{"type":["string","null"]},"expiresAt":{"type":["string","null"]}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listInvitations","x-permission":"member.view","x-permissions":["member.view"]},"post":{"summary":"Invite someone","tags":["Members"],"description":"Adds the person at the scope in the role, then emails them a link to set a first password. Someone who already has a password is added and sent no link. Needs member.invite at the scope; the role may carry nothing the caller does not hold there.\n\nPermission: member.invite.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","role","scopeType"],"additionalProperties":false,"properties":{"email":{"type":"string","format":"email","maxLength":320},"name":{"type":"string","maxLength":200},"role":{"type":"string","enum":["admin","user","guest"],"description":"admin (Admin), user (Editor) or guest (Viewer). GET /v1/roles lists what each carries."},"scopeType":{"type":"string","enum":["org","group","project"]},"scopeId":{"type":"string","format":"uuid"}}}}}},"parameters":[{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"member":{"type":"object","description":"One membership: a person, a scope and a role. The only thing that grants access.","properties":{"id":{"type":"string","format":"uuid"},"userId":{"type":"string","format":"uuid"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"role":{"type":"string","enum":["admin","user","guest"],"description":"admin (Admin), user (Editor) or guest (Viewer). GET /v1/roles lists what each carries."},"roleLabel":{"type":["string","null"]},"scope":{"type":"object","properties":{"type":{"type":"string","enum":["org","group","project"]},"id":{"type":["string","null"],"description":"The group or project; null at the organisation."}},"additionalProperties":true},"createdAt":{"type":["string","null"]},"lastSignInAt":{"type":["string","null"]}},"additionalProperties":true},"invitation":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Null when no link was minted."},"minted":{"type":"boolean"},"sent":{"type":"boolean"},"reason":{"type":["string","null"]}},"additionalProperties":true}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createInvitation","x-permission":"member.invite","x-permissions":["member.invite"],"x-idempotent":true}},"/v1/invitations/{id}":{"delete":{"summary":"Cancel an invitation","tags":["Members"],"description":"The link stops working. The membership it came with stays: remove that with DELETE /v1/members/{id}. Needs member.invite.\n\nPermission: member.invite.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"revoked":{"type":"boolean"}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"cancelInvitation","x-permission":"member.invite","x-permissions":["member.invite"],"x-idempotent":true}},"/v1/groups":{"get":{"summary":"List groups","tags":["Groups"],"description":"The organisation’s tree, every node. Needs group.view.\n\nPermission: group.view.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"A node in the organisation’s tree.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"parentGroupId":{"type":["string","null"]},"path":{"type":"string"},"isRoot":{"type":"boolean"},"discoverable":{"type":["boolean","null"]},"partnerCode":{"type":["string","null"]}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listGroups","x-permission":"group.view","x-permissions":["group.view"]},"post":{"summary":"Create a group","tags":["Groups"],"description":"A new group beneath a parent. Needs group.create on the parent.\n\nPermission: group.create.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["parentGroupId","name"],"additionalProperties":false,"properties":{"parentGroupId":{"type":"string","format":"uuid"},"name":{"type":"string","minLength":1,"maxLength":200}}}}}},"parameters":[{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"A node in the organisation’s tree.","content":{"application/json":{"schema":{"type":"object","description":"A node in the organisation’s tree.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"parentGroupId":{"type":["string","null"]},"path":{"type":"string"},"isRoot":{"type":"boolean"},"discoverable":{"type":["boolean","null"]},"partnerCode":{"type":["string","null"]}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createGroup","x-permission":"group.create","x-permissions":["group.create"],"x-idempotent":true}},"/v1/groups/{id}":{"patch":{"summary":"Rename a group, or change whether partners can find it","tags":["Groups"],"description":"Needs group.rename on the group.\n\nPermission: group.rename.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"discoverable":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"A node in the organisation’s tree.","content":{"application/json":{"schema":{"type":"object","description":"A node in the organisation’s tree.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"parentGroupId":{"type":["string","null"]},"path":{"type":"string"},"isRoot":{"type":"boolean"},"discoverable":{"type":["boolean","null"]},"partnerCode":{"type":["string","null"]}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"updateGroup","x-permission":"group.rename","x-permissions":["group.rename"],"x-idempotent":true},"delete":{"summary":"Delete a group","tags":["Groups"],"description":"The group and the groups beneath it, with their memberships. Refused for the top-level group, and for a group that still holds projects. Needs group.delete on the group.\n\nPermission: group.delete.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"},"removedGroups":{"type":"integer"}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"deleteGroup","x-permission":"group.delete","x-permissions":["group.delete"],"x-idempotent":true}},"/v1/audit":{"get":{"summary":"The organisation’s action log","tags":["Audit"],"description":"Every recorded action across the organisation, newest first. Filter by when, who, which key and which action (\"api_key.\" for every key action). Needs audit.view.\n\nPermission: audit.view.","parameters":[{"schema":{"type":"string","maxLength":40},"in":"query","name":"since","required":false,"description":"ISO date or date-time: entries at or after it."},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"actor","required":false,"description":"A person’s id: what they did, and what keys did as them."},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"apiKeyId","required":false},{"schema":{"type":"string","maxLength":120},"in":"query","name":"action","required":false,"description":"An exact action, or a prefix ending in a dot."},{"schema":{"type":"string","maxLength":300},"in":"query","name":"cursor","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"action":{"type":"string"},"subject":{"type":"string"},"actorId":{"type":["string","null"]},"actorName":{"type":["string","null"]},"actorEmail":{"type":["string","null"]},"apiKeyId":{"type":["string","null"],"description":"The key that made the change, when one did."},"projectId":{"type":["string","null"]},"reason":{"type":["string","null"]},"before":{},"after":{},"createdAt":{"type":"string"}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listAudit","x-permission":"audit.view","x-permissions":["audit.view"]}},"/v1/settings/agent-approvals":{"get":{"summary":"Whether agent writes need approval","tags":["Approvals"],"description":"on: a write by an untrusted key or token waits for a person. pending: how many wait now. Needs integration.configure.\n\nPermission: integration.configure.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"on":{"type":"boolean"},"pending":{"type":"integer"}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getAgentApprovals","x-permission":"integration.configure","x-permissions":["integration.configure"]},"put":{"summary":"Switch approvals on agent writes on or off","tags":["Approvals"],"description":"Needs integration.configure. While approvals are on, this write from an untrusted credential waits for a person too.\n\nPermission: integration.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["on"],"additionalProperties":false,"properties":{"on":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"on":{"type":"boolean"}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"setAgentApprovals","x-permission":"integration.configure","x-permissions":["integration.configure"],"x-idempotent":true}},"/v1/proposals":{"get":{"summary":"List proposals","tags":["Approvals"],"description":"Writes held for approval, newest first: what was asked, by which key or app, and how it ended. Filter by status (decided: every status but pending) or key. Needs integration.configure.\n\nPermission: integration.configure.","parameters":[{"schema":{"type":"string","enum":["pending","running","approved","failed","declined","expired","decided"]},"in":"query","name":"status","required":false},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"apiKeyId","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"A write held for a person to approve.","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","running","approved","failed","declined","expired"]},"channel":{"type":"string","enum":["v1","mcp"]},"tool":{"type":"string","description":"The MCP tool, or METHOD /v1/route."},"inputs":{"type":"object","properties":{},"additionalProperties":true},"touches":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"}},"additionalProperties":true}},"project":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"proposedBy":{"type":"object","properties":{"principal":{"type":"string"},"kind":{"type":"string"},"apiKeyId":{"type":["string","null"]},"keyName":{"type":["string","null"]},"appId":{"type":["string","null"]},"appName":{"type":["string","null"]},"actorName":{"type":["string","null"]}},"additionalProperties":true},"createdAt":{"type":"string"},"expiresAt":{"type":"string"},"decidedAt":{"type":["string","null"]},"decidedBy":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"note":{"type":["string","null"]},"resultStatus":{"type":["integer","null"]},"failure":{"type":["string","null"],"description":"Why the write was refused when it ran."}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listProposals","x-permission":"integration.configure","x-permissions":["integration.configure"]}},"/v1/proposals/{id}/approve":{"post":{"summary":"Approve a proposal","tags":["Approvals"],"description":"Runs the held write once, under the credential that proposed it, as it stands now; the answer says whether it was written. Needs integration.configure. A credential never decides its own proposal.\n\nPermission: integration.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":["object","null"],"additionalProperties":false,"properties":{"note":{"type":["string","null"],"maxLength":1000}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"A write held for a person to approve.","content":{"application/json":{"schema":{"type":"object","description":"A write held for a person to approve.","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","running","approved","failed","declined","expired"]},"channel":{"type":"string","enum":["v1","mcp"]},"tool":{"type":"string","description":"The MCP tool, or METHOD /v1/route."},"inputs":{"type":"object","properties":{},"additionalProperties":true},"touches":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"}},"additionalProperties":true}},"project":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"proposedBy":{"type":"object","properties":{"principal":{"type":"string"},"kind":{"type":"string"},"apiKeyId":{"type":["string","null"]},"keyName":{"type":["string","null"]},"appId":{"type":["string","null"]},"appName":{"type":["string","null"]},"actorName":{"type":["string","null"]}},"additionalProperties":true},"createdAt":{"type":"string"},"expiresAt":{"type":"string"},"decidedAt":{"type":["string","null"]},"decidedBy":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"note":{"type":["string","null"]},"resultStatus":{"type":["integer","null"]},"failure":{"type":["string","null"],"description":"Why the write was refused when it ran."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"approveProposal","x-permission":"integration.configure","x-permissions":["integration.configure"],"x-idempotent":true}},"/v1/proposals/{id}/decline":{"post":{"summary":"Decline a proposal","tags":["Approvals"],"description":"Nothing is written; the proposing credential is told so. Needs integration.configure. A credential never decides its own proposal.\n\nPermission: integration.configure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":["object","null"],"additionalProperties":false,"properties":{"note":{"type":["string","null"],"maxLength":1000}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"A write held for a person to approve.","content":{"application/json":{"schema":{"type":"object","description":"A write held for a person to approve.","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","running","approved","failed","declined","expired"]},"channel":{"type":"string","enum":["v1","mcp"]},"tool":{"type":"string","description":"The MCP tool, or METHOD /v1/route."},"inputs":{"type":"object","properties":{},"additionalProperties":true},"touches":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"}},"additionalProperties":true}},"project":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"proposedBy":{"type":"object","properties":{"principal":{"type":"string"},"kind":{"type":"string"},"apiKeyId":{"type":["string","null"]},"keyName":{"type":["string","null"]},"appId":{"type":["string","null"]},"appName":{"type":["string","null"]},"actorName":{"type":["string","null"]}},"additionalProperties":true},"createdAt":{"type":"string"},"expiresAt":{"type":"string"},"decidedAt":{"type":["string","null"]},"decidedBy":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"note":{"type":["string","null"]},"resultStatus":{"type":["integer","null"]},"failure":{"type":["string","null"],"description":"Why the write was refused when it ran."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"declineProposal","x-permission":"integration.configure","x-permissions":["integration.configure"],"x-idempotent":true}},"/v1/cloud/connections":{"get":{"summary":"List connected buckets","tags":["Cloud"],"description":"Every connection this credential may browse: all of them with integration.configure, otherwise those granted to a project it can see. Never a credential.\n\nPermission: project.view.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"A bucket of the organisation’s own, connected for browsing and importing.","properties":{"id":{"type":"string","format":"uuid"},"provider":{"type":"string","enum":["gcs","s3","azure"]},"name":{"type":"string"},"bucket":{"type":"string"},"prefix":{"type":"string"},"region":{"type":["string","null"]},"endpoint":{"type":["string","null"]},"authKind":{"type":"string","description":"The shape of the stored credential. Never the credential."},"platformIdentity":{"type":"boolean","description":"True when the bucket is read under Final Frame’s own identity."},"access":{"type":"string","enum":["read","read_write"]},"status":{"type":"string","enum":["untested","ok","failed"]},"lastTestedAt":{"type":["string","null"]},"lastError":{"type":["string","null"]},"projectIds":{"type":"array","items":{"type":"string","format":"uuid"}},"createdAt":{"type":"string"},"disabledAt":{"type":["string","null"]}},"additionalProperties":true}}},"additionalProperties":true}}}},"401":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listCloudConnections","x-permission":"project.view","x-permissions":["project.view"]}},"/v1/cloud/connections/{connectionId}/objects":{"get":{"summary":"Browse a connected bucket","tags":["Cloud"],"description":"Objects and folders under a prefix, a page at a time. `prefix` is a folder from a previous page, or a path under the connection’s own prefix. With `q` the listing is flat and filtered by name.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","maxLength":1024},"in":"query","name":"prefix","required":false},{"schema":{"type":"string","maxLength":2000},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"q","required":false},{"schema":{"type":"integer","minimum":1,"maximum":1000},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"connectionId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"objects":{"type":"array","items":{"type":"object","description":"One object as the store lists it.","properties":{"key":{"type":"string"},"name":{"type":"string"},"size":{"type":["number","null"]},"contentType":{"type":["string","null"]},"updatedAt":{"type":["string","null"]}},"additionalProperties":true}},"folders":{"type":"array","items":{"type":"string"}},"nextCursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."},"prefix":{"type":"string"}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listCloudObjects","x-permission":"project.view","x-permissions":["project.view"]}},"/v1/cloud/imports":{"post":{"summary":"Bring an object in","tags":["Cloud"],"description":"Copy one object from a connected bucket into a project: onto a placeholder with deliverableId, or into the project’s imports without. Needs asset.upload on the project and the connection granted to it. The copy runs in the background: the answer’s statusUrl, also the Location header, is its run (GET /v1/processes/{id}); GET /v1/cloud/imports lists the imports.\n\nPermission: asset.upload.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["connectionId","key","projectId"],"additionalProperties":false,"properties":{"connectionId":{"type":"string","format":"uuid"},"key":{"type":"string","minLength":1,"maxLength":1024},"projectId":{"type":"string","format":"uuid"},"deliverableId":{"type":["string","null"],"format":"uuid"},"senderAccess":{"type":"string","enum":["open","withheld"]}}}}}},"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"import":{"type":"object","description":"One copy of one object from a connected bucket into the organisation’s own.","properties":{"id":{"type":"string","format":"uuid"},"connectionId":{"type":"string","format":"uuid"},"projectId":{"type":"string","format":"uuid"},"deliverableId":{"type":["string","null"],"description":"The placeholder the file is filed on, or null when it lands in the project’s imports."},"sourceKey":{"type":"string"},"filename":{"type":"string"},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"object":{"type":["string","null"],"description":"Where it landed in the organisation’s own storage, once copied."},"status":{"type":"string","enum":["queued","copying","filed","failed"]},"copiedBytes":{"type":"integer"},"error":{"type":["string","null"]},"startedAt":{"type":["string","null"]},"finishedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"additionalProperties":true},"processId":{"type":["string","null"],"description":"The copy’s run on the processing log."},"statusUrl":{"type":["string","null"],"description":"GET it to watch the copy. Also the Location header."}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createCloudImport","x-permission":"asset.upload","x-permissions":["asset.upload"],"x-idempotent":true},"get":{"summary":"List imports into a project","tags":["Cloud"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"query","name":"projectId","required":true},{"schema":{"type":"string","enum":["queued","copying","filed","failed"]},"in":"query","name":"status","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"One copy of one object from a connected bucket into the organisation’s own.","properties":{"id":{"type":"string","format":"uuid"},"connectionId":{"type":"string","format":"uuid"},"projectId":{"type":"string","format":"uuid"},"deliverableId":{"type":["string","null"],"description":"The placeholder the file is filed on, or null when it lands in the project’s imports."},"sourceKey":{"type":"string"},"filename":{"type":"string"},"byteSize":{"type":["number","null"]},"contentType":{"type":["string","null"]},"object":{"type":["string","null"],"description":"Where it landed in the organisation’s own storage, once copied."},"status":{"type":"string","enum":["queued","copying","filed","failed"]},"copiedBytes":{"type":"integer"},"error":{"type":["string","null"]},"startedAt":{"type":["string","null"]},"finishedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"additionalProperties":true}},"next_cursor":{"type":["string","null"]}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listCloudImports","x-permission":"project.view","x-permissions":["project.view"],"description":"Permission: project.view."}},"/v1/bin":{"get":{"summary":"List the asset bin","tags":["Bin"],"description":"Objects under a prefix of the organisation’s asset bin and the folders below it, newest first. With projectId, a project’s imports instead. Needs asset.view at the organisation.\n\nPermission: asset.view.","parameters":[{"schema":{"type":"string","maxLength":1024},"in":"query","name":"prefix","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"q","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"projectId","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"An object in the organisation’s asset bin.","properties":{"id":{"type":"string","format":"uuid"},"key":{"type":"string"},"name":{"type":"string"},"size":{"type":"integer"},"contentType":{"type":"string"},"updatedAt":{"type":"string"},"family":{"type":"string"},"projectId":{"type":["string","null"]},"previewUrl":{"type":["string","null"],"description":"A signed URL for an image, valid for an hour."}},"additionalProperties":true}},"folders":{"type":"array","items":{"type":"string"}},"next_cursor":{"type":["string","null"]},"prefix":{"type":"string"}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listBin","x-permission":"asset.view","x-permissions":["asset.view"]}},"/v1/bin/uploads":{"post":{"summary":"Open an upload into the bin","tags":["Bin"],"description":"Declare the file; get a session. Send parts to the URLs from /parts (or the whole file to uploadUrl with the length-range header), then /complete. Needs asset.upload at the organisation.\n\nPermission: asset.upload.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["filename","contentType","byteSize"],"additionalProperties":false,"properties":{"filename":{"type":"string","minLength":1,"maxLength":200},"contentType":{"type":"string","pattern":"^(?!.*(?:html|javascript|ecmascript|svg))(?!(?!application/vnd\\.openxmlformats-officedocument\\.(?:wordprocessingml|spreadsheetml|presentationml)\\.[a-z.]+$).*xml)(image/|video/|audio/|text/|application/)[a-zA-Z0-9.+-]+$","maxLength":100},"byteSize":{"type":"integer","minimum":1,"maximum":5497558138880},"folder":{"type":["string","null"],"maxLength":500},"multipart":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"An open upload session. Send parts to signed URLs from /parts, then /complete with their ETags.","content":{"application/json":{"schema":{"type":"object","description":"An open upload session. Send parts to signed URLs from /parts, then /complete with their ETags.","properties":{"sessionId":{"type":"string","format":"uuid"},"object":{"type":"string"},"uploadId":{"type":["string","null"],"description":"The multipart upload id; null when opened for one signed PUT."},"partSize":{"type":"integer"},"partCount":{"type":"integer"},"uploadUrl":{"type":["string","null"],"description":"One signed PUT for the whole file, with x-goog-content-length-range set to lengthRange. Null above 5 GB."},"lengthRange":{"type":"string"},"contentType":{"type":"string"},"expiresInSeconds":{"type":"integer"}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"startBinUpload","x-permission":"asset.upload","x-permissions":["asset.upload"],"x-idempotent":true}},"/v1/bin/uploads/{sessionId}/parts":{"post":{"summary":"Signed URLs for a run of parts","tags":["Bin"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["object","uploadId","from","to"],"additionalProperties":false,"properties":{"object":{"type":"string","minLength":1,"maxLength":1200},"uploadId":{"type":"string","minLength":1,"maxLength":400},"from":{"type":"integer","minimum":1,"maximum":10000},"to":{"type":"integer","minimum":1,"maximum":10000}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"sessionId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"urls":{"type":"array","items":{"type":"object","properties":{"partNumber":{"type":"integer"},"url":{"type":"string"}},"additionalProperties":true}},"expiresInSeconds":{"type":"integer"}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createBinUploadPartUrls","x-permission":"asset.upload","x-permissions":["asset.upload"],"description":"Permission: asset.upload.","x-idempotent":true}},"/v1/bin/uploads/{sessionId}/complete":{"post":{"summary":"Complete an upload into the bin","tags":["Bin"],"description":"Assemble the parts (every part, 1 to N) or confirm the single PUT. What landed is read back and refused if its size or type is not what was declared.\n\nPermission: asset.upload.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["object"],"additionalProperties":false,"properties":{"object":{"type":"string","minLength":1,"maxLength":1200},"uploadId":{"type":["string","null"],"maxLength":400},"parts":{"type":"array","maxItems":10000,"items":{"type":"object","required":["partNumber","etag"],"additionalProperties":false,"properties":{"partNumber":{"type":"integer","minimum":1,"maximum":10000},"etag":{"type":"string","minLength":1,"maxLength":200}}}}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"object","description":"An object in the organisation’s asset bin.","properties":{"id":{"type":"string","format":"uuid"},"key":{"type":"string"},"name":{"type":"string"},"size":{"type":"integer"},"contentType":{"type":"string"},"updatedAt":{"type":"string"},"family":{"type":"string"},"projectId":{"type":["string","null"]},"previewUrl":{"type":["string","null"],"description":"A signed URL for an image, valid for an hour."}},"additionalProperties":true}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"completeBinUpload","x-permission":"asset.upload","x-permissions":["asset.upload"],"x-idempotent":true}},"/v1/bin/uploads/{sessionId}/abort":{"post":{"summary":"Abandon an upload session","tags":["Bin"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["object"],"additionalProperties":false,"properties":{"object":{"type":"string","minLength":1,"maxLength":1200},"uploadId":{"type":["string","null"],"maxLength":400}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"sessionId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"aborted":{"type":"boolean"}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"abortBinUpload","x-permission":"asset.upload","x-permissions":["asset.upload"],"description":"Permission: asset.upload.","x-idempotent":true}},"/v1/bin/download":{"get":{"summary":"A download link for a bin object","tags":["Bin"],"description":"A signed URL valid for fifteen minutes, served as a download. Needs asset.download at the organisation.\n\nPermission: asset.download.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":1200},"in":"query","name":"key","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"filename":{"type":"string"},"expiresInSeconds":{"type":"integer"}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getBinDownload","x-permission":"asset.download","x-permissions":["asset.download"]}},"/v1/projects/{projectId}/deliverables/{deliverableId}/attach":{"post":{"summary":"Add an existing file to a placeholder","tags":["Bin"],"description":"Copy an object from the bin ({ source: \"bin\", key }) or from another row ({ source: \"asset\", deliverableId }) onto this placeholder, inside the organisation’s storage, and file it as an upload would be. A file of the wrong family is refused with a sentence.\n\nPermission: asset.upload.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["source"],"additionalProperties":false,"properties":{"source":{"type":"string","enum":["bin","asset"]},"key":{"type":"string","minLength":1,"maxLength":1200},"deliverableId":{"type":"string","format":"uuid"},"senderAccess":{"type":"string","enum":["open","withheld"]}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"deliverableId","required":true},{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"object":{"type":"string"},"bytes":{"type":"integer"},"contentType":{"type":["string","null"]}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"attachBinObject","x-permission":"asset.upload","x-permissions":["asset.upload"],"x-idempotent":true}},"/v1/projects/{projectId}/storage-deliveries/preview":{"post":{"summary":"Preview a delivery to storage","tags":["Deliveries"],"description":"Where every file would land under the path template, and anything that would stop the delivery (a row with no file, a QC block, two files on one key). Writes nothing. Needs project.configure and asset.download on the deal.\n\nPermission: project.configure and asset.download.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["connectionId","pathTemplate"],"additionalProperties":false,"properties":{"connectionId":{"type":"string","format":"uuid"},"deliverableIds":{"type":"array","minItems":1,"maxItems":500,"items":{"type":"string","format":"uuid"}},"deliveryId":{"type":"string","format":"uuid"},"pathTemplate":{"type":"string","minLength":1,"maxLength":500},"overwrite":{"type":"string","enum":["skip","rename","overwrite"]},"verifyChecksums":{"type":"boolean"},"buyerEmail":{"type":["string","null"],"maxLength":320}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"object","properties":{"files":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}},"problems":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}},"qcBlocks":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":true}},"collisions":{"type":"array","items":{"type":"string"}},"canStart":{"type":"boolean"}},"additionalProperties":true}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"previewStorageDelivery","x-permission":"project.configure asset.download","x-permissions":["project.configure","asset.download"],"x-idempotent":true}},"/v1/projects/{projectId}/storage-deliveries":{"post":{"summary":"Deliver files to a connected bucket","tags":["Deliveries"],"description":"Copy deliverables (deliverableIds, or a delivery’s rows with deliveryId) from the organisation’s storage to a connection with read and write access, granted to the deal or connected for its buyer, that has passed its test. Every file is checksummed on read and checked against what the destination reports; receipt.json is written beside the files when all arrive. Fires delivery.completed or delivery.failed. Needs project.configure and asset.download on the deal. The copy runs in the background; poll the delivery.\n\nPermission: project.configure and asset.download.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["connectionId","pathTemplate"],"additionalProperties":false,"properties":{"connectionId":{"type":"string","format":"uuid"},"deliverableIds":{"type":"array","minItems":1,"maxItems":500,"items":{"type":"string","format":"uuid"}},"deliveryId":{"type":"string","format":"uuid"},"pathTemplate":{"type":"string","minLength":1,"maxLength":500},"overwrite":{"type":"string","enum":["skip","rename","overwrite"]},"verifyChecksums":{"type":"boolean"},"buyerEmail":{"type":["string","null"],"maxLength":320}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"storageDelivery":{"type":"object","description":"One delivery of files to a connected bucket.","properties":{"id":{"type":"string","format":"uuid"},"projectId":{"type":"string","format":"uuid"},"deliveryId":{"type":["string","null"],"description":"The bill of goods this run ships, when it ships one."},"connectionId":{"type":"string","format":"uuid"},"destination":{"type":"object","properties":{"provider":{"type":"string"},"bucket":{"type":"string"},"prefix":{"type":"string"},"connectionName":{"type":"string"},"buyerName":{"type":["string","null"]},"uriBase":{"type":"string"}},"additionalProperties":true},"pathTemplate":{"type":"string"},"overwrite":{"type":"string","enum":["skip","rename","overwrite"]},"verifyChecksums":{"type":"boolean"},"status":{"type":"string","enum":["queued","running","completed","failed"]},"fileCount":{"type":"integer"},"totalBytes":{"type":"integer"},"copiedBytes":{"type":"integer"},"deliveredCount":{"type":"integer"},"skippedCount":{"type":"integer","description":"Files already at the destination under the skip policy, left as they were."},"failedCount":{"type":"integer"},"error":{"type":["string","null"]},"receiptUri":{"type":["string","null"],"description":"Where receipt.json was written beside the files. Null until the run completes."},"receiptSha256":{"type":["string","null"],"description":"SHA-256 of receipt.json as written."},"createdAt":{"type":"string"},"startedAt":{"type":["string","null"]},"finishedAt":{"type":["string","null"]}},"additionalProperties":true}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"createStorageDelivery","x-permission":"project.configure asset.download","x-permissions":["project.configure","asset.download"],"x-idempotent":true},"get":{"summary":"List deliveries to storage","tags":["Deliveries"],"description":"A deal’s deliveries to storage, newest first, a page at a time. Filter by delivery, by deliverable or by status.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"query","name":"deliveryId","required":false},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"deliverableId","required":false},{"schema":{"type":"string","enum":["queued","running","completed","failed"]},"in":"query","name":"status","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"One delivery of files to a connected bucket.","properties":{"id":{"type":"string","format":"uuid"},"projectId":{"type":"string","format":"uuid"},"deliveryId":{"type":["string","null"],"description":"The bill of goods this run ships, when it ships one."},"connectionId":{"type":"string","format":"uuid"},"destination":{"type":"object","properties":{"provider":{"type":"string"},"bucket":{"type":"string"},"prefix":{"type":"string"},"connectionName":{"type":"string"},"buyerName":{"type":["string","null"]},"uriBase":{"type":"string"}},"additionalProperties":true},"pathTemplate":{"type":"string"},"overwrite":{"type":"string","enum":["skip","rename","overwrite"]},"verifyChecksums":{"type":"boolean"},"status":{"type":"string","enum":["queued","running","completed","failed"]},"fileCount":{"type":"integer"},"totalBytes":{"type":"integer"},"copiedBytes":{"type":"integer"},"deliveredCount":{"type":"integer"},"skippedCount":{"type":"integer","description":"Files already at the destination under the skip policy, left as they were."},"failedCount":{"type":"integer"},"error":{"type":["string","null"]},"receiptUri":{"type":["string","null"],"description":"Where receipt.json was written beside the files. Null until the run completes."},"receiptSha256":{"type":["string","null"],"description":"SHA-256 of receipt.json as written."},"createdAt":{"type":"string"},"startedAt":{"type":["string","null"]},"finishedAt":{"type":["string","null"]}},"additionalProperties":true}},"next_cursor":{"type":["string","null"]}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listStorageDeliveries","x-permission":"project.view","x-permissions":["project.view"]}},"/v1/projects/{projectId}/storage-deliveries/{storageDeliveryId}":{"get":{"summary":"Read a delivery to storage","tags":["Deliveries"],"description":"Its status and progress, and every file with its destination, checksums and check.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"storageDeliveryId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"storageDelivery":{"type":"object","description":"One delivery of files to a connected bucket.","properties":{"id":{"type":"string","format":"uuid"},"projectId":{"type":"string","format":"uuid"},"deliveryId":{"type":["string","null"],"description":"The bill of goods this run ships, when it ships one."},"connectionId":{"type":"string","format":"uuid"},"destination":{"type":"object","properties":{"provider":{"type":"string"},"bucket":{"type":"string"},"prefix":{"type":"string"},"connectionName":{"type":"string"},"buyerName":{"type":["string","null"]},"uriBase":{"type":"string"}},"additionalProperties":true},"pathTemplate":{"type":"string"},"overwrite":{"type":"string","enum":["skip","rename","overwrite"]},"verifyChecksums":{"type":"boolean"},"status":{"type":"string","enum":["queued","running","completed","failed"]},"fileCount":{"type":"integer"},"totalBytes":{"type":"integer"},"copiedBytes":{"type":"integer"},"deliveredCount":{"type":"integer"},"skippedCount":{"type":"integer","description":"Files already at the destination under the skip policy, left as they were."},"failedCount":{"type":"integer"},"error":{"type":["string","null"]},"receiptUri":{"type":["string","null"],"description":"Where receipt.json was written beside the files. Null until the run completes."},"receiptSha256":{"type":["string","null"],"description":"SHA-256 of receipt.json as written."},"createdAt":{"type":"string"},"startedAt":{"type":["string","null"]},"finishedAt":{"type":["string","null"]}},"additionalProperties":true},"files":{"type":"array","items":{"type":"object","description":"One file of a delivery.","properties":{"id":{"type":"string","format":"uuid"},"deliverableId":{"type":["string","null"]},"label":{"type":["string","null"]},"titleName":{"type":["string","null"]},"filename":{"type":"string"},"byteSize":{"type":["integer","null"]},"destKey":{"type":["string","null"]},"destUri":{"type":["string","null"]},"status":{"type":"string","enum":["pending","copying","verified","skipped","failed"]},"copiedBytes":{"type":"integer"},"checksums":{"type":"object","description":"Computed as the bytes were read.","properties":{"md5":{"type":["string","null"],"description":"Hex."},"crc32c":{"type":["string","null"],"description":"Base64, big-endian, as Google reports it."},"sha256":{"type":["string","null"],"description":"Hex."}},"additionalProperties":true},"verification":{"type":["string","null"],"description":"verified (the destination’s checksum matched), computed_at_source (the destination gave nothing comparable; its size matched), or existing (skipped)."},"verifyMethod":{"type":["string","null"],"description":"s3-etag, s3-multipart-etag, gcs-md5, gcs-crc32c, azure-block-md5 or size."},"error":{"type":["string","null"]}},"additionalProperties":true}}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getStorageDelivery","x-permission":"project.view","x-permissions":["project.view"]}},"/v1/projects/{projectId}/storage-deliveries/{storageDeliveryId}/receipt":{"get":{"summary":"Read a delivery’s receipt","tags":["Deliveries"],"description":"The receipt as written to the destination as receipt.json: files, sizes, checksums, destination URIs, times and who sent it. 404 until the delivery completes.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"storageDeliveryId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"receipt":{"type":"object","properties":{},"additionalProperties":true},"receiptUri":{"type":["string","null"]},"receiptSha256":{"type":["string","null"]}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getStorageDeliveryReceipt","x-permission":"project.view","x-permissions":["project.view"]}},"/v1/billing":{"get":{"summary":"The plan, the month so far and the invoices","tags":["Billing"],"description":"The organisation’s plan (subscription, included allowances, unit prices, the marketplace fee), this month’s figures line by line (subscription, seats, storage, processing, AI, transfers, API calls, marketplace fees) with the allowance and what is priced beyond it, the projection for the month, ninety days of daily cost by metric, and the invoices. Amounts are micros (millionths) of the currency. Lines marked indicative came from loaded figures, not the meter, and are never invoiced.\n\nPermission: org.settings.edit.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"object","properties":{"code":{"type":["null","string"]},"name":{"type":["null","string"]},"currency":{"type":["null","string"]},"monthlyFeeMicros":{"type":["null","number"]},"seatFeeMicros":{"type":["null","number"]},"included":{"type":"object","properties":{},"additionalProperties":true},"unitPriceMicros":{"type":"object","properties":{},"additionalProperties":true},"marketplaceFeeBps":{"type":["null","number"]},"aiMarkupBps":{"type":["null","number"]}},"additionalProperties":true},"status":{"type":["null","string"]},"period":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"},"daysInMonth":{"type":"integer"},"daysElapsed":{"type":"integer"}},"additionalProperties":true},"lines":{"type":"array","items":{"type":"object","properties":{"metric":{"type":"string"},"label":{"type":"string"},"quantity":{"type":"number"},"unit":{"type":"string"},"included":{"type":["null","number"]},"billable":{"type":"number"},"unitPriceMicros":{"type":"number"},"amountMicros":{"type":"number"},"indicative":{"type":"boolean"}},"additionalProperties":true}},"totals":{"type":"object","properties":{"monthToDateMicros":{"type":["null","number"]},"projectedMicros":{"type":["null","number"]},"subscriptionMicros":{"type":["null","number"]},"usageMicros":{"type":["null","number"]},"feesMicros":{"type":["null","number"]}},"additionalProperties":true},"series":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string"},"byMetric":{"type":"object","description":"Gross cost per metric in micros.","properties":{},"additionalProperties":true},"indicative":{"type":"boolean"}},"additionalProperties":true}},"invoices":{"type":"array","items":{"type":"object","properties":{"number":{"type":"string"},"periodStart":{"type":"string"},"periodEnd":{"type":"string"},"status":{"type":"string"},"totalMicros":{"type":["null","number"]},"issuedAt":{"type":["null","string"]},"paidAt":{"type":["null","string"]}},"additionalProperties":true}}},"additionalProperties":true}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getBilling","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"]}},"/v1/processes":{"get":{"summary":"List processes","tags":["Processes"],"description":"The processing log, newest first: proxies, QC, readings, imports, exports, enrichment. Filter by status, kind, project or subject. A run left running by a crash is marked failed as the log is read. Needs project.view at the organisation.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","enum":["running","done","failed","cancelled"]},"in":"query","name":"status","required":false},{"schema":{"type":"string","maxLength":60},"in":"query","name":"kind","required":false},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"project","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"subject","required":false,"description":"The id of what a run worked on: a deliverable, a title, an import."},{"schema":{"type":"string","maxLength":200},"in":"query","name":"cursor","required":false,"description":"The next_cursor of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","description":"One run on the processing log: what was asked, how far it got, how it ended.","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","description":"What ran: media.proxy, media.qc, media.universe, cloud.import, rights.avails, catalog.enrich and so on."},"status":{"type":"string","enum":["running","done","failed","cancelled"]},"progress":{"type":"number","description":"0 to 1, for the runs that can say."},"project":{"type":"object","description":"The deal it was about, or null.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"subject":{"type":["string","null"],"description":"The id of what it worked on: a deliverable, a title, an import."},"subjectLabel":{"type":["string","null"],"description":"What it worked on, in words."},"current":{"type":["string","null"],"description":"What it is doing now, while it runs."},"reason":{"type":["string","null"],"description":"Why it stopped, for a failed or cancelled run."},"titles":{"type":"object","properties":{"total":{"type":"integer"},"done":{"type":"integer"}},"additionalProperties":true},"found":{"type":"object","description":"What a lookup run turned up, counted.","properties":{},"additionalProperties":true},"startedBy":{"type":["string","null"],"description":"The person behind the run."},"startedAt":{"type":"string","format":"date-time"},"endedAt":{"type":["string","null"]},"retryable":{"type":"boolean","description":"POST /v1/processes/{id}/retry can start it again from what it knows. Others start again from their own page or route."}},"additionalProperties":true}},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null on the last."}},"additionalProperties":true},"example":{"data":[{"id":"e9d8c7b6-a5f4-4e3d-8c2b-1a0f9e8d7c6b","kind":"enrichment","status":"running","progress":0.4,"project":{"id":"7d1f4c2a-3b5e-4f6a-9c8d-1e2f3a4b5c6d","name":"Nordic SVOD licence 2027"},"subject":"a3c9e1b2-4d5f-4a6b-8c7d-9e0f1a2b3c4d","subjectLabel":"The Long Harbour","current":"Reading credits","reason":null,"titles":{"total":5,"done":2},"startedBy":"Delivery robot","startedAt":"2027-01-15T10:30:00.000Z","endedAt":null,"retryable":false}],"next_cursor":null}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"listProcesses","x-permission":"project.view","x-permissions":["project.view"]}},"/v1/processes/{processId}":{"get":{"summary":"Read a process","tags":["Processes"],"description":"One run: its status, progress, what it is doing now and, once ended, how. The status URL every async write names. Needs project.view at the organisation.\n\nPermission: project.view.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"processId","required":true}],"responses":{"200":{"description":"One run on the processing log: what was asked, how far it got, how it ended.","content":{"application/json":{"schema":{"type":"object","description":"One run on the processing log: what was asked, how far it got, how it ended.","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","description":"What ran: media.proxy, media.qc, media.universe, cloud.import, rights.avails, catalog.enrich and so on."},"status":{"type":"string","enum":["running","done","failed","cancelled"]},"progress":{"type":"number","description":"0 to 1, for the runs that can say."},"project":{"type":"object","description":"The deal it was about, or null.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":true},"subject":{"type":["string","null"],"description":"The id of what it worked on: a deliverable, a title, an import."},"subjectLabel":{"type":["string","null"],"description":"What it worked on, in words."},"current":{"type":["string","null"],"description":"What it is doing now, while it runs."},"reason":{"type":["string","null"],"description":"Why it stopped, for a failed or cancelled run."},"titles":{"type":"object","properties":{"total":{"type":"integer"},"done":{"type":"integer"}},"additionalProperties":true},"found":{"type":"object","description":"What a lookup run turned up, counted.","properties":{},"additionalProperties":true},"startedBy":{"type":["string","null"],"description":"The person behind the run."},"startedAt":{"type":"string","format":"date-time"},"endedAt":{"type":["string","null"]},"retryable":{"type":"boolean","description":"POST /v1/processes/{id}/retry can start it again from what it knows. Others start again from their own page or route."}},"additionalProperties":true},"example":{"id":"e9d8c7b6-a5f4-4e3d-8c2b-1a0f9e8d7c6b","kind":"enrichment","status":"running","progress":0.4,"project":{"id":"7d1f4c2a-3b5e-4f6a-9c8d-1e2f3a4b5c6d","name":"Nordic SVOD licence 2027"},"subject":"a3c9e1b2-4d5f-4a6b-8c7d-9e0f1a2b3c4d","subjectLabel":"The Long Harbour","current":"Reading credits","reason":null,"titles":{"total":5,"done":2},"startedBy":"Delivery robot","startedAt":"2027-01-15T10:30:00.000Z","endedAt":null,"retryable":false}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getProcess","x-permission":"project.view","x-permissions":["project.view"]}},"/v1/processes/{processId}/retry":{"post":{"summary":"Retry a process","tags":["Processes"],"description":"Starts a finished, failed or stopped run again from what it knows: a proxy or QC from its file, a reading from its title (picking up where it stopped), a master, key art, a character sheet. Answers 202 with a Location naming the new run. A run that is still going is a 409; a kind that needs its inputs again (an extraction, an enrichment) is a 409 that says to start it from its own route. Needs project.configure at the organisation. Idempotent on the Idempotency-Key.\n\nPermission: project.configure.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"processId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"202":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"retried":{"type":"boolean"},"resumed":{"type":"boolean","description":"A reading picked up where it stopped."},"of":{"type":"string","format":"uuid"},"processId":{"type":["string","null"],"description":"The new run, once it is on the log."},"statusUrl":{"type":["string","null"],"description":"GET it to watch the new run."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"retryProcess","x-permission":"project.configure","x-permissions":["project.configure"],"x-idempotent":true}},"/v1/processes/{processId}/stop":{"post":{"summary":"Stop a process","tags":["Processes"],"description":"Ends a running run now: it is marked cancelled, the fields it was writing unlock at once, a job still waiting for the worker is taken off the queue, and a job already running stops at its next boundary. A run that has already ended answers stopped false with its status. Needs project.configure at the organisation. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: project.configure.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"processId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"stopped":{"type":"boolean"},"status":{"type":"string","enum":["running","done","failed","cancelled"]}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"stopProcess","x-permission":"project.configure","x-permissions":["project.configure"],"x-idempotent":true}},"/v1/projects/{projectId}/archive":{"post":{"summary":"Archive a project","tags":["Projects"],"description":"Sets the deal to archived, as the product’s status menu does: it leaves the working lists and keeps every record and file. Archiving an archived deal changes nothing. On a deal received from another organisation, the sender’s handoff permissions decide. Needs project.configure. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: project.configure.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"A project is one deal: the work agreed, in one direction, with one counterparty.","content":{"application/json":{"schema":{"type":"object","description":"A project is one deal: the work agreed, in one direction, with one counterparty.","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":["string","null"],"description":"The deal reference people quote, e.g. FM-7KQ2-P9XA."},"name":{"type":"string"},"status":{"type":"string"},"direction":{"type":["string","null"],"description":"outbound: this Org is sending. inbound: this Org is receiving."},"group":{"type":"object","description":"The Group the deal hangs off, or null at the Org root.","properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]}},"additionalProperties":true},"client":{"type":["string","null"]},"distributor":{"type":["string","null"]},"counterparty":{"type":"object","properties":{"orgId":{"type":["string","null"]},"name":{"type":["string","null"]}},"additionalProperties":true},"dealReference":{"type":["string","null"],"description":"The counterparty’s own reference."},"deliveryStarts":{"type":["string","null"],"description":"ISO date."},"deliveryEnds":{"type":["string","null"],"description":"ISO date."},"contentTier":{"type":["string","null"]},"licence":{"type":"object","properties":{"starts":{"type":["string","null"]},"ends":{"type":["string","null"]},"months":{"type":["number","null"]},"fee":{"type":["string","null"],"description":"A decimal string, never a float."},"currency":{"type":["string","null"]}},"additionalProperties":true},"rights":{"type":"object","properties":{"granted":{"type":"array","items":{"type":"string"}},"reserved":{"type":"array","items":{"type":"string"}},"exclusivity":{"type":["string","null"],"description":"exclusive, first_window or non_exclusive."},"territories":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"archiveProject","x-permission":"project.configure","x-permissions":["project.configure"],"x-idempotent":true}},"/v1/projects/{projectId}/restore":{"post":{"summary":"Restore an archived project","tags":["Projects"],"description":"Sets an archived deal back to active. A deal that is not archived is a 409. Needs project.configure. Idempotent on the Idempotency-Key. Written to the action log.\n\nPermission: project.configure.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"A project is one deal: the work agreed, in one direction, with one counterparty.","content":{"application/json":{"schema":{"type":"object","description":"A project is one deal: the work agreed, in one direction, with one counterparty.","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":["string","null"],"description":"The deal reference people quote, e.g. FM-7KQ2-P9XA."},"name":{"type":"string"},"status":{"type":"string"},"direction":{"type":["string","null"],"description":"outbound: this Org is sending. inbound: this Org is receiving."},"group":{"type":"object","description":"The Group the deal hangs off, or null at the Org root.","properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]}},"additionalProperties":true},"client":{"type":["string","null"]},"distributor":{"type":["string","null"]},"counterparty":{"type":"object","properties":{"orgId":{"type":["string","null"]},"name":{"type":["string","null"]}},"additionalProperties":true},"dealReference":{"type":["string","null"],"description":"The counterparty’s own reference."},"deliveryStarts":{"type":["string","null"],"description":"ISO date."},"deliveryEnds":{"type":["string","null"],"description":"ISO date."},"contentTier":{"type":["string","null"]},"licence":{"type":"object","properties":{"starts":{"type":["string","null"]},"ends":{"type":["string","null"]},"months":{"type":["number","null"]},"fee":{"type":["string","null"],"description":"A decimal string, never a float."},"currency":{"type":["string","null"]}},"additionalProperties":true},"rights":{"type":"object","properties":{"granted":{"type":"array","items":{"type":"string"}},"reserved":{"type":"array","items":{"type":"string"}},"exclusivity":{"type":["string","null"],"description":"exclusive, first_window or non_exclusive."},"territories":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"restoreProject","x-permission":"project.configure","x-permissions":["project.configure"],"x-idempotent":true}},"/v1/webhooks/{webhookId}/rotate-secret":{"post":{"summary":"Rotate an endpoint’s signing secret","tags":["Webhooks"],"description":"A new secret, in this answer and no other: store it. Deliveries signed from now on use it, and the old one stops at once. Needs integration.configure. Idempotent on the Idempotency-Key: a retry returns the same new secret rather than rotating again. Written to the action log without the secret.\n\nPermission: integration.configure.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"webhookId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"secret":{"type":"string","description":"whsec_<base64>. Shown once."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"rotateWebhookSecret","x-permission":"integration.configure","x-permissions":["integration.configure"],"x-idempotent":true}},"/v1/webhooks/{webhookId}/test":{"post":{"summary":"Send a test event","tags":["Webhooks"],"description":"A ping event to this endpoint only, whatever it subscribes to, signed like every delivery. The answer is the delivery; follow it in the endpoint’s delivery log. Needs integration.configure. Idempotent on the Idempotency-Key.\n\nPermission: integration.configure.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"webhookId","required":true},{"schema":{"type":"string","maxLength":255},"in":"header","name":"idempotency-key","required":true,"description":"Any string up to 255 characters, unique per intended write. The first answer is kept for a day: the same key and body replay it with Idempotent-Replayed: true; the same key with a different body is a 409."}],"responses":{"201":{"description":"One event to one endpoint. A 2xx within ten seconds is delivered; anything else retries after 1m, 5m, 30m, 2h and 12h, then is dead.","content":{"application/json":{"schema":{"type":"object","description":"One event to one endpoint. A 2xx within ten seconds is delivered; anything else retries after 1m, 5m, 30m, 2h and 12h, then is dead.","properties":{"id":{"type":"string","format":"uuid"},"endpointId":{"type":"string","format":"uuid"},"eventId":{"type":"string","description":"The webhook-id header. The same for every delivery of one event, redeliveries included."},"eventType":{"type":"string"},"subject":{"type":"string","description":"What the event is about: a deliverable id, a handoff id."},"status":{"type":"string","enum":["pending","delivered","failed","dead"]},"attempt":{"type":"integer"},"responseStatus":{"type":["number","null"]},"responseMs":{"type":["number","null"]},"error":{"type":["string","null"]},"nextAttemptAt":{"type":["string","null"]},"deliveredAt":{"type":["string","null"]},"createdAt":{"type":"string"},"payload":{"type":"object","description":"The body as sent: id, type, createdAt, organisationId, projectId, subject, data.","properties":{},"additionalProperties":true}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"testWebhook","x-permission":"integration.configure","x-permissions":["integration.configure"],"x-idempotent":true}},"/v1/usage":{"get":{"summary":"Metered usage for a period","tags":["Usage"],"description":"The figures billing is computed from, for any span of up to 92 days (default: this month to today): per metric, the quantity (gauges such as seats and storage averaged over the days read, flows summed) and its gross cost at the plan’s list price, in micros of the currency. Name one metric for its days. The priced month, with allowances and the projection, is GET /v1/billing. Indicative figures came from loaded data, not the meter, and are never invoiced. Read only. Needs org.settings.edit. Metrics: subscription, seats, storage_gb, processing_minutes, ai_micros, transfer_gb, api_calls, marketplace_fees.\n\nPermission: org.settings.edit.","parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"in":"query","name":"from","required":false},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"in":"query","name":"to","required":false},{"schema":{"type":"string","enum":["subscription","seats","storage_gb","processing_minutes","ai_micros","transfer_gb","api_calls","marketplace_fees"]},"in":"query","name":"metric","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"period":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"days":{"type":"integer"}},"additionalProperties":true},"currency":{"type":["string","null"],"description":"ISO 4217, from the plan. Null with no plan."},"metrics":{"type":"array","items":{"type":"object","properties":{"metric":{"type":"string"},"label":{"type":"string"},"unit":{"type":"string"},"aggregate":{"type":"string","enum":["average","sum"]},"quantity":{"type":"number"},"costMicros":{"type":"number"},"indicative":{"type":"boolean"},"days":{"type":"integer","description":"Days with a reading."}},"additionalProperties":true}},"daily":{"type":["array","null"],"items":{"type":"object","properties":{"day":{"type":"string"},"quantity":{"type":"number"},"costMicros":{"type":["number","null"]},"indicative":{"type":"boolean"}},"additionalProperties":true},"description":"The named metric’s days; null without metric."},"indicative":{"type":"boolean"}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"403":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getUsage","x-permission":"org.settings.edit","x-permissions":["org.settings.edit"]}},"/v1/sandbox":{"get":{"summary":"How to get a Sandbox key","tags":["Access"],"description":"Public, no credential. Says whether instant Sandbox keys are offered here (POST /v1/sandbox/keys), and where to ask for a key a person approves. Every Sandbox key starts ff_sbx_, works only in the Sandbox and carries the permissions listed.\n\nPermission: none beyond a valid credential of the organisation.","security":[],"responses":{"200":{"description":"How to get a key to the Sandbox here.","content":{"application/json":{"schema":{"type":"object","description":"How to get a key to the Sandbox here.","properties":{"instantKeys":{"type":"boolean","description":"Whether POST /v1/sandbox/keys is offered here now."},"instant":{"type":"object","description":"How instant keys work, or null while they are not offered.","properties":{"request":{"type":"string","description":"The call that asks for a key."},"keyDays":{"type":"integer","description":"How long an instant key lasts from when it is collected."},"linkHours":{"type":"integer","description":"How long the emailed link works."}},"additionalProperties":true},"requestAccess":{"type":"string","description":"Where to ask for a Sandbox key that a person approves."},"approvedKeyDays":{"type":"integer","description":"How long an approved key lasts."},"permissions":{"type":"array","items":{"type":"string"},"description":"What every Sandbox key may do."}},"additionalProperties":true}}}},"429":{"description":"Too many requests: rate_limited. Wait Retry-After seconds, then send the same request.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"getSandboxOffer","x-permission":"none","x-permissions":[]}},"/v1/sandbox/keys":{"post":{"summary":"Ask for an instant Sandbox key","tags":["Access"],"description":"Public, no credential, and only while GET /v1/sandbox says instantKeys: true; otherwise 404. Emails the address a one-time link that works for 24 hours. Opening it shows a Sandbox key once, which lasts 30 days. One live instant key per address (409); 3 requests an hour per address and 10 per caller (429 with Retry-After); a daily cap across everyone (429 until midnight UTC). Asking again replaces the earlier link. A repeat within a minute sends nothing and answers the same, so a retry is safe without an Idempotency-Key.\n\nPermission: none beyond a valid credential of the organisation.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"additionalProperties":false,"properties":{"email":{"type":"string","format":"email","maxLength":320,"description":"An inbox you or your agent can read."}}}}}},"security":[],"responses":{"202":{"description":"A one-time link is on its way to the address. Opening it shows the key once.","content":{"application/json":{"schema":{"type":"object","description":"A one-time link is on its way to the address. Opening it shows the key once.","properties":{"status":{"type":"string","enum":["sent"]},"email":{"type":"string"},"linkExpiresAt":{"type":"string","format":"date-time"},"message":{"type":"string"}},"additionalProperties":true}}}},"400":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"404":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"409":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}}},"429":{"description":"Every error: a code from the catalogue and what to do next.","content":{"application/json":{"schema":{"type":"object","description":"Every error: a code from the catalogue and what to do next.","properties":{"error":{"type":"string"},"message":{"type":"string"},"docs":{"type":"string","format":"uri","description":"The code's row on the troubleshooting page: https://www.final-frame.com/docs/troubleshooting#<code>."},"retryable":{"type":"boolean","description":"True when sending the same request again, after a wait, can succeed (rate_limited, internal, unavailable). Wait Retry-After seconds when it is sent."}},"additionalProperties":true}}},"headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}}},"500":{"description":"internal: logged on our side. Retry with backoff and, on a write, the same Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"unavailable: a service Final Frame depends on is not answering. Wait Retry-After seconds and retry.","headers":{"Retry-After":{"description":"Seconds to wait before sending the same request again.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"requestSandboxKey","x-permission":"none","x-permissions":[],"x-idempotent":true}}},"servers":[{"url":"https://qa.app.final-frame.com/api","description":"QA: the current platform. Sandbox keys work here too."}],"security":[{"apiKey":[]},{"oauth":[]}],"tags":[{"name":"Access","description":"Who is calling and what they may do."},{"name":"Projects","description":"Deals: one project per deal, in one direction, with one counterparty."},{"name":"Titles","description":"The works a deal covers: films, shows, seasons and episodes."},{"name":"Deliverables","description":"Required outputs, their status, their QC and their files."},{"name":"Work orders","description":"Work raised where demand exceeds supply."},{"name":"Delivery specs","description":"A destination’s technical and editorial requirements: the organisation’s library, and the industry’s published specs."},{"name":"Rights","description":"What a deal grants, reserves and holds back."},{"name":"Audit","description":"The append-only record of who did what."},{"name":"Webhooks","description":"Endpoints that receive signed events as they happen, with the log of every delivery."},{"name":"Billing","description":"The plan the organisation is on, the month so far line by line, the projection and the invoices."},{"name":"Reference","description":"Vocabularies with stable identifiers."},{"name":"Configuration","description":"The organisation’s set-up as one document: export it, apply it."},{"name":"Cloud","description":"The organisation’s own buckets, connected: browse them and bring objects in."},{"name":"Deliveries","description":"Files sent to a connected bucket: start a delivery, follow it, read its receipt."},{"name":"Bin","description":"The organisation’s asset bin: upload, list, download, and add a file to a placeholder."},{"name":"Keys","description":"API keys: list, mint (the value shown once), revoke, and trust for writes without approval."},{"name":"Members","description":"Who has access: memberships, roles, invitations."},{"name":"Groups","description":"The organisation’s tree of groups."},{"name":"Approvals","description":"Writes held for a person to approve, when the organisation asks for that: where each one stands."},{"name":"Processes","description":"The processing log: what is running, what ran and how it ended, with retry and stop. Every async write names its run here."},{"name":"Usage","description":"The metered figures billing is computed from, for any period."}]}