{"openapi":"3.1.0","info":{"title":"FlowRights API","version":"1.0.0","description":"Contract management for the music industry — contracts, templates, obligations, approval workflows and signatures.\n\nAll JSON responses are enveloped: `{ \"success\": true, \"data\": … }` on success, `{ \"success\": false, \"error\": … }` on failure. Discriminate on `success` rather than on the status code."},"servers":[{"url":"https://api.flowrights.io","description":"This deployment"},{"url":"https://api.flowrights.io","description":"Production"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A workspace API key (`fr_live_…`) sent as `Authorization: Bearer`. Mint one at POST /api-keys. Accepted on every authenticated endpoint."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"better-auth.session_token","description":"Better Auth session cookie. How the first-party web app authenticates; not usable for server-to-server calls."}},"schemas":{"Health":{"type":"object","properties":{"status":{"type":"string","enum":["ok","error"]},"timestamp":{"type":"string","format":"date-time"},"database":{"type":"string","enum":["connected","disconnected"]},"release":{"type":"string","description":"Git SHA of the running build; matches the Sentry release tag.","example":"fda3cfb"}},"required":["status","timestamp","database","release"]},"SignContext":{"type":"object","properties":{"contractId":{"type":"string","description":"Contract id, as a string."},"title":{"type":"string"},"contractType":{"type":"string"},"status":{"type":"string"},"content":{"description":"The contract body, so the signer can read what they are agreeing to."},"alreadySigned":{"type":"boolean"},"dealTerms":{"type":"object","properties":{"territory":{"type":["array","null"],"items":{"type":"string"}},"contractValue":{"type":["string","null"],"description":"Decimal string, not a number."},"royaltyRate":{"type":["string","null"],"description":"Decimal string, not a number."},"effectiveDate":{"type":["string","null"],"format":"date-time"},"expirationDate":{"type":["string","null"],"format":"date-time"}},"required":["territory","contractValue","royaltyRate","effectiveDate","expirationDate"]},"you":{"type":"object","properties":{"role":{"type":"string"},"email":{"type":["string","null"]}},"required":["role","email"]},"parties":{"type":"array","items":{"type":"object","properties":{"role":{"type":"string"},"signatureStatus":{"type":"string"}},"required":["role","signatureStatus"]},"description":"Signing progress ONLY — role and status. Deliberately carries no other party PII, since this is served to an unauthenticated guest."}},"required":["contractId","title","contractType","status","alreadySigned","dealTerms","you","parties"]},"Meta":{"type":"object","properties":{"cursor":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"null"},{"type":"null"}],"description":"Opaque cursor for the next page, or null when exhausted."},"hasMore":{"type":"boolean","description":"Whether more rows exist."}},"required":["cursor","hasMore"]},"Error":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"string","description":"Human-readable message. Not intended for programmatic branching.","example":"API key not found"},"code":{"type":"string","description":"Stable machine-readable code, where one exists.","example":"ORG_REQUIRED"},"requestId":{"type":"string","description":"Echoed on 500s only, matching the X-Request-ID header, so a reported failure can be traced to its Sentry issue."}},"required":["success","error"]},"SignatureResult":{"type":"object","properties":{"contractStatus":{"type":"string","description":"The contract's status AFTER this signature — flips to `active` once all parties have signed."},"allSigned":{"type":"boolean"},"signedAt":{"type":"string","format":"date-time"},"signatureHash":{"type":"string"}},"required":["contractStatus","allSigned","signedAt","signatureHash"]},"ContractPartyListItem":{"type":"object","properties":{"id":{"type":"integer"},"contractId":{"type":"integer"},"organizationId":{"type":"string"},"userId":{"type":["string","null"],"description":"Org member who signs. Null for external parties, which carry `email` instead."},"email":{"type":["string","null"]},"isExternal":{"type":"boolean"},"role":{"type":"string","enum":["artist","producer","songwriter","publisher","label","manager","distributor","engineer","featured_artist","session_musician","other"]},"signatureStatus":{"type":"string","enum":["pending","signed","declined"]},"signedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","contractId","organizationId","userId","email","isExternal","role","signatureStatus","signedAt"]},"ContractSplit":{"type":"object","properties":{"id":{"type":"integer"},"contractId":{"type":"integer"},"organizationId":{"type":"string"},"userId":{"type":"string"},"role":{"type":"string","enum":["artist","producer","songwriter","publisher","label","manager","distributor","other"]},"percentage":{"type":"string","description":"Share of revenue, 0–100. Decimal string, not a number."},"revenueType":{"type":"string","enum":["all","streaming","sync","mechanical","performance","physical","merch","live"]},"conditions":{"type":["string","null"]}},"required":["id","contractId","organizationId","userId","role","percentage","revenueType","conditions"]},"Contract":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"description":{"type":["string","null"]},"contractType":{"type":"string","enum":["publishing","master_recording","sync_license","producer","distribution","work_for_hire","cowriting","three_sixty","management","licensing","other","platform_order_form","master_supply_agreement"]},"status":{"type":"string","enum":["draft","pending_review","approved","pending_signatures","active","expired","terminated"],"description":"Lifecycle: draft → pending_review → approved → pending_signatures → active → expired/terminated."},"content":{"description":"Tiptap document JSON. Shape is editor-defined and not modelled here."},"version":{"type":"integer"},"createdById":{"type":"string"},"templateId":{"type":["integer","null"]},"templateVersion":{"type":["integer","null"],"description":"Version of the template this contract was generated from."},"organizationId":{"type":"string"},"effectiveDate":{"type":["string","null"],"format":"date-time"},"expirationDate":{"type":["string","null"],"format":"date-time"},"territory":{"type":["array","null"],"items":{"type":"string"},"description":"ISO country codes, or a territory group."},"contractValue":{"type":["string","null"],"description":"Total contract value. Decimal string, not a number."},"advanceAmount":{"type":["string","null"],"description":"Advance paid against royalties. Decimal string, not a number."},"royaltyRate":{"type":["string","null"],"description":"Royalty rate as a percentage. Decimal string, not a number."},"exclusivity":{"type":"boolean"},"optionPeriods":{"type":["integer","null"]},"aiTrainingOptIn":{"type":["string","null"],"enum":["not_specified","opted_in","opted_out"]},"aiTrainingNotes":{"type":["string","null"]},"syncedToRoyalti":{"type":"boolean"},"royaltiContractId":{"type":["string","null"]},"royaltiAssetIsrc":{"type":["string","null"]},"lastSyncedAt":{"type":["string","null"],"format":"date-time"},"royaltiSyncError":{"type":["string","null"],"description":"Non-null means the contract is in a sync-error state. Cleared on re-queue."},"deletedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","title","description","contractType","status","version","createdById","templateId","templateVersion","organizationId","effectiveDate","expirationDate","territory","contractValue","advanceAmount","royaltyRate","exclusivity","optionPeriods","aiTrainingOptIn","aiTrainingNotes","syncedToRoyalti","royaltiContractId","royaltiAssetIsrc","lastSyncedAt","royaltiSyncError","deletedAt","createdAt","updatedAt"]},"ContractListItem":{"allOf":[{"$ref":"#/components/schemas/Contract"},{"type":"object","properties":{"parties":{"type":"array","items":{"$ref":"#/components/schemas/ContractPartyListItem"}},"splits":{"type":"array","items":{"$ref":"#/components/schemas/ContractSplit"}}},"required":["parties","splits"]}]},"ContractParty":{"type":"object","properties":{"id":{"type":"integer"},"contractId":{"type":"integer"},"organizationId":{"type":"string"},"userId":{"type":["string","null"],"description":"Org member who signs. Null for external parties, which carry `email` instead."},"email":{"type":["string","null"]},"isExternal":{"type":"boolean"},"role":{"type":"string","enum":["artist","producer","songwriter","publisher","label","manager","distributor","engineer","featured_artist","session_musician","other"]},"signatureStatus":{"type":"string","enum":["pending","signed","declined"]},"signedAt":{"type":["string","null"],"format":"date-time"},"signatureHash":{"type":["string","null"],"description":"sha256 over the signature image, timestamp, user and contract id."},"signatureGcsKey":{"type":["string","null"]},"signatureImageUrl":{"type":["string","null"],"description":"A short-lived signed GCS URL or a base64 data URL. Presence does NOT imply signed — check `signatureStatus`."},"signatureIpAddress":{"type":["string","null"]},"signatureUserAgent":{"type":["string","null"]},"signatureConsent":{"type":"boolean","description":"Explicit e-signature consent captured at signing (E-SIGN / eIDAS)."}},"required":["id","contractId","organizationId","userId","email","isExternal","role","signatureStatus","signedAt","signatureHash","signatureGcsKey","signatureImageUrl","signatureIpAddress","signatureUserAgent","signatureConsent"]},"ContractWithRelations":{"allOf":[{"$ref":"#/components/schemas/Contract"},{"type":"object","properties":{"parties":{"type":"array","items":{"$ref":"#/components/schemas/ContractParty"}},"splits":{"type":"array","items":{"$ref":"#/components/schemas/ContractSplit"}}},"required":["parties","splits"]}]},"SigningLinkItem":{"type":"object","properties":{"partyId":{"type":"integer","description":"ID of the party slot."},"email":{"type":"string","format":"email","description":"Email address of the external signer."},"role":{"type":"string","description":"Role of the signer party."},"signUrl":{"type":"string","description":"Single-use guest signing URL for this party.","example":"http://localhost:4400/sign/42?token=abc123xyz"}},"required":["partyId","email","role","signUrl"]},"CreateContractFromTemplateResponse":{"type":"object","properties":{"contract":{"$ref":"#/components/schemas/ContractWithRelations"},"signingLinks":{"type":"array","items":{"$ref":"#/components/schemas/SigningLinkItem"},"description":"Signing links generated for external signers."},"primarySignUrl":{"type":["string","null"],"description":"Convenience shortcut: signUrl for the first external signer, if any.","example":"http://localhost:4400/sign/42?token=abc123xyz"}},"required":["contract","signingLinks","primarySignUrl"]},"CreateContractFromTemplateParty":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"Display name for the party.","example":"Spaceship Entertainment"},"role":{"type":"string","enum":["artist","producer","songwriter","publisher","label","manager","distributor","engineer","featured_artist","session_musician","other"],"description":"Role of the party on the contract.","example":"label"},"email":{"type":"string","format":"email","description":"Email for external signer. Required if userId is omitted.","example":"legal@spaceship.com"},"userId":{"type":"string","description":"User ID for internal organization member signer."}},"required":["role"]},"CreateContractFromTemplateSplit":{"type":"object","properties":{"userId":{"type":"string","minLength":1,"description":"User ID of the split beneficiary."},"role":{"type":"string","enum":["artist","producer","songwriter","publisher","label","manager","distributor","other"],"description":"Split role.","example":"label"},"percentage":{"type":"number","minimum":0,"maximum":100,"description":"Share of revenue (0–100).","example":85},"revenueType":{"type":"string","enum":["all","streaming","sync","mechanical","performance","physical","merch","live"],"default":"all","example":"all"},"conditions":{"type":"string","maxLength":1000,"description":"Optional split payout condition."}},"required":["userId","role","percentage"]},"CreateContractFromTemplateRequest":{"type":"object","properties":{"template":{"anyOf":[{"type":"integer"},{"type":"string","minLength":1}],"description":"Template identifier: numeric ID (e.g. 1) or natural slug (\"contractType/jurisdiction\", e.g. \"distribution/ng\").","example":"distribution/ng"},"title":{"type":"string","minLength":1,"maxLength":500,"description":"Contract title. Defaults to the template title if omitted.","example":"Distribution Agreement - Spaceship Records"},"description":{"type":"string","maxLength":2000,"description":"Optional contract description."},"variables":{"type":"object","additionalProperties":{"type":"string"},"default":{},"description":"Key-value map of variable substitutions for {{tokens}} in the template body.","example":{"label_name":"Spaceship Records","distributor_name":"Royalti Distribution Ltd","effective_date":"2026-09-01","exclusivity":"exclusive","territory":"worldwide"}},"parties":{"type":"array","items":{"$ref":"#/components/schemas/CreateContractFromTemplateParty"},"minItems":1,"description":"Parties to include on the contract. External signers with an email receive signing tokens/links."},"splits":{"type":"array","items":{"$ref":"#/components/schemas/CreateContractFromTemplateSplit"},"default":[],"description":"Optional explicit split allocations."},"sendForSignature":{"type":"boolean","default":true,"description":"If true (default), automatically moves contract to pending_signatures and issues guest signing links for external signers.","example":true},"effectiveDate":{"type":"string","format":"date-time","description":"ISO effective date."},"expirationDate":{"type":"string","format":"date-time","description":"ISO expiration date."},"territory":{"type":"array","items":{"type":"string"},"description":"Territory coverage."},"contractValue":{"type":"number","description":"Total contract value."},"advanceAmount":{"type":"number","description":"Advance amount paid against royalties."},"royaltyRate":{"type":"number","description":"Royalty rate as a percentage."},"exclusivity":{"type":"boolean","default":false,"description":"Exclusivity flag."}},"required":["template","parties"]},"ContractDetail":{"allOf":[{"$ref":"#/components/schemas/Contract"},{"type":"object","properties":{"parties":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/ContractParty"},{"type":"object","properties":{"name":{"type":["string","null"],"description":"Resolved member display name."},"image":{"type":["string","null"],"description":"Resolved member avatar URL."}},"required":["name","image"]}]}},"splits":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/ContractSplit"},{"type":"object","properties":{"name":{"type":["string","null"]}},"required":["name"]}]}},"template":{"description":"The source template row, in full. Modelled by the templates module; treated as opaque here."},"obligations":{"type":"array","items":{},"description":"Non-deleted obligations, in full. Modelled by the obligations module; treated as opaque here."},"activeWorkflowId":{"type":["string","null"],"description":"Id of the most recent approval workflow, as a STRING, for deep-linking to /approvals/:id. Null when the contract has never been submitted."}},"required":["parties","splits","obligations","activeWorkflowId"]}]},"RemindSignersRequest":{"type":"object","properties":{"userId":{"type":"string","description":"Remind only this member. Omit to remind every pending party."}}},"HashResult":{"type":"object","properties":{"hash":{"type":"string"},"algorithm":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"inputSummary":{"type":"string"}},"required":["hash","algorithm","timestamp","inputSummary"]},"ContractHash":{"type":"object","properties":{"id":{"type":"integer"},"contractId":{"type":"integer"},"hash":{"type":"string"},"algorithm":{"type":"string"},"version":{"type":"integer","description":"Contract version the hash was taken over."},"hashedAt":{"type":"string","format":"date-time"},"hashedById":{"type":"string"},"trigger":{"type":"string","enum":["signed","executed","manual","version_snapshot"]},"previousHash":{"type":["string","null"]},"inputSummary":{"type":["string","null"]}},"required":["id","contractId","hash","algorithm","version","hashedAt","hashedById","trigger","previousHash","inputSummary"]},"ContractVersion":{"type":"object","properties":{"id":{"type":"integer"},"contractId":{"type":"integer"},"versionNumber":{"type":"integer"},"content":{"description":"Tiptap document JSON as of this version."},"splits":{},"changedById":{"type":"string"},"summary":{"type":["string","null"]},"changeType":{"type":["string","null"],"enum":["content_edit","party_added","party_removed","split_modified","terms_updated","status_changed","ai_rights_updated","ai_proposed","imported"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","contractId","versionNumber","changedById","summary","changeType","createdAt"]},"ContractTemplateVersion":{"type":"object","properties":{"contractId":{"type":"integer","example":12},"templateId":{"type":["integer","null"],"example":5},"templateVersion":{"type":["integer","null"],"example":1},"latestTemplateVersion":{"type":["integer","null"],"example":2},"isLatestVersion":{"type":"boolean","example":false},"termsPinnedAtCreation":{"type":"boolean","description":"True per Rule D-13: terms in force when created govern permanently.","example":true},"isExcludedByDistinctAgreement":{"type":"boolean","description":"True if a distinct signed agreement overrides default terms (Rule D-13).","example":false},"reconsentRequired":{"type":"boolean","description":"True if contract is on an older version AND not excluded by a distinct agreement.","example":true},"ruleSummary":{"type":"string","description":"Human-readable explanation based on Founder Decision D-13."}},"required":["contractId","templateId","templateVersion","latestTemplateVersion","isLatestVersion","termsPinnedAtCreation","isExcludedByDistinctAgreement","reconsentRequired","ruleSummary"]},"ActivityEntry":{"type":"object","properties":{"id":{"type":"string","description":"Audit-log id, as a string."},"action":{"type":"string"},"actorName":{"type":"string"},"description":{"type":"string","description":"Pre-rendered human sentence for the feed."},"createdAt":{"type":"string","format":"date-time"},"metadata":{}},"required":["id","action","actorName","description","createdAt"]},"AiFinding":{"type":"object","properties":{"id":{"type":"string","description":"cuid2, not a serial integer."},"engineType":{"type":"string","description":"ai_rights | pro_mechanical | risk | consistency"},"severity":{"type":["string","null"],"description":"low | medium | high. Null when the engine emits no severity."},"description":{"type":"string"},"clauseRef":{"type":["string","null"],"example":"Section 4.2"},"confidenceScore":{"type":["number","null"],"description":"Model confidence, 0..1."},"runAt":{"type":"string","format":"date-time"}},"required":["id","engineType","severity","description","clauseRef","confidenceScore","runAt"]},"ContractComment":{"type":"object","properties":{"id":{"type":"integer"},"contractId":{"type":"integer"},"clauseAnchor":{"type":["string","null"],"description":"Editor anchor tying the thread to a clause. Null for contract-level comments."},"parentId":{"type":["integer","null"],"description":"Set on replies. Threading is flat — a reply can only attach to a top-level comment."},"authorId":{"type":"string"},"authorName":{"type":"string","description":"Resolved display name, or 'Unknown'."},"body":{"type":"string"},"visibility":{"type":"string","enum":["internal","external"]},"resolvedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","contractId","clauseAnchor","parentId","authorId","authorName","body","visibility","resolvedAt","createdAt","updatedAt"]},"ContractImport":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string","enum":["uploaded","parsing","parsed","extracting","ready_for_review","confirmed","failed"],"description":"Pipeline lifecycle: uploaded → parsing → parsed → extracting → ready_for_review → confirmed. Any stage may go to `failed`, which is retryable within a bounded count."},"mediaId":{"type":"integer","description":"The uploaded source document, kept as provenance."},"organizationId":{"type":"string"},"uploadedById":{"type":"string"},"parsedContent":{"description":"Parse-stage output: the Tiptap document, and the SOLE owner of the contract body — confirm copies it verbatim. Omitted while the import is still churning or has failed."},"extraction":{"description":"Extract-stage output: field detections only, never the body. Omitted while the import is still churning or has failed."},"contractId":{"type":["integer","null"],"description":"Set at confirm, closing the provenance loop from media to contract."},"error":{"type":["string","null"]},"retryCount":{"type":"integer"},"deletedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","status","mediaId","organizationId","uploadedById","contractId","error","retryCount","deletedAt","createdAt","updatedAt"]},"Template":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"description":{"type":["string","null"]},"contractType":{"type":["string","null"],"enum":["publishing","master_recording","sync_license","producer","distribution","work_for_hire","cowriting","three_sixty","management","licensing","other","platform_order_form","master_supply_agreement"],"description":"Nullable: a template need not commit to a contract type."},"category":{"type":["string","null"]},"jurisdiction":{"type":["string","null"],"enum":["ng","us","uk","generic"],"description":"(contractType, jurisdiction) is the natural key for the seeded library. Nullable for user-authored templates."},"reviewStatus":{"type":"string","enum":["draft","in_review","changes_requested","approved"],"description":"Counsel-review gate. Defaults to `draft` — only `approved` is production-eligible."},"reviewedBy":{"type":["string","null"]},"reviewedAt":{"type":["string","null"],"format":"date-time"},"reviewNote":{"type":["string","null"]},"content":{"description":"Tiptap document JSON, with `{{token}}` variables left in place."},"isPublic":{"type":"boolean","description":"Published to the cross-tenant community library. Publishing requires the `templates:publish` permission AND explicit confirmation."},"userId":{"type":"string"},"organizationId":{"type":"string"},"tags":{"type":["array","null"],"items":{"type":"string"}},"defaultSplits":{"description":"Role-only split rows. They carry no userId at seed time — POST /templates/{id}/use assigns one per row."},"variables":{"type":"array","items":{"type":"string"},"description":"Token names extracted from `content` on save. Re-derived on every content edit."},"version":{"type":"integer","description":"Version identifier of the template. Immutable once contracts reference it."},"isLocked":{"type":"boolean","description":"True if at least one contract references this template version."},"usageCount":{"type":"integer"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"deletedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","title","description","contractType","category","jurisdiction","reviewStatus","reviewedBy","reviewedAt","reviewNote","isPublic","userId","organizationId","tags","variables","version","usageCount","lastUsedAt","deletedAt","createdAt","updatedAt"]},"TemplateListItem":{"allOf":[{"$ref":"#/components/schemas/Template"},{"type":"object","properties":{"authorName":{"type":["string","null"],"description":"Resolved author display name."},"isOwnTemplate":{"type":"boolean","description":"True when the caller's org owns the template or the caller authored it. False for a public template from another tenant, which is read-only."}},"required":["authorName","isOwnTemplate"]}]},"UseTemplateRequest":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":500,"description":"Title for the new contract."},"variables":{"type":"object","additionalProperties":{"type":"string"},"description":"Values for the template's `{{token}}` variables, keyed by token name."},"splitAssignees":{"type":"object","additionalProperties":{"type":"string"},"description":"A userId per default-split row, keyed by the row INDEX as a string (\"0\", \"1\", …) so two rows sharing a role stay distinguishable. Rows left unassigned are omitted from the new contract, not defaulted."}},"required":["title"]},"ReviewTemplateRequest":{"type":"object","properties":{"reviewStatus":{"type":"string","enum":["draft","in_review","changes_requested","approved"],"description":"Only `approved` is production-eligible."},"note":{"type":"string","maxLength":2000,"description":"Reviewer note, e.g. the reason for `changes_requested`."}},"required":["reviewStatus"]},"Obligation":{"type":"object","properties":{"id":{"type":"integer"},"contractId":{"type":"integer"},"organizationId":{"type":"string"},"title":{"type":"string"},"description":{"type":["string","null"]},"obligationType":{"type":"string","enum":["delivery","option","advance_payment","royalty_payment","reversion","exclusivity_expiration","renewal","audit","reporting","other"]},"status":{"type":"string","enum":["pending","in_progress","completed","overdue","waived"]},"assignedToId":{"type":"string"},"dueDate":{"type":"string","format":"date-time"},"completedAt":{"type":["string","null"],"description":"Stamped automatically when `status` is set to `completed`.","format":"date-time"},"amount":{"type":["string","null"],"description":"Monetary amount owed, where the obligation is a payment. Decimal string, not a number."},"currency":{"type":["string","null"]},"reminderSent":{"type":"boolean"},"deletedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","contractId","organizationId","title","description","obligationType","status","assignedToId","dueDate","completedAt","amount","currency","reminderSent","deletedAt","createdAt","updatedAt"]},"ObligationWithRelations":{"allOf":[{"$ref":"#/components/schemas/Obligation"},{"type":"object","properties":{"contract":{"anyOf":[{"$ref":"#/components/schemas/Contract"},{"type":"null"},{"type":"null"}]},"assignedTo":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]}},"required":["id","name"]},{"type":"null"},{"type":"null"}],"description":"Resolved assignee. `name` is null when the user row is missing."}},"required":["contract","assignedTo"]}]},"UserProfile":{"type":"object","properties":{"id":{"type":"integer"},"userId":{"type":"string"},"role":{"type":"string","enum":["admin","artist","manager","lawyer","producer","publisher","label"],"description":"LEGACY global professional role. RBAC canonicalises on the per-workspace role instead — prefer `activeRole` from GET /users/me."},"phone":{"type":["string","null"]},"company":{"type":["string","null"]},"bio":{"type":["string","null"]},"street":{"type":["string","null"]},"city":{"type":["string","null"]},"state":{"type":["string","null"]},"postalCode":{"type":["string","null"]},"country":{"type":["string","null"]},"profilePictureId":{"type":["integer","null"]},"socialMedia":{"type":["array","null"],"items":{"type":"object","properties":{"platform":{"type":"string"},"url":{"type":"string"}},"required":["platform","url"]}},"royaltiUserId":{"type":["string","null"]},"onboarding":{"type":["object","null"],"properties":{"completedSteps":{"type":"array","items":{"type":"string"}},"completedAt":{"type":"string"},"skippedAt":{"type":"string"}}}},"required":["id","userId","role","phone","company","bio","street","city","state","postalCode","country","profilePictureId","socialMedia","royaltiUserId","onboarding"]},"Me":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"emailVerified":{"type":"boolean"},"image":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"role":{"type":["string","null"],"description":"Better Auth admin-plugin role. Not the professional role."},"banned":{"type":["boolean","null"]},"banReason":{"type":["string","null"]},"banExpires":{"type":["string","null"],"format":"date-time"},"twoFactorEnabled":{"type":["boolean","null"]},"profile":{"anyOf":[{"$ref":"#/components/schemas/UserProfile"},{"type":"null"},{"type":"null"}],"description":"The profile row. On this endpoint it also carries a resolved `profilePicture` media row, which the schema does not model."},"activeOrganization":{"description":"The full active organization row, or null when no workspace is active."},"activeRole":{"type":"string","description":"The caller's professional role IN the active workspace. This is the RBAC-canonical role to gate UI on — not `profile.role`. A plain string, not an enum, for the same reason as `TeamMember.professionalRole`: it is resolved from an unconstrained `text` column.","example":"artist"}},"required":["id","name","email","emailVerified","createdAt","updatedAt","profile","activeRole"]},"OrganizationSummary":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"logo":{"type":["string","null"]},"role":{"type":"string","description":"The CALLER's access role in this org (owner / admin / member)."},"memberCount":{"type":"integer"}},"required":["id","name","logo","role","memberCount"]},"PendingInvitation":{"type":"object","properties":{"id":{"type":"string"},"orgName":{"type":"string","description":"Falls back to 'an organization' when unresolvable."},"role":{"type":"string","description":"Access role the invite grants."},"invitedBy":{"type":"string","description":"Inviter's display name. Absent when unresolvable."},"invitedAt":{"type":"string","format":"date-time"}},"required":["id","orgName","role","invitedAt"]},"ProfilePhotoUpload":{"type":"object","properties":{"file":{"type":"string","format":"binary"}}},"NotificationPreferences":{"type":"object","properties":{"email":{"type":"object","properties":{"digest":{"type":"boolean"},"contractUpdates":{"type":"boolean"},"obligations":{"type":"boolean"},"approvals":{"type":"boolean"}},"required":["digest","contractUpdates","obligations","approvals"]},"inApp":{"type":"object","properties":{"all":{"type":"boolean"}},"required":["all"]}},"required":["email","inApp"]},"UpdateNotificationPreferences":{"type":"object","properties":{"email":{"type":"object","properties":{"digest":{"type":"boolean"},"contractUpdates":{"type":"boolean"},"obligations":{"type":"boolean"},"approvals":{"type":"boolean"}}},"inApp":{"type":"object","properties":{"all":{"type":"boolean"}}}}},"SignOutAllResult":{"type":"object","properties":{}},"SignOutAllRequest":{"type":"object","properties":{"password":{"type":"string"}}},"SessionSummary":{"type":"object","properties":{"id":{"type":"string"},"token":{"type":"string","description":"Session token — pass it to DELETE /users/me/sessions/{token} to revoke. Treat as a credential."},"ipAddress":{"type":["string","null"]},"userAgent":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"isCurrent":{"type":"boolean","description":"True for the caller's own session, which cannot revoke itself."}},"required":["id","token","ipAddress","userAgent","createdAt","expiresAt","isCurrent"]},"TeamMember":{"type":"object","properties":{"id":{"type":"string","description":"User id for an active member; INVITATION id for a pending one."},"role":{"type":"string","description":"Access role (owner / admin / member)."},"professionalRole":{"type":"string","description":"Per-workspace RBAC role. Defaults to `artist` when unset. Typed as a plain string, not an enum, because the underlying column is unconstrained `text` — writes are validated against the role list but historical rows are not, so a reader must not assume membership.","example":"artist"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"image":{"type":["string","null"]},"status":{"type":"string","enum":["active","pending"]}},"required":["id","role","professionalRole","email","name","image","status"]},"SetMemberRoleRequest":{"type":"object","properties":{"professionalRole":{"type":"string","enum":["admin","artist","manager","lawyer","producer","publisher","label"]}},"required":["professionalRole"]},"InviteMemberRequest":{"type":"object","properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["admin","member"],"description":"Access role. `owner` is deliberately not invitable."},"professionalRole":{"type":"string","enum":["admin","artist","manager","lawyer","producer","publisher","label"],"description":"Per-workspace RBAC role granted on acceptance. Defaults to `artist`."}},"required":["email","role"]},"LeaveOrganizationRequest":{"type":"object","properties":{"organizationId":{"type":"string","description":"Defaults to the active workspace."}}},"User":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"emailVerified":{"type":"boolean"},"image":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"role":{"type":["string","null"],"description":"Better Auth admin-plugin role ('user' / 'admin'). Not the professional role."},"banned":{"type":["boolean","null"]},"banReason":{"type":["string","null"]},"banExpires":{"type":["string","null"],"format":"date-time"},"twoFactorEnabled":{"type":["boolean","null"]},"notificationPreferences":{"allOf":[{"$ref":"#/components/schemas/NotificationPreferences"},{"type":["object","null"]}]}},"required":["id","name","email","emailVerified","image","createdAt","updatedAt","role","banned","banReason","banExpires","twoFactorEnabled","notificationPreferences"]},"ApiKey":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"keyPrefix":{"type":"string","description":"Display-safe leading fragment. Not a credential.","example":"fr_live_a1b2c3d4"},"scopes":{"type":"array","items":{"type":"string"},"description":"Permission scopes assigned to the key.","example":["contracts:read","contracts:write"]},"expiresAt":{"type":["string","null"],"format":"date-time"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","keyPrefix","scopes","expiresAt","lastUsedAt","createdAt"]},"ApiKeyCreated":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"keyPrefix":{"type":"string","description":"Display-safe leading fragment. Not a credential.","example":"fr_live_a1b2c3d4"},"scopes":{"type":"array","items":{"type":"string"},"description":"Permission scopes assigned to the key.","example":["contracts:read","contracts:write"]},"expiresAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"key":{"type":"string","description":"The full secret, returned EXACTLY ONCE at creation and never retrievable again. Store it immediately.","example":"fr_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}},"required":["id","name","keyPrefix","scopes","expiresAt","createdAt","key"]},"CreateApiKeyRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":128,"description":"Label shown in the UI. Not unique; purely for humans.","example":"CI deploy key"},"scopes":{"type":"array","items":{"type":"string","enum":["contracts:read","contracts:write","templates:read","templates:write","obligations:read","obligations:write","workflows:read","workflows:write","royalti:read","royalti:export","webhooks:read","webhooks:write"]},"default":[],"description":"List of permission scopes granted to this key.","example":["contracts:read","contracts:write"]},"expiresInDays":{"type":["integer","null"],"exclusiveMinimum":0,"description":"Number of days until the key expires. Null or omitted for never.","example":90}},"required":["name"]},"Notification":{"type":"object","properties":{"id":{"type":"integer"},"userId":{"type":"string"},"organizationId":{"type":"string"},"type":{"type":"string","enum":["approval_requested","contract_signed","obligation_due","sync_status","obligation_overdue"]},"title":{"type":"string"},"message":{"type":["string","null"]},"contractId":{"type":["integer","null"]},"read":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","userId","organizationId","type","title","message","contractId","read","createdAt"]},"AiRightsEntry":{"type":"object","properties":{"id":{"type":"integer"},"workTitle":{"type":"string"},"workType":{"type":"string","enum":["sound_recording","composition","lyrics","performance","music_video","album"]},"registeredById":{"type":"string"},"organizationId":{"type":"string"},"contractId":{"type":["integer","null"]},"aiTrainingConsent":{"type":"string","enum":["opted_in","opted_out","conditional"],"description":"The registered consent position for AI training on this work."},"conditions":{"type":["string","null"]},"licensedTo":{"type":["array","null"],"items":{"type":"object","properties":{"entity":{"type":"string"},"scope":{"type":"string"},"startDate":{"type":"string"},"endDate":{"type":"string"}},"required":["entity","scope","startDate","endDate"]}},"complianceNotes":{"type":["string","null"]},"isrc":{"type":["string","null"],"description":"Recording identifier."},"iswc":{"type":["string","null"],"description":"Composition identifier."},"deletedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","workTitle","workType","registeredById","organizationId","contractId","aiTrainingConsent","conditions","licensedTo","complianceNotes","isrc","iswc","deletedAt","createdAt","updatedAt"]},"AiRightsEntryWithContract":{"allOf":[{"$ref":"#/components/schemas/AiRightsEntry"},{"type":"object","properties":{"contract":{"anyOf":[{"$ref":"#/components/schemas/Contract"},{"type":"null"},{"type":"null"}]}},"required":["contract"]}]},"AiDashboardSnapshots":{"type":"object","properties":{"snapshots":{"type":"array","items":{},"description":"Best-effort AI snapshots derived from the contracts table. Findings are not persisted for this view, so an empty list is the normal case, not an error."}},"required":["snapshots"]},"ModelReply":{"type":"object","properties":{},"description":"Model output, passed through as parsed. Its shape is set by the prompt for the endpoint, not by the API, and may change when the prompt does. Do not treat it as a stable contract."},"RejectActionRequest":{"type":"object","properties":{"reason":{"type":"string"}}},"AiAction":{"type":"object","properties":{"id":{"type":"string"},"orgId":{"type":"string"},"contractId":{"type":["integer","null"]},"conversationId":{"type":["string","null"]},"proposedBy":{"type":"string","description":"Model that proposed the action."},"action":{"type":"string","description":"Tool name."},"inputJson":{"type":"string","description":"Tool input, as a JSON STRING."},"undoSnapshotJson":{"type":["string","null"],"description":"The full pre-edit document, as a JSON string. Sensitive — it is why the actions list is scoped to contracts the caller can access."},"status":{"type":"string","description":"proposed | applied | undone | rejected."},"appliedAt":{"type":["string","null"],"format":"date-time"},"undoExpiresAt":{"type":["string","null"],"description":"After this, undo returns 410 and the action is permanent.","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","orgId","contractId","conversationId","proposedBy","action","inputJson","undoSnapshotJson","status","appliedAt","undoExpiresAt","createdAt","updatedAt"]},"AiMessage":{"type":"object","properties":{"id":{"type":"string"},"conversationId":{"type":"string"},"role":{"type":"string"},"content":{"type":"string"},"toolCallsJson":{"type":["string","null"],"description":"Tool calls the model PROPOSED, as a JSON string. Chat never executes them — they are persisted as ai_actions and surfaced as proposals."},"createdAt":{"type":"string","format":"date-time"}},"required":["id","conversationId","role","content","toolCallsJson","createdAt"]},"AiRunStep":{"type":"object","properties":{"id":{"type":"string"},"runId":{"type":"string"},"stepIndex":{"type":"integer"},"description":{"type":"string"},"toolName":{"type":["string","null"]},"toolInputJson":{"type":["string","null"]},"status":{"type":"string"},"resultSummary":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","runId","stepIndex","description","toolName","toolInputJson","status","resultSummary","createdAt"]},"AiRun":{"type":"object","properties":{"id":{"type":"string"},"contractId":{"type":"integer"},"goal":{"type":"string"},"status":{"type":"string","description":"planning | running | paused | completed | aborted | failed."},"autonomyLevel":{"type":"string","description":"observe | propose | confirm | auto. Governs where the run pauses for a human decision."},"plan":{"type":"array","items":{"type":"string"}},"startedAt":{"type":["string","null"],"format":"date-time"},"completedAt":{"type":["string","null"],"format":"date-time"},"steps":{"type":"array","items":{"$ref":"#/components/schemas/AiRunStep"}}},"required":["id","contractId","goal","status","autonomyLevel","plan","startedAt","completedAt","steps"]},"EngineFinding":{"type":"object","properties":{"type":{"type":"string","description":"Finding type. The web UI derives its severity treatment from THIS field, which is why no `severity` key is sent alongside — two sources of truth could disagree."},"clauseRef":{"type":"string"},"description":{"type":"string"}},"required":["type","clauseRef","description"]},"EngineFindings":{"type":"object","properties":{"findings":{"type":"array","items":{"$ref":"#/components/schemas/EngineFinding"}}},"required":["findings"]},"WorkflowContract":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"type":{"type":"string","enum":["publishing","master_recording","sync_license","producer","distribution","work_for_hire","cowriting","three_sixty","management","licensing","other","platform_order_form","master_supply_agreement"],"description":"The contract's `contractType`, renamed to `type` in this projection."},"dealTerms":{"type":"object","properties":{"territory":{"type":["array","null"],"items":{"type":"string"}},"contractValue":{"type":["string","null"],"description":"Decimal string, not a number."},"royaltyRate":{"type":["string","null"],"description":"Decimal string, not a number."},"term":{"type":"null","description":"Always null — reserved, never populated by this endpoint."}},"required":["territory","contractValue","royaltyRate","term"]}},"required":["id","title","type","dealTerms"]},"ApprovalStep":{"type":"object","properties":{"id":{"type":"string","description":"Step id, as a string."},"stepOrder":{"type":"integer","description":"Ascending. Steps are decided in order."},"approverRole":{"type":"string","enum":["manager","lawyer","artist","publisher","label","admin"]},"approverId":{"type":["string","null"],"description":"A specific designated approver. Null means anyone holding `approverRole` may decide the step."},"status":{"type":"string","enum":["pending","approved","rejected","skipped"]},"decidedAt":{"type":["string","null"],"format":"date-time"},"comments":{"type":["string","null"]}},"required":["id","stepOrder","approverRole","approverId","status","decidedAt","comments"]},"UserRef":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["id","name","email"]},"WorkflowListItem":{"type":"object","properties":{"id":{"type":"string","description":"Workflow id, as a string."},"contractId":{"type":"integer"},"contractTitle":{"type":["string","null"]},"contractType":{"type":["string","null"],"enum":["publishing","master_recording","sync_license","producer","distribution","work_for_hire","cowriting","three_sixty","management","licensing","other","platform_order_form","master_supply_agreement"]},"contract":{"anyOf":[{"$ref":"#/components/schemas/WorkflowContract"},{"type":"null"},{"type":"null"}]},"status":{"type":"string","enum":["pending","in_progress","approved","rejected","cancelled"]},"steps":{"type":"array","items":{"$ref":"#/components/schemas/ApprovalStep"}},"currentStepIndex":{"type":"integer","description":"Index into `steps` of the first pending step, or -1 when none remain."},"totalSteps":{"type":"integer"},"submittedBy":{"anyOf":[{"$ref":"#/components/schemas/UserRef"},{"type":"null"},{"type":"null"}]},"submittedAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":["string","null"],"format":"date-time"},"currentStep":{"type":["integer","null"],"description":"`stepOrder` of the current pending step. Null when none remain."},"approverRole":{"type":["string","null"],"enum":["manager","lawyer","artist","publisher","label","admin"],"description":"Role required by the current pending step. Null when none remain."}},"required":["id","contractId","contractTitle","contractType","contract","status","steps","currentStepIndex","totalSteps","submittedBy","submittedAt","createdAt","completedAt","currentStep","approverRole"]},"WorkflowDetail":{"type":"object","properties":{"id":{"type":"string","description":"Workflow id, as a string."},"contractId":{"type":"integer"},"contractTitle":{"type":["string","null"]},"contractType":{"type":["string","null"],"enum":["publishing","master_recording","sync_license","producer","distribution","work_for_hire","cowriting","three_sixty","management","licensing","other","platform_order_form","master_supply_agreement"]},"contract":{"anyOf":[{"$ref":"#/components/schemas/WorkflowContract"},{"type":"null"},{"type":"null"}]},"status":{"type":"string","enum":["pending","in_progress","approved","rejected","cancelled"]},"steps":{"type":"array","items":{"$ref":"#/components/schemas/ApprovalStep"}},"currentStepIndex":{"type":"integer","description":"Index into `steps` of the first pending step, or -1 when none remain."},"totalSteps":{"type":"integer"},"submittedBy":{"anyOf":[{"$ref":"#/components/schemas/UserRef"},{"type":"null"},{"type":"null"}]},"submittedAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","contractId","contractTitle","contractType","contract","status","steps","currentStepIndex","totalSteps","submittedBy","submittedAt","createdAt","completedAt"]},"WebhookSubscription":{"type":"object","properties":{"id":{"type":"integer","example":1},"organizationId":{"type":"string","example":"org_123"},"url":{"type":"string","format":"uri","example":"https://example.com/webhooks"},"description":{"type":["string","null"],"example":"Downstream sync endpoint"},"secret":{"type":"string","example":"whsec_abcdef123456"},"events":{"type":"array","items":{"type":"string"},"example":["contract.signed","contract.voided"]},"isActive":{"type":"boolean","example":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","organizationId","url","description","secret","events","isActive","createdAt","updatedAt"]},"CreateWebhookSubscription":{"type":"object","properties":{"url":{"type":"string","format":"uri","example":"https://example.com/webhooks"},"description":{"type":"string","maxLength":500},"events":{"type":"array","items":{"type":"string"},"default":["*"],"description":"List of event names to subscribe to, or ['*'] for all events.","example":["contract.signed","contract.declined"]},"secret":{"type":"string","minLength":16,"description":"Optional custom secret (at least 16 chars). If omitted, a secure whsec_ secret is generated."}},"required":["url"]},"UpdateWebhookSubscription":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"description":{"type":["string","null"],"maxLength":500},"events":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"}}},"RotateSecretResponse":{"type":"object","properties":{"id":{"type":"integer","example":1},"secret":{"type":"string","example":"whsec_new_secret_12345"}},"required":["id","secret"]},"TestDeliveryResponse":{"type":"object","properties":{"deliveryId":{"type":"integer","example":1},"subscriptionId":{"type":"integer","example":1},"url":{"type":"string","example":"https://example.com/webhooks"},"status":{"type":"string","enum":["success","failed"],"example":"success"},"responseStatus":{"type":["integer","null"],"example":200},"durationMs":{"type":"integer","example":85},"error":{"type":"string"}},"required":["deliveryId","subscriptionId","url","status","responseStatus","durationMs"]},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"integer","example":1},"subscriptionId":{"type":"integer","example":1},"organizationId":{"type":"string"},"eventId":{"type":"string","example":"evt_123456"},"event":{"type":"string","example":"contract.signed"},"payload":{"type":"object","additionalProperties":{},"description":"The delivered event payload envelope."},"url":{"type":"string","example":"https://example.com/webhooks"},"responseStatus":{"type":["integer","null"],"example":200},"responseBody":{"type":["string","null"],"example":"{\"ok\":true}"},"errorMessage":{"type":["string","null"]},"durationMs":{"type":["integer","null"],"example":124},"attempt":{"type":"integer","example":1},"status":{"type":"string","enum":["success","failed"],"example":"success"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","subscriptionId","organizationId","eventId","event","payload","url","responseStatus","responseBody","errorMessage","durationMs","attempt","status","createdAt"]},"RoyaltiSyncState":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"syncStatus":{"type":"string","description":"Per-contract sync state: `error`, `synced`, or `pending`."},"syncError":{"type":["string","null"],"description":"The recorded failure message. Cleared when the contract is re-queued."},"lastSynced":{"type":["string","null"],"format":"date-time"},"revenue":{"type":"number"},"linked":{"type":"boolean","description":"Whether an ISRC has been attached. Distinguishes \"needs an asset\" from \"linked, pending sync\"."},"assetIsrc":{"type":["string","null"]}},"required":["id","title","syncStatus","syncError","lastSynced","revenue","linked","assetIsrc"]},"RoyaltiStatus":{"type":"object","properties":{"connected":{"type":"boolean","description":"True when a live (non-disconnected) connection row exists."},"workspaceName":{"type":["string","null"]},"workspaceUid":{"type":["string","null"]},"syncedContracts":{"type":"integer"},"totalRevenue":{"type":"number"},"revenueByContract":{"type":"object","additionalProperties":{"type":"number"},"description":"Revenue keyed by contract id as a STRING."},"monthlyRevenue":{},"revenueUpdatedAt":{},"pendingSyncs":{"type":"integer"},"erroredSyncs":{"type":"integer"},"lastSyncedAt":{"type":["string","null"],"format":"date-time"},"contracts":{"type":"array","items":{"$ref":"#/components/schemas/RoyaltiSyncState"},"description":"EVERY syncable contract with its real state — not only the synced ones."}},"required":["connected","workspaceName","workspaceUid","syncedContracts","totalRevenue","revenueByContract","pendingSyncs","erroredSyncs","lastSyncedAt","contracts"]},"RoyaltiEvent":{"type":"object","properties":{"id":{"type":"string"},"user":{"type":"string","description":"Actor display name, or 'Royalti.io' for changes the integration made itself."},"description":{"type":"string"},"timeAgo":{"type":"string","description":"Pre-formatted relative time, e.g. \"2 hours ago\"."},"iconType":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"contractId":{"type":"string"}},"required":["id","user","description","timeAgo","iconType","createdAt"]},"FairnessResult":{"type":"object","properties":{"contractId":{"type":"integer"},"verdict":{"type":"string","enum":["fair","below_market","above_market","unknown"]},"deltaPercent":{"type":"number","description":"Aggregate signed delta from market across benchmarked roles. Positive = above."},"details":{"type":"array","items":{"type":"object","properties":{"role":{"type":"string"},"contractPercentage":{"type":"number"},"benchmarkPercentage":{"type":["number","null"]},"deltaPercent":{"type":["number","null"]}},"required":["role","contractPercentage","benchmarkPercentage","deltaPercent"]}},"benchmarkSource":{"type":"string","description":"Which curated benchmark set the comparison came from."},"message":{"type":"string","description":"Set when the analysis could not be grounded — e.g. the contract has no splits. Present means the verdict is not evidence."}},"required":["contractId","verdict","deltaPercent","details","benchmarkSource"]},"SplitsExport":{"type":"object","properties":{"contractId":{"type":"integer"},"splits":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"role":{"type":"string","enum":["artist","producer","songwriter","publisher","label","manager","distributor","other"]},"percentage":{"type":"number","description":"A NUMBER here, unlike the decimal string on the contract itself."},"revenueType":{"type":"string","enum":["all","streaming","sync","mechanical","performance","physical","merch","live"]},"conditions":{"type":["string","null"]}},"required":["userId","role","percentage","revenueType","conditions"]}},"parties":{"type":"array","items":{"type":"object","properties":{"userId":{"type":["string","null"]},"role":{"type":"string","enum":["artist","producer","songwriter","publisher","label","manager","distributor","engineer","featured_artist","session_musician","other"]},"signatureStatus":{"type":"string","enum":["pending","signed","declined"]}},"required":["userId","role","signatureStatus"]}}},"required":["contractId","splits","parties"]},"RoyaltiContractExport":{"type":"object","properties":{}},"DriveFile":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":["integer","null"],"description":"Bytes. Null for Google-native documents, which have no fixed size until exported."},"modifiedTime":{"type":["string","null"]}},"required":["id","name","mimeType","size","modifiedTime"]},"DriveImportRequest":{"type":"object","properties":{"fileId":{"type":"string","minLength":10,"maxLength":200,"pattern":"^[\\w-]+$","description":"Google Drive file id."}},"required":["fileId"]},"AdminUser":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","description":"The member's per-workspace professional role, falling back to the legacy global profile role and then to `artist`. A plain string: the underlying column is unconstrained `text`.","example":"artist"},"joinedAt":{"type":"string","format":"date-time","description":"When the USER ACCOUNT was created, not when they joined this organization."},"contractCount":{"type":"integer","description":"Contracts this user created in this organization."}},"required":["id","name","email","role","joinedAt","contractCount"]},"AdminMetrics":{"type":"object","properties":{"totalContracts":{"type":"integer"},"activeContracts":{"type":"integer"},"totalUsers":{"type":"integer"},"storageUsedMB":{"type":"integer","description":"NOT measured. A flat estimate of ~50 KB per contract — it does not reflect real object storage usage."},"contractsByMonth":{"type":"array","items":{"type":"object","properties":{"month":{"type":"string","example":"2026-08"},"count":{"type":"integer"}},"required":["month","count"]},"description":"A full 12-month series, oldest first, with zero-filled gaps."}},"required":["totalContracts","activeContracts","totalUsers","storageUsedMB","contractsByMonth"]},"AdminActivityEntry":{"type":"object","properties":{"id":{"type":"string"},"action":{"type":"string"},"actorName":{"type":"string"},"contractId":{"type":["string","null"]},"contractTitle":{"type":["string","null"]},"description":{"type":"string","description":"Pre-rendered sentence for the feed."},"createdAt":{"type":"string","format":"date-time"},"metadata":{}},"required":["id","action","actorName","contractId","contractTitle","description","createdAt"]},"SystemHealth":{"type":"object","properties":{"status":{"type":"string","enum":["healthy"]},"database":{"type":"string","enum":["connected"]},"timestamp":{"type":"string","format":"date-time"}},"required":["status","database","timestamp"]},"SearchResults":{"type":"object","properties":{"query":{"type":"string"},"contracts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"contractType":{"type":"string"},"status":{"type":"string"}},"required":["id","title","contractType","status"]}},"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"contractType":{"type":["string","null"]}},"required":["id","title","contractType"]}},"people":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","description":"Access role in the active organization."}},"required":["id","name","email","role"]}}},"required":["query","contracts","templates","people"]},"DashboardStats":{"type":"object","properties":{"totalContracts":{"type":"integer"},"activeContracts":{"type":"integer"},"newThisMonth":{"type":"integer"},"needsReview":{"type":"integer"},"awaitingSignatures":{"type":"integer"},"draftContracts":{"type":"integer"},"obligationsOverdue":{"type":"integer"},"obligationsDueSoon":{"type":"integer"},"obligationsDue":{"type":"integer","description":"Convenience sum of `obligationsOverdue` and `obligationsDueSoon`."}},"required":["totalContracts","activeContracts","newThisMonth","needsReview","awaitingSignatures","draftContracts","obligationsOverdue","obligationsDueSoon","obligationsDue"]},"RecentContract":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"status":{"type":"string"},"parties":{"type":"string","description":"Up to three party names, already joined into one comma-separated STRING for display. Not an array."},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","title","status","parties","updatedAt"]},"PendingObligation":{"type":"object","properties":{"id":{"type":"string"},"category":{"type":"string","enum":["payment","delivery","filing","other"],"description":"Coarse bucket derived from the obligation type, driving the widget icon. Kept stable as obligation types evolve."},"title":{"type":"string"},"contract":{"type":"string","description":"Parent contract title, or 'Contract'."},"dueDate":{"type":"string","format":"date-time"},"daysUntilDue":{"type":"integer","description":"Negative when overdue."},"status":{"type":"string","enum":["overdue","due_soon"]},"amount":{"type":["number","null"],"description":"A NUMBER here, unlike the decimal string on the obligation itself — this is display data. Null when absent or non-finite."},"link":{"type":"string"}},"required":["id","category","title","contract","dueDate","daysUntilDue","status","amount","link"]},"ActivityItem":{"type":"object","properties":{"id":{"type":"string"},"message":{"type":"string","description":"Pre-rendered sentence for the feed."},"timestamp":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["info","success","warning"]},"icon":{"type":"string","enum":["create","review","sign","ai"],"description":"Widget icon hint. Absent for entries that carry no specific icon."}},"required":["id","message","timestamp","type"]},"DashboardSummary":{"type":"object","properties":{"stats":{"$ref":"#/components/schemas/DashboardStats"},"recentDraft":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"progress":{"type":"integer","description":"Completion estimate, 5–95. A crude heuristic over content length, not a real measure of readiness."}},"required":["id","title","progress"]},{"type":"null"},{"type":"null"}],"description":"A draft to resume, or null when there is none."},"recentContracts":{"type":"array","items":{"$ref":"#/components/schemas/RecentContract"}},"pendingObligations":{"type":"array","items":{"$ref":"#/components/schemas/PendingObligation"}},"pendingObligationsTotal":{"type":"integer"},"recentActivity":{"type":"array","items":{"$ref":"#/components/schemas/ActivityItem"}}},"required":["stats","recentDraft","recentContracts","pendingObligations","pendingObligationsTotal","recentActivity"]}},"parameters":{}},"paths":{"/health":{"get":{"tags":["Health"],"operationId":"getHealth","security":[],"summary":"Liveness and database connectivity","description":"Unauthenticated. Returns 200 when the database answers `SELECT 1`, 503 otherwise. Both responses carry `release`, so an outage can be attributed to a build.","responses":{"200":{"description":"Service healthy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"503":{"description":"Database unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/public/sign/contracts/{id}/sign-context":{"get":{"tags":["Public signing"],"operationId":"getPublicSignContext","security":[],"summary":"Fetch the guest signing context","description":"Authenticated by the single-use signing token alone — no session, no API key. The token must be live, scoped to this exact contract, and point at a party on it.\n\nReturns just enough for an external party to review and sign: the contract body, the deal terms, their own role, and the other parties’ progress as role + status only.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"},{"schema":{"type":"string","description":"The single-use signing token."},"required":false,"name":"token","in":"query"}],"responses":{"200":{"description":"The signing context.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/SignContext"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing, expired, or wrongly-scoped signing token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or the token points at a party that is not on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/sign/contracts/{id}/sign":{"post":{"tags":["Public signing"],"operationId":"publicSignContract","security":[],"summary":"Sign a contract as an external party","description":"Authenticated by the single-use signing token alone — no session, no API key. The token must be live, scoped to this exact contract, and point at a party on it.\n\nThe token is claimed ATOMICALLY before signing, which is what makes it genuinely single-use: on concurrent requests the loser is rejected rather than both signing. If the signature itself does not go through, the claim is released so a real retry still works.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"signatureDataUrl":{"type":"string","minLength":1,"maxLength":3500000},"consent":{"type":"boolean","enum":[true]},"token":{"type":"string","minLength":1}},"required":["signatureDataUrl","consent","token"]}}}},"responses":{"200":{"description":"Signature recorded.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/SignatureResult"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Wrong contract status, already signed, or an unusable signature image.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid token, or one that has already been used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The token does not authorise signing for this party.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or signing party not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/sign/contracts/{id}/decline":{"post":{"tags":["Public signing"],"operationId":"publicDeclineContract","security":[],"summary":"Decline to sign a contract as an external party","description":"Authenticated by the single-use signing token alone — no session, no API key. The token must be live, scoped to this exact contract, and point at a party on it.\n\nMarks the external party as declined and records an audit trail entry. Claims the token so it cannot be reused.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1,"description":"The single-use signing token."},"reason":{"type":"string","maxLength":500,"description":"Optional reason for declining to sign."}},"required":["token"]}}}},"responses":{"200":{"description":"Decline recorded.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"declined":{"type":"boolean","enum":[true]}},"required":["declined"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Invalid token, or one that has already been used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or signing party not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts":{"get":{"tags":["Contracts"],"operationId":"listContracts","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List contracts","parameters":[{"schema":{"type":["integer","null"]},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string"},"required":false,"name":"contractType","in":"query"}],"responses":{"200":{"description":"A page of contracts.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/ContractListItem"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Contracts"],"operationId":"createContract","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Create a contract","description":"Creates the contract with its parties and splits in one transaction. Parties carry either `userId` (org member) or `email` (external signer). Supplying `templateId` also bumps that template's usage counters.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":500},"description":{"type":"string","maxLength":2000},"contractType":{"type":"string","enum":["publishing","master_recording","sync_license","producer","distribution","work_for_hire","cowriting","three_sixty","management","licensing","other","platform_order_form","master_supply_agreement"]},"content":{},"parties":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["artist","producer","songwriter","publisher","label","manager","distributor","engineer","featured_artist","session_musician","other"]}},"required":["role"]},"default":[]},"splits":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"role":{"type":"string","enum":["artist","producer","songwriter","publisher","label","manager","distributor","other"]},"percentage":{"type":"number","minimum":0,"maximum":100},"revenueType":{"type":"string","enum":["all","streaming","sync","mechanical","performance","physical","merch","live"],"default":"all"},"conditions":{"type":"string"}},"required":["userId","role","percentage"]},"default":[]},"effectiveDate":{"type":"string","format":"date-time"},"expirationDate":{"type":"string","format":"date-time"},"territory":{"type":"array","items":{"type":"string","enum":["worldwide","us","ca","uk","eu","latam","apac","africa","middle_east","ad","ae","af","ag","ai","al","am","ao","aq","ar","as","at","au","aw","ax","az","ba","bb","bd","be","bf","bg","bh","bi","bj","bl","bm","bn","bo","bq","br","bs","bt","bv","bw","by","bz","cc","cd","cf","cg","ch","ci","ck","cl","cm","cn","co","cr","cu","cv","cw","cx","cy","cz","de","dj","dk","dm","do","dz","ec","ee","eg","eh","er","es","et","fi","fj","fk","fm","fo","fr","ga","gb","gd","ge","gf","gg","gh","gi","gl","gm","gn","gp","gq","gr","gs","gt","gu","gw","gy","hk","hm","hn","hr","ht","hu","id","ie","il","im","in","io","iq","ir","is","it","je","jm","jo","jp","ke","kg","kh","ki","km","kn","kp","kr","kw","ky","kz","la","lb","lc","li","lk","lr","ls","lt","lu","lv","ly","ma","mc","md","me","mf","mg","mh","mk","ml","mm","mn","mo","mp","mq","mr","ms","mt","mu","mv","mw","mx","my","mz","na","nc","ne","nf","ng","ni","nl","no","np","nr","nu","nz","om","pa","pe","pf","pg","ph","pk","pl","pm","pn","pr","ps","pt","pw","py","qa","re","ro","rs","ru","rw","sa","sb","sc","sd","se","sg","sh","si","sj","sk","sl","sm","sn","so","sr","ss","st","sv","sx","sy","sz","tc","td","tf","tg","th","tj","tk","tl","tm","tn","to","tr","tt","tv","tw","tz","ua","ug","um","uy","uz","va","vc","ve","vg","vi","vn","vu","wf","ws","xk","ye","yt","za","zm","zw"]}},"contractValue":{"type":"number","minimum":0},"advanceAmount":{"type":"number","minimum":0},"royaltyRate":{"type":"number","minimum":0,"maximum":100},"exclusivity":{"type":"boolean"},"optionPeriods":{"type":"integer","minimum":0},"aiTrainingOptIn":{"type":"string","enum":["not_specified","opted_in","opted_out"]},"aiTrainingNotes":{"type":"string"},"templateId":{"type":"integer","exclusiveMinimum":0}},"required":["title","contractType"]}}}},"responses":{"201":{"description":"The created contract, with relations.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ContractWithRelations"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/from-template":{"post":{"tags":["Contracts"],"operationId":"createContractFromTemplate","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Create a contract from a template (API-first)","description":"Machine-to-machine endpoint to instantiate a contract from a template (by ID or natural slug like \"distribution/ng\"), substitute variables, configure signers, and receive signing links without a browser session. Requires `contracts:write` scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateContractFromTemplateRequest"}}}},"responses":{"201":{"description":"The created contract with generated signing links.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/CreateContractFromTemplateResponse"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body or template not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Template not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/bulk":{"post":{"tags":["Contracts"],"operationId":"bulkContractAction","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Bulk delete or change status","description":"Applies an action across many contracts. Deletion only affects contracts in `draft`; anything else is counted in `skipped` rather than failing the call. Status changes require an admin.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"},"minItems":1},"action":{"type":"string","enum":["delete","change_status"]},"status":{"type":"string","enum":["draft","pending_review","approved","pending_signatures","active","expired","terminated"]}},"required":["ids","action"]}}}},"responses":{"200":{"description":"Counts of what changed.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"affected":{"type":"integer","description":"How many rows actually changed."},"skipped":{"type":"integer","description":"Delete only: selected contracts left untouched because they were not drafts. Absent for other actions."}},"required":["affected"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Unknown action, or `change_status` without a status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/{id}":{"get":{"tags":["Contracts"],"operationId":"getContract","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Get a contract","description":"Returns the contract with parties and splits hydrated with display names, the source template, non-deleted obligations, and the newest approval workflow id.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"The contract.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ContractDetail"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Contracts"],"operationId":"updateContract","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Update a draft contract","description":"Creator-only, and only while the contract is in `draft`. Supplying `content` snapshots the previous body as a new version and increments `version`. Supplying `parties` or `splits` REPLACES the whole set.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":500},"description":{"type":"string","maxLength":2000},"contractType":{"type":"string","enum":["publishing","master_recording","sync_license","producer","distribution","work_for_hire","cowriting","three_sixty","management","licensing","other","platform_order_form","master_supply_agreement"]},"content":{},"parties":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["artist","producer","songwriter","publisher","label","manager","distributor","engineer","featured_artist","session_musician","other"]}},"required":["role"]},"default":[]},"splits":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"role":{"type":"string","enum":["artist","producer","songwriter","publisher","label","manager","distributor","other"]},"percentage":{"type":"number","minimum":0,"maximum":100},"revenueType":{"type":"string","enum":["all","streaming","sync","mechanical","performance","physical","merch","live"],"default":"all"},"conditions":{"type":"string"}},"required":["userId","role","percentage"]},"default":[]},"effectiveDate":{"type":"string","format":"date-time"},"expirationDate":{"type":"string","format":"date-time"},"territory":{"type":"array","items":{"type":"string","enum":["worldwide","us","ca","uk","eu","latam","apac","africa","middle_east","ad","ae","af","ag","ai","al","am","ao","aq","ar","as","at","au","aw","ax","az","ba","bb","bd","be","bf","bg","bh","bi","bj","bl","bm","bn","bo","bq","br","bs","bt","bv","bw","by","bz","cc","cd","cf","cg","ch","ci","ck","cl","cm","cn","co","cr","cu","cv","cw","cx","cy","cz","de","dj","dk","dm","do","dz","ec","ee","eg","eh","er","es","et","fi","fj","fk","fm","fo","fr","ga","gb","gd","ge","gf","gg","gh","gi","gl","gm","gn","gp","gq","gr","gs","gt","gu","gw","gy","hk","hm","hn","hr","ht","hu","id","ie","il","im","in","io","iq","ir","is","it","je","jm","jo","jp","ke","kg","kh","ki","km","kn","kp","kr","kw","ky","kz","la","lb","lc","li","lk","lr","ls","lt","lu","lv","ly","ma","mc","md","me","mf","mg","mh","mk","ml","mm","mn","mo","mp","mq","mr","ms","mt","mu","mv","mw","mx","my","mz","na","nc","ne","nf","ng","ni","nl","no","np","nr","nu","nz","om","pa","pe","pf","pg","ph","pk","pl","pm","pn","pr","ps","pt","pw","py","qa","re","ro","rs","ru","rw","sa","sb","sc","sd","se","sg","sh","si","sj","sk","sl","sm","sn","so","sr","ss","st","sv","sx","sy","sz","tc","td","tf","tg","th","tj","tk","tl","tm","tn","to","tr","tt","tv","tw","tz","ua","ug","um","uy","uz","va","vc","ve","vg","vi","vn","vu","wf","ws","xk","ye","yt","za","zm","zw"]}},"contractValue":{"type":"number","minimum":0},"advanceAmount":{"type":"number","minimum":0},"royaltyRate":{"type":"number","minimum":0,"maximum":100},"exclusivity":{"type":"boolean"},"optionPeriods":{"type":"integer","minimum":0},"aiTrainingOptIn":{"type":"string","enum":["not_specified","opted_in","opted_out"]},"aiTrainingNotes":{"type":"string"},"templateId":{"type":"integer","exclusiveMinimum":0}}}}}},"responses":{"200":{"description":"The updated contract.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ContractWithRelations"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid body, or the contract is not in draft status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Contracts"],"operationId":"deleteContract","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Soft-delete a draft contract","description":"Creator or admin, drafts only. The row is retained with a deletion timestamp and disappears from every other endpoint.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Contract soft-deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]}},"required":["deleted"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Only contracts in draft status can be deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/{id}/remind":{"post":{"tags":["Contracts"],"operationId":"remindContractSigners","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Remind pending signers","description":"Nudges parties who have not signed. Passing `userId` targets one member; omitting it reminds everyone AND re-emails sign links to external parties without a live token.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemindSignersRequest"}}}},"responses":{"200":{"description":"How many parties were targeted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"reminded":{"type":"integer"}},"required":["reminded"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The contract is not awaiting signatures.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/{id}/sign":{"post":{"tags":["Contracts"],"operationId":"signContract","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Sign a contract as a member party","description":"The caller must be a member party on this contract, which must be `approved` or `pending_signatures`. The IP and User-Agent are recorded alongside the signature as evidence of intent. Once the last party signs, the contract flips to `active`.\n\nExternal (emailed) signers use the public signing routes instead.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"signatureDataUrl":{"type":"string","minLength":1,"maxLength":3500000},"consent":{"type":"boolean","enum":[true]}},"required":["signatureDataUrl","consent"]}}}},"responses":{"200":{"description":"Signature recorded.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/SignatureResult"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Wrong status, already signed, or an unusable signature image.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/{id}/hash":{"post":{"tags":["Contracts"],"operationId":"createContractHash","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Anchor a tamper-evident hash","description":"Computes a sha256 over the contract body, parties and splits, and stores it. Returns the freshly computed result rather than the stored row.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"responses":{"201":{"description":"Hash computed and stored.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/HashResult"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Contracts"],"operationId":"getContractHash","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Get the most recent hash","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"The newest stored hash.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ContractHash"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No hash has been taken for this contract.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/{id}/submit-for-approval":{"post":{"tags":["Contracts"],"operationId":"submitContractForApproval","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Submit a draft for approval","description":"Creator-only, drafts only. Opens an approval workflow with the given ordered steps, moves the contract to `pending_review`, and notifies the first approver.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"approvalSteps":{"type":"array","items":{"type":"object","properties":{"stepOrder":{"type":"integer","minimum":1},"approverRole":{"type":"string","enum":["manager","lawyer","artist","publisher","label","admin"]},"approverId":{"type":"string"}},"required":["stepOrder","approverRole"]},"minItems":1}},"required":["approvalSteps"]}}}},"responses":{"200":{"description":"Workflow opened.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"submitted":{"type":"boolean","enum":[true]},"contractStatus":{"type":"string","enum":["pending_review"]}},"required":["submitted","contractStatus"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"The contract is not in draft status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/{id}/versions":{"get":{"tags":["Contracts"],"operationId":"listContractVersions","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List content versions","description":"Newest first. A version is snapshotted on each content edit, not on every save.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Versions, newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/ContractVersion"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/{id}/template-version":{"get":{"tags":["Contracts"],"operationId":"getContractTemplateVersion","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Get contract template version and re-consent status","description":"Returns the version of the template this contract was generated from, the latest version in force, and evaluates re-consent rules under Founder Decision D-13.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Template versioning and re-consent evaluation.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ContractTemplateVersion"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Contract not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/{id}/pdf":{"get":{"tags":["Contracts"],"operationId":"exportContractPdf","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Export the contract as a PDF","description":"Renders the contract, its parties (with signature images where present) and splits, stamped with the latest hash. Returns the PDF bytes as an attachment — this is the one endpoint that does not answer with the JSON envelope.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"The rendered PDF.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/{id}/activity":{"get":{"tags":["Contracts"],"operationId":"getContractActivity","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Per-contract activity feed","description":"The 50 most recent audit entries, newest first, with actor names resolved and a pre-rendered description per entry.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Recent activity.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"activity":{"type":"array","items":{"$ref":"#/components/schemas/ActivityEntry"}}},"required":["activity"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/{id}/findings":{"get":{"tags":["Contracts"],"operationId":"getContractFindings","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"AI findings summary","description":"Findings ordered by confidence, plus per-engine counts for a summary header.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Findings and counts.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"findings":{"type":"array","items":{"$ref":"#/components/schemas/AiFinding"}},"total":{"type":"integer"},"byEngine":{"type":"object","additionalProperties":{"type":"integer"},"description":"Finding count keyed by engine type."}},"required":["findings","total","byEngine"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/{id}/duplicate":{"post":{"tags":["Contracts"],"operationId":"duplicateContract","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Duplicate a contract","description":"Copies terms, parties and splits into a new `draft` titled \"Copy of …\". Signatures are NOT carried over — every party resets to pending.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"responses":{"201":{"description":"The duplicate was created.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"id":{"type":"string","description":"New contract id, as a string."}},"required":["id"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/{id}/comments":{"get":{"tags":["Contracts"],"operationId":"listContractComments","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List clause comments","description":"Newest first, capped at 1000, soft-deleted rows excluded. `clauseAnchor` narrows to a single clause thread.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"},{"schema":{"type":"string","description":"Return only comments anchored to this clause."},"required":false,"name":"clauseAnchor","in":"query"}],"responses":{"200":{"description":"Comments, newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/ContractComment"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Contracts"],"operationId":"createContractComment","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Add a comment or reply","description":"Threading is flat: a reply may only attach to a top-level comment, and inherits its parent's `clauseAnchor` — any client-supplied anchor is ignored on a reply.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"clauseAnchor":{"type":"string","minLength":1,"maxLength":200},"body":{"type":"string","minLength":1,"maxLength":5000},"visibility":{"type":"string","enum":["internal","external"]},"parentId":{"type":"integer","exclusiveMinimum":0}},"required":["clauseAnchor","body"]}}}},"responses":{"201":{"description":"The created comment.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ContractComment"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid body, or an attempt to reply to a reply.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or the parent comment is not on it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/{id}/comments/{commentId}":{"patch":{"tags":["Contracts"],"operationId":"updateContractComment","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Edit, resolve or re-open a comment","description":"Editing `body` is author-or-admin. Setting `resolved` is open to any member with contract access.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"},{"schema":{"type":["integer","null"],"example":34},"required":false,"name":"commentId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","minLength":1,"maxLength":5000},"resolved":{"type":"boolean"}}}}}},"responses":{"200":{"description":"The updated comment.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ContractComment"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid body or ids.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract or comment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Contracts"],"operationId":"deleteContractComment","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Soft-delete a comment","description":"Author-or-admin. Never hard-deleted, so replies stay anchored.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"},{"schema":{"type":["integer","null"],"example":34},"required":false,"name":"commentId","in":"path"}],"responses":{"200":{"description":"The deleted comment id.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"id":{"type":"integer"}},"required":["id"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid ids.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract or comment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/imports":{"post":{"tags":["Contract imports"],"operationId":"createContractImport","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Start a document import","description":"Part of the upload -> parse -> extract -> review -> confirm pipeline. The pipeline runs IN-PROCESS and this request returns immediately, so the review screen polls GET /contracts/imports/{id} for progress.\n\nThe referenced media must already be uploaded and belong to the active organization. Its bytes are re-verified against the declared MIME type and, for DOCX, checked for archive-bomb characteristics — a client-declared content type is not trusted. Rate-limited per organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mediaId":{"type":"integer","exclusiveMinimum":0},"filename":{"type":"string","minLength":1,"maxLength":500},"mimeType":{"type":"string","enum":["application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/pdf"]},"fileSize":{"type":"integer","exclusiveMinimum":0,"maximum":26214400}},"required":["mediaId","filename","mimeType","fileSize"]}}}},"responses":{"201":{"description":"Import row created; the pipeline is running.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ContractImport"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid body, oversized file, or failed byte verification. Carries `code: MIME_MISMATCH` or `code: UNSAFE_ARCHIVE` for the two security cases.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such media in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Import rate limit exceeded for this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Object storage is not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/imports/{id}":{"get":{"tags":["Contract imports"],"operationId":"getContractImport","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Poll import status","description":"The heavy fields — `parsedContent` and `extraction` — are OMITTED until `status` is `ready_for_review` or `confirmed`. While the import is still churning or has failed, each poll returns the light row rather than shipping a large body the client would ignore. Treat both fields as absent, not null.","parameters":[{"schema":{"type":["integer","null"],"example":6},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"The import row, light or full depending on status.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ContractImport"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such import in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/imports/{id}/retry":{"post":{"tags":["Contract imports"],"operationId":"retryContractImport","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Retry a failed import","description":"Only a `failed` import can be retried, and only within a bounded retry count — the AI extraction stage costs money, so this is deliberately capped rather than open-ended. Subject to the same rate limit and byte re-verification as create.","parameters":[{"schema":{"type":["integer","null"],"example":6},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Import re-queued; poll for progress.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ContractImport"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"The import is not in a failed state, or byte verification failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such import, or its source media is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Retry limit reached, or the rate limit was exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Object storage is not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contracts/imports/{id}/confirm":{"post":{"tags":["Contract imports"],"operationId":"confirmContractImport","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Confirm an import into a contract","description":"Copies the parsed body VERBATIM into a new contract and links the import to it, closing the provenance loop from the original file through to the contract. Only valid once `status` is `ready_for_review`.\n\nSplits that could not be resolved to a user are returned as warnings rather than dropped silently.","parameters":[{"schema":{"type":["integer","null"],"example":6},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":500},"description":{"type":"string","maxLength":2000},"contractType":{"type":"string","enum":["publishing","master_recording","sync_license","producer","distribution","work_for_hire","cowriting","three_sixty","management","licensing","other","platform_order_form","master_supply_agreement"]},"content":{},"parties":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["artist","producer","songwriter","publisher","label","manager","distributor","engineer","featured_artist","session_musician","other"]}},"required":["role"]},"default":[]},"splits":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"role":{"type":"string","enum":["artist","producer","songwriter","publisher","label","manager","distributor","other"]},"percentage":{"type":"number","minimum":0,"maximum":100},"revenueType":{"type":"string","enum":["all","streaming","sync","mechanical","performance","physical","merch","live"],"default":"all"},"conditions":{"type":"string"}},"required":["userId","role","percentage"]},"default":[]},"effectiveDate":{"type":"string","format":"date-time"},"expirationDate":{"type":"string","format":"date-time"},"territory":{"type":"array","items":{"type":"string","enum":["worldwide","us","ca","uk","eu","latam","apac","africa","middle_east","ad","ae","af","ag","ai","al","am","ao","aq","ar","as","at","au","aw","ax","az","ba","bb","bd","be","bf","bg","bh","bi","bj","bl","bm","bn","bo","bq","br","bs","bt","bv","bw","by","bz","cc","cd","cf","cg","ch","ci","ck","cl","cm","cn","co","cr","cu","cv","cw","cx","cy","cz","de","dj","dk","dm","do","dz","ec","ee","eg","eh","er","es","et","fi","fj","fk","fm","fo","fr","ga","gb","gd","ge","gf","gg","gh","gi","gl","gm","gn","gp","gq","gr","gs","gt","gu","gw","gy","hk","hm","hn","hr","ht","hu","id","ie","il","im","in","io","iq","ir","is","it","je","jm","jo","jp","ke","kg","kh","ki","km","kn","kp","kr","kw","ky","kz","la","lb","lc","li","lk","lr","ls","lt","lu","lv","ly","ma","mc","md","me","mf","mg","mh","mk","ml","mm","mn","mo","mp","mq","mr","ms","mt","mu","mv","mw","mx","my","mz","na","nc","ne","nf","ng","ni","nl","no","np","nr","nu","nz","om","pa","pe","pf","pg","ph","pk","pl","pm","pn","pr","ps","pt","pw","py","qa","re","ro","rs","ru","rw","sa","sb","sc","sd","se","sg","sh","si","sj","sk","sl","sm","sn","so","sr","ss","st","sv","sx","sy","sz","tc","td","tf","tg","th","tj","tk","tl","tm","tn","to","tr","tt","tv","tw","tz","ua","ug","um","uy","uz","va","vc","ve","vg","vi","vn","vu","wf","ws","xk","ye","yt","za","zm","zw"]}},"contractValue":{"type":"number","minimum":0},"advanceAmount":{"type":"number","minimum":0},"royaltyRate":{"type":"number","minimum":0,"maximum":100},"exclusivity":{"type":"boolean"},"optionPeriods":{"type":"integer","minimum":0},"aiTrainingOptIn":{"type":"string","enum":["not_specified","opted_in","opted_out"]},"aiTrainingNotes":{"type":"string"},"templateId":{"type":"integer","exclusiveMinimum":0},"saveAsTemplate":{"type":"boolean","default":false}},"required":["title","contractType"]}}}},"responses":{"201":{"description":"Contract created from the import.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"contract":{"anyOf":[{"$ref":"#/components/schemas/ContractWithRelations"},{"type":"null"},{"type":"null"}]},"deferredSplitWarnings":{"type":"array","items":{"type":"string"},"description":"Splits that could not be resolved to a user, surfaced rather than dropped."},"saveAsTemplate":{"type":"object","properties":{"requested":{"type":"boolean"},"applied":{"type":"boolean"},"reason":{"type":"string"}},"required":["requested","applied"],"description":"Always `applied: false` — save-as-template is not implemented. Reported explicitly so the review screen can tell the user their toggle had no effect instead of silently discarding it."}},"required":["contract","deferredSplitWarnings","saveAsTemplate"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Already confirmed, wrong status, or no parsed content to confirm.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such import in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates":{"get":{"tags":["Templates"],"operationId":"listTemplates","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List templates","description":"Returns the caller's own-org templates plus every public template from any tenant — private templates of other orgs are excluded. Newest first; the cursor pages to OLDER rows.","parameters":[{"schema":{"type":["integer","null"],"description":"Id from the previous page's `meta.cursor`. Pages to older rows."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Case-insensitive substring match on title."},"required":false,"name":"q","in":"query"}],"responses":{"200":{"description":"A page of templates, newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/TemplateListItem"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Templates"],"operationId":"createTemplate","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Create a template","description":"Variables are extracted from `content` and stored. Creating a PUBLIC template needs the `templates:publish` permission and `confirmedCrossTenant: true` — publishing exposes it to every other tenant, so the confirmation is deliberate friction.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":500},"description":{"type":"string"},"contractType":{"type":"string","enum":["publishing","master_recording","sync_license","producer","distribution","work_for_hire","cowriting","three_sixty","management","licensing","other","platform_order_form","master_supply_agreement"]},"jurisdiction":{"type":"string","enum":["ng","us","uk","generic"]},"category":{"type":"string"},"content":{},"isPublic":{"type":"boolean","default":false},"confirmedCrossTenant":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"defaultSplits":{}},"required":["title"]}}}},"responses":{"201":{"description":"The created template.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Template"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid body, or a public template without cross-tenant confirmation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates/{id}/use":{"post":{"tags":["Templates"],"operationId":"useTemplate","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Create a contract from a template","description":"Substitutes variables into the template body and opens a fresh draft contract, resolving role-only default splits through `splitAssignees`. Also bumps the template's usage counters.","parameters":[{"schema":{"type":["integer","null"],"example":7},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UseTemplateRequest"}}}},"responses":{"201":{"description":"The new draft contract. Splits are created but not returned.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Contract"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates/{id}":{"get":{"tags":["Templates"],"operationId":"getTemplate","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Get a template","description":"Readable when the template is public, or owned by the caller's organization.","parameters":[{"schema":{"type":["integer","null"],"example":7},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"The template.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/TemplateListItem"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such template, or it is soft-deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Templates"],"operationId":"updateTemplate","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Update a template","description":"Owner-only, and scoped to the owning organization. Editing `content` re-derives `variables`. Flipping `isPublic` to true carries the same permission and confirmation requirements as create.","parameters":[{"schema":{"type":["integer","null"],"example":7},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":500},"description":{"type":"string"},"contractType":{"type":"string","enum":["publishing","master_recording","sync_license","producer","distribution","work_for_hire","cowriting","three_sixty","management","licensing","other","platform_order_form","master_supply_agreement"]},"jurisdiction":{"type":"string","enum":["ng","us","uk","generic"]},"category":{"type":"string"},"content":{},"isPublic":{"type":"boolean","default":false},"confirmedCrossTenant":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"defaultSplits":{}}}}}},"responses":{"200":{"description":"The updated template.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Template"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid body, or a publish without cross-tenant confirmation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such template in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Templates"],"operationId":"deleteTemplate","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Soft-delete a template","description":"Owner-only. The row is retained with a deletion timestamp.","parameters":[{"schema":{"type":["integer","null"],"example":7},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Template soft-deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]}},"required":["deleted"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such template in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates/{id}/review":{"patch":{"tags":["Templates"],"operationId":"reviewTemplate","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Record a counsel review decision","description":"Sets `reviewStatus` and stamps who reviewed, when, and an optional note. Distinct from PATCH /{id}, which is owner-only content editing — counsel edits the body there and signs off here. Requires `templates:review` (admin, manager or lawyer), and the reviewer must belong to the owning organization.","parameters":[{"schema":{"type":["integer","null"],"example":7},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewTemplateRequest"}}}},"responses":{"200":{"description":"The reviewed template.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Template"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Unrecognised reviewStatus.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such template in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/templates/{id}/duplicate":{"post":{"tags":["Templates"],"operationId":"duplicateTemplate","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Duplicate a template","description":"Owner-only. Copies content and default splits into a fresh private draft titled \"Copy of …\". The copy is always `isPublic: false` — duplicating never re-publishes.","parameters":[{"schema":{"type":["integer","null"],"example":7},"required":false,"name":"id","in":"path"}],"responses":{"201":{"description":"The duplicate.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Template"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such template in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/obligations":{"get":{"tags":["Obligations"],"operationId":"listObligations","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List obligations","description":"Visible to the assignee, or to any org admin. Admins see the whole organization; everyone else sees only obligations assigned to them.\n\nNewest first; the cursor pages to OLDER rows.","parameters":[{"schema":{"type":["integer","null"]},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Filter by obligation status."},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"A page of obligations, newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/ObligationWithRelations"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Obligations"],"operationId":"createObligation","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Create an obligation","description":"The parent contract must belong to the active organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contractId":{"type":"integer","exclusiveMinimum":0},"title":{"type":"string","minLength":1,"maxLength":500},"description":{"type":"string"},"obligationType":{"type":"string","enum":["delivery","option","advance_payment","royalty_payment","reversion","exclusivity_expiration","renewal","audit","reporting","other"]},"assignedToId":{"type":"string"},"dueDate":{"type":"string","format":"date-time"},"amount":{"type":"number","minimum":0},"currency":{"type":"string","minLength":3,"maxLength":3,"default":"USD"}},"required":["contractId","title","obligationType","assignedToId","dueDate"]}}}},"responses":{"201":{"description":"The created obligation.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Obligation"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/obligations/{id}":{"get":{"tags":["Obligations"],"operationId":"getObligation","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Get an obligation","description":"Visible to the assignee, or to any org admin. Admins see the whole organization; everyone else sees only obligations assigned to them.","parameters":[{"schema":{"type":["integer","null"],"example":3},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"The obligation, with contract and assignee.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ObligationWithRelations"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such obligation in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Obligations"],"operationId":"updateObligation","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Update an obligation","description":"Assignee or org admin only. Setting `status` to `completed` also stamps `completedAt`.\n\nVisible to the assignee, or to any org admin. Admins see the whole organization; everyone else sees only obligations assigned to them.","parameters":[{"schema":{"type":["integer","null"],"example":3},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":500},"description":{"type":"string"},"status":{"type":"string","enum":["pending","in_progress","completed","overdue","waived"]},"dueDate":{"type":"string","format":"date-time"},"amount":{"type":"number","minimum":0},"currency":{"type":"string","minLength":3,"maxLength":3}}}}}},"responses":{"200":{"description":"The updated obligation.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Obligation"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such obligation in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Obligations"],"operationId":"deleteObligation","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Soft-delete an obligation","description":"Assignee or org admin only. The row is retained with a deletion timestamp.\n\nVisible to the assignee, or to any org admin. Admins see the whole organization; everyone else sees only obligations assigned to them.","parameters":[{"schema":{"type":["integer","null"],"example":3},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Obligation soft-deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]}},"required":["deleted"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such obligation in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/me":{"get":{"tags":["Users & teams"],"operationId":"getCurrentUser","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Get the current user","description":"The auth user, the profile row, the active organization, and - importantly - `activeRole`, the per-workspace professional role RBAC gates on. Prefer it over the legacy `profile.role`.","responses":{"200":{"description":"The current user.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Me"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Users & teams"],"operationId":"updateCurrentUser","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Update the current user","description":"`name` is written to the auth user record; every other field lands on the profile row, which is created if absent. Returns the profile only - the updated `name` is not echoed back.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"phone":{"type":"string"},"company":{"type":"string"},"bio":{"type":"string"},"street":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"postalCode":{"type":"string"},"country":{"type":"string"},"socialMedia":{"type":"array","items":{"type":"object","properties":{"platform":{"type":"string"},"url":{"type":"string","format":"uri"}},"required":["platform","url"]}},"onboarding":{"type":"object","properties":{"completedSteps":{"type":"array","items":{"type":"string"}},"completedAt":{"type":"string"},"skippedAt":{"type":"string"}}}}}}}},"responses":{"200":{"description":"The updated profile.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/UserProfile"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Users & teams"],"operationId":"deleteCurrentUser","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Delete the account","description":"Permanently deletes the caller's account. Cascades to sessions, accounts, org memberships, invitations and 2FA enrolment. **Irreversible.**\n\n**Reachable with an API key.** Like every authenticated endpoint here, this accepts `Authorization: Bearer fr_live_...`, so a leaked key can perform this action. An API key can therefore delete the very account that issued it.","responses":{"200":{"description":"Account deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]}},"required":["deleted"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/organizations":{"get":{"tags":["Users & teams"],"operationId":"listMyOrganizations","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List the caller's organizations","description":"Better Auth's own org list returns bare rows; this enriches each with the caller's access role and a member count.","responses":{"200":{"description":"Organizations the caller belongs to.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationSummary"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/invitations":{"get":{"tags":["Users & teams"],"operationId":"listMyInvitations","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List pending invitations addressed to the caller","description":"Matched on the caller's email address, across all organizations.","responses":{"200":{"description":"Pending invitations.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/PendingInvitation"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/me/photo":{"patch":{"tags":["Users & teams"],"operationId":"updateProfilePhoto","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Upload a profile photo","description":"Multipart upload. The image is re-encoded to a 256x256 webp, stored, recorded as a media asset, and set as the avatar. Returns 503 when object storage is not configured.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/ProfilePhotoUpload"}}}},"responses":{"200":{"description":"The stored avatar.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"url":{"type":"string"},"mediaId":{"type":"integer"}},"required":["url","mediaId"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Missing `file` field, or the upload is not an image.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Object storage is not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/me/notifications":{"get":{"tags":["Users & teams"],"operationId":"getNotificationPreferences","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Get notification preferences","description":"Returns the built-in defaults when the user has never set any.","responses":{"200":{"description":"Current preferences.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/NotificationPreferences"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Users & teams"],"operationId":"updateNotificationPreferences","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Update notification preferences","description":"Shallow-merged per channel, so a partial update does not wipe the channels it omits. Returns the merged result.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNotificationPreferences"}}}},"responses":{"200":{"description":"The merged preferences.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/NotificationPreferences"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/sign-out-all":{"post":{"tags":["Users & teams"],"operationId":"signOutOtherSessions","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Revoke all other sessions","description":"Delegates to Better Auth, which requires the account password for credential users. The response body is Better Auth's own and is not modelled here.","requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignOutAllRequest"}}}},"responses":{"200":{"description":"Better Auth's response, passed through unmodelled.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/SignOutAllResult"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/me/sessions":{"get":{"tags":["Users & teams"],"operationId":"listMySessions","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List active sessions","description":"Each row carries its session `token`, which is what revocation takes. Treat those tokens as credentials.","responses":{"200":{"description":"Active sessions.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/SessionSummary"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/me/sessions/{token}":{"delete":{"tags":["Users & teams"],"operationId":"revokeSession","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Revoke one session","description":"Answers 200 whether or not the token matched an existing session.","parameters":[{"schema":{"type":"string","description":"Session token from GET /users/me/sessions. URL-encoded."},"required":true,"name":"token","in":"path"}],"responses":{"200":{"description":"Revocation attempted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"revoked":{"type":"boolean","enum":[true]}},"required":["revoked"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/team":{"get":{"tags":["Users & teams"],"operationId":"listTeam","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List the active workspace team","description":"Active members and unexpired pending invitations in ONE list, discriminated by `status`. Note `id` means different things per row: a user id for `active`, an invitation id for `pending`.","responses":{"200":{"description":"Members and pending invitations.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/TeamMember"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/team/{id}/role":{"patch":{"tags":["Users & teams"],"operationId":"setMemberProfessionalRole","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Set a member's professional role","description":"Admin-only. Sets the per-workspace RBAC role, NOT the access role. The workspace owner's role can only be changed by the owner themselves.\n\n**Reachable with an API key.** Like every authenticated endpoint here, this accepts `Authorization: Bearer fr_live_...`, so a leaked key can perform this action.","parameters":[{"schema":{"type":"string","description":"Target user id."},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetMemberRoleRequest"}}}},"responses":{"200":{"description":"Role updated.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"userId":{"type":"string"},"professionalRole":{"type":"string","enum":["admin","artist","manager","lawyer","producer","publisher","label"]}},"required":["userId","professionalRole"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such member in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/invite":{"post":{"tags":["Users & teams"],"operationId":"inviteMember","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Invite a member","description":"Admin-only. `role` is the ACCESS role - inviting straight to `owner` is not permitted. `professionalRole` is applied on acceptance and defaults to `artist`. Sends an invitation email.\n\n**Reachable with an API key.** Like every authenticated endpoint here, this accepts `Authorization: Bearer fr_live_...`, so a leaked key can perform this action.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteMemberRequest"}}}},"responses":{"200":{"description":"The created invitation id.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/invite/{id}":{"delete":{"tags":["Users & teams"],"operationId":"cancelInvitation","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Cancel a pending invitation","description":"Admin-only. Marks the invitation canceled; the row is retained.","parameters":[{"schema":{"type":"string","description":"Invitation id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Invitation canceled.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"canceled":{"type":"boolean","enum":[true]}},"required":["canceled"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such invitation in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/invite/{id}/resend":{"post":{"tags":["Users & teams"],"operationId":"resendInvitation","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Re-issue an invitation","description":"Admin-only. Cancels the stale invitation and creates a fresh one - new id, new token, new expiry - carrying the original professional role. The returned id is the NEW invitation.","parameters":[{"schema":{"type":"string","description":"Invitation id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The replacement invitation id.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such invitation in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/invitations/{id}/accept":{"post":{"tags":["Users & teams"],"operationId":"acceptInvitation","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Accept an invitation","description":"Better Auth enforces that the session email matches the invitation, so this cannot accept someone else's. The invited professional role is applied only when the membership has none yet, so re-inviting an existing member cannot silently downgrade them.","parameters":[{"schema":{"type":"string","description":"Invitation id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Invitation accepted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"accepted":{"type":"boolean","enum":[true]},"organizationId":{"type":"string"},"res":{"description":"Better Auth's response, unmodelled."}},"required":["accepted","organizationId"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such invitation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/team/{id}":{"delete":{"tags":["Users & teams"],"operationId":"removeMember","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Remove a member","description":"Admin-only. Only an owner may remove another owner, and the last owner can never be removed. Removing yourself is rejected - use POST /users/leave-organization.\n\n**Reachable with an API key.** Like every authenticated endpoint here, this accepts `Authorization: Bearer fr_live_...`, so a leaked key can perform this action.","parameters":[{"schema":{"type":"string","description":"Target user id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Member removed.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"removed":{"type":"boolean","enum":[true]}},"required":["removed"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Attempted to remove yourself, or the last owner.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such member in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/leave-organization":{"post":{"tags":["Users & teams"],"operationId":"leaveOrganization","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Leave an organization","description":"Defaults to the active workspace. A sole owner cannot leave - ownership must be transferred first, which is what stops an organization being orphaned.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaveOrganizationRequest"}}}},"responses":{"200":{"description":"Membership removed.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"left":{"type":"boolean","enum":[true]}},"required":["left"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"You are the only owner.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"You are not a member of that organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/users/{id}":{"get":{"tags":["Users & teams"],"operationId":"getUser","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Look up a user","description":"Legacy. The caller may fetch themselves; fetching anyone else requires an admin professional role. Note this is NOT organization-scoped - an admin can look up a user id from any tenant.","parameters":[{"schema":{"type":"string","description":"User id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The user.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"allOf":[{"$ref":"#/components/schemas/User"},{"type":"object","properties":{"profile":{"anyOf":[{"$ref":"#/components/schemas/UserProfile"},{"type":"null"},{"type":"null"}]}},"required":["profile"]}]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api-keys":{"get":{"tags":["API keys"],"operationId":"listApiKeys","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List active API keys","description":"Returns the calling user's non-revoked keys within the active organization. Revoked keys are omitted entirely rather than flagged.","responses":{"200":{"description":"Active keys, newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["API keys"],"operationId":"createApiKey","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Create an API key","description":"Mints a new key scoped to the calling user and active organization. The plaintext secret is in the response body and is NOT recoverable afterwards — only its hash is stored.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}}},"responses":{"201":{"description":"Key created. Contains the one-time plaintext secret.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ApiKeyCreated"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api-keys/{id}":{"delete":{"tags":["API keys"],"operationId":"revokeApiKey","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Revoke an API key","description":"Irreversible. The key stops authenticating immediately; the row is retained with a revocation timestamp for audit.","parameters":[{"schema":{"type":["integer","null"],"example":42},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Key revoked.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"revoked":{"type":"boolean","enum":[true]}},"required":["revoked"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Key was already revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such key for this user and organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/notifications":{"get":{"tags":["Notifications"],"operationId":"listNotifications","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List notifications","description":"The caller's own notifications in the active workspace, newest first. The cursor pages to OLDER rows.","parameters":[{"schema":{"type":["integer","null"]},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Pass the literal string `true` to return only unread. Any other value is ignored.","example":"true"},"required":false,"name":"unread","in":"query"}],"responses":{"200":{"description":"A page of notifications.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Notification"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/notifications/unread-count":{"get":{"tags":["Notifications"],"operationId":"getUnreadNotificationCount","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Count unread notifications","responses":{"200":{"description":"Unread count.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"count":{"type":"integer"}},"required":["count"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/notifications/{id}/read":{"patch":{"tags":["Notifications"],"operationId":"markNotificationRead","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Mark one notification read","parameters":[{"schema":{"type":["integer","null"],"example":9},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Marked read.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"read":{"type":"boolean","enum":[true]}},"required":["read"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such notification for this user and organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/notifications/read-all":{"post":{"tags":["Notifications"],"operationId":"markAllNotificationsRead","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Mark every notification read","description":"Affects only the caller's own notifications in the active workspace.","responses":{"200":{"description":"All marked read.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"readAll":{"type":"boolean","enum":[true]}},"required":["readAll"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai-rights":{"get":{"tags":["AI rights registry"],"operationId":"listAiRights","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List AI rights entries","description":"The registry is a RECORD of the consent position for a work. Nothing in this system enforces it — it exists to be cited, exported, and pointed at.\n\nOrg-scoped, newest first.","parameters":[{"schema":{"type":["integer","null"]},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"A page of entries.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/AiRightsEntryWithContract"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["AI rights registry"],"operationId":"createAiRightsEntry","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Register a work","description":"The registry is a RECORD of the consent position for a work. Nothing in this system enforces it — it exists to be cited, exported, and pointed at.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"workTitle":{"type":"string","minLength":1},"workType":{"type":"string","enum":["sound_recording","composition","lyrics","performance","music_video","album"]},"contractId":{"type":"integer","exclusiveMinimum":0},"aiTrainingConsent":{"type":"string","enum":["opted_in","opted_out","conditional"]},"conditions":{"type":"string"},"licensedTo":{"type":"array","items":{"type":"object","properties":{"entity":{"type":"string"},"scope":{"type":"string"},"startDate":{"type":"string"},"endDate":{"type":"string"}},"required":["entity","scope","startDate","endDate"]}},"complianceNotes":{"type":"string"},"isrc":{"type":"string"},"iswc":{"type":"string"}},"required":["workTitle","workType","aiTrainingConsent"]}}}},"responses":{"201":{"description":"The created entry.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/AiRightsEntry"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai-rights/{id}":{"get":{"tags":["AI rights registry"],"operationId":"getAiRightsEntry","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Get an AI rights entry","parameters":[{"schema":{"type":["integer","null"],"example":2},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"The entry, with its linked contract.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/AiRightsEntryWithContract"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such entry in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["AI rights registry"],"operationId":"updateAiRightsEntry","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Update an AI rights entry","description":"Only the user who registered the work may edit it — not org admins.","parameters":[{"schema":{"type":["integer","null"],"example":2},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"workTitle":{"type":"string","minLength":1},"workType":{"type":"string","enum":["sound_recording","composition","lyrics","performance","music_video","album"]},"contractId":{"type":"integer","exclusiveMinimum":0},"aiTrainingConsent":{"type":"string","enum":["opted_in","opted_out","conditional"]},"conditions":{"type":"string"},"licensedTo":{"type":"array","items":{"type":"object","properties":{"entity":{"type":"string"},"scope":{"type":"string"},"startDate":{"type":"string"},"endDate":{"type":"string"}},"required":["entity","scope","startDate","endDate"]}},"complianceNotes":{"type":"string"},"isrc":{"type":"string"},"iswc":{"type":"string"}}}}}},"responses":{"200":{"description":"The updated entry.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/AiRightsEntry"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such entry in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["AI rights registry"],"operationId":"deleteAiRightsEntry","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Soft-delete an AI rights entry","description":"Registrant-only. The row is retained with a deletion timestamp — a consent record that could be hard-deleted would be poor evidence.","parameters":[{"schema":{"type":["integer","null"],"example":2},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Entry soft-deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]}},"required":["deleted"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such entry in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/media/upload-url":{"post":{"tags":["Media"],"operationId":"createUploadUrl","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Get a signed upload URL","description":"Returns a short-lived URL the client PUTs the file to directly, plus a media record created up front. That record starts with `fileSize: 0` and is NOT reconciled after the upload — nothing here observes whether the client actually completed it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"filename":{"type":"string","minLength":1},"contentType":{"type":"string","minLength":1},"folder":{"type":"string","enum":["media","contract-imports"],"default":"media"}},"required":["filename","contentType"]}}}},"responses":{"201":{"description":"Upload URL issued and a media record created.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"uploadUrl":{"type":"string","description":"Signed URL to PUT the file to."},"mediaId":{"type":"integer"},"gcsKey":{"type":"string"}},"required":["uploadUrl","mediaId","gcsKey"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Object storage is not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/media/download-url/{id}":{"get":{"tags":["Media"],"operationId":"createDownloadUrl","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Get a signed download URL","description":"Org-scoped. The returned URL is short-lived; re-request rather than storing it.","parameters":[{"schema":{"type":["integer","null"],"example":4},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Download URL issued.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"url":{"type":"string"},"filename":{"type":"string"},"mimeType":{"type":"string"}},"required":["url","filename","mimeType"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such media in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Object storage is not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/inline/complete":{"post":{"tags":["AI"],"operationId":"streamInlineCompletion","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Stream a ghost-text completion","description":"Continues the prose at the caret. Suggest-only: the editor never commits without an explicit Accept.\n\nStreams Server-Sent Events, NOT the JSON envelope. Requires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.","responses":{"200":{"description":"An SSE stream.","content":{"text/event-stream":{"schema":{"type":"string"}}}},"503":{"description":"AI features are not configured on this deployment."}}}},"/ai/inline/transform":{"post":{"tags":["AI"],"operationId":"streamInlineTransform","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Stream a transformation of selected text","description":"Rewrites the selection per an instruction. Suggest-only, as above.\n\nStreams Server-Sent Events, NOT the JSON envelope. Requires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.","responses":{"200":{"description":"An SSE stream.","content":{"text/event-stream":{"schema":{"type":"string"}}}},"503":{"description":"AI features are not configured on this deployment."}}}},"/ai/conversations/{id}/messages":{"post":{"tags":["AI"],"operationId":"streamAssistantReply","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Post a message and stream the reply","description":"The model may PROPOSE gated tool calls; they are persisted as actions for a human to accept and are never executed from chat.\n\nStreams Server-Sent Events, NOT the JSON envelope. Requires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"An SSE stream.","content":{"text/event-stream":{"schema":{"type":"string"}}}},"503":{"description":"AI features are not configured on this deployment."}}},"get":{"tags":["AI"],"operationId":"listAiMessages","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Read a conversation","description":"Oldest first. `toolCallsJson` records tools the model PROPOSED — chat never executes them; they become `ai_actions` for a human to accept.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The message history.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/AiMessage"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such conversation in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/runs/{id}/start":{"post":{"tags":["AI"],"operationId":"startAiRun","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Execute a planned run","description":"Runs until the first checkpoint — a gated tool, or any tool at all under the lower autonomy levels — then pauses for a decision.\n\nStreams Server-Sent Events, NOT the JSON envelope. Requires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"An SSE stream.","content":{"text/event-stream":{"schema":{"type":"string"}}}},"503":{"description":"AI features are not configured on this deployment."}}}},"/ai/runs/{id}/resume":{"post":{"tags":["AI"],"operationId":"resumeAiRun","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Resume a paused run","description":"Continues from the checkpoint with the caller's decision applied.\n\nStreams Server-Sent Events, NOT the JSON envelope. Requires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"An SSE stream.","content":{"text/event-stream":{"schema":{"type":"string"}}}},"503":{"description":"AI features are not configured on this deployment."}}}},"/ai/dashboard-snapshots":{"get":{"tags":["AI"],"operationId":"getAiDashboardSnapshots","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"AI snapshots for the dashboard","description":"Best-effort snapshots for the three most recently updated active contracts. Findings are not persisted for this view, so an EMPTY list is the normal case rather than a failure — the widget renders nothing.","responses":{"200":{"description":"Snapshots, possibly empty.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/AiDashboardSnapshots"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/summarize":{"post":{"tags":["AI"],"operationId":"summarizeContract","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Summarise a contract","description":"Produces a plain-language summary of the contract.\n\nRequires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.\n\nThe `data` payload is MODEL OUTPUT, not an API-defined shape: it is whatever the prompt asked for, parsed out of the reply. Treat it as unstable.\n\nA 502 here means the reply was unusable — `code: ai_reply_truncated` (the answer was cut off) or `ai_reply_unparseable` (the model ignored the format). Neither is a bug in the request, and both are worth retrying. The unusable text is deliberately NOT echoed back.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contractId":{"type":"integer","exclusiveMinimum":0}},"required":["contractId"]}}}},"responses":{"200":{"description":"The model reply, parsed.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ModelReply"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or no access to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The model reply was unusable. See `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"AI features are not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/risk-score":{"post":{"tags":["AI"],"operationId":"scoreContractRisk","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Score clause risk","description":"Scores clauses against typical risk patterns for the contract type.\n\nRequires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.\n\nThe `data` payload is MODEL OUTPUT, not an API-defined shape: it is whatever the prompt asked for, parsed out of the reply. Treat it as unstable.\n\nA 502 here means the reply was unusable — `code: ai_reply_truncated` (the answer was cut off) or `ai_reply_unparseable` (the model ignored the format). Neither is a bug in the request, and both are worth retrying. The unusable text is deliberately NOT echoed back.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contractId":{"type":"integer","exclusiveMinimum":0}},"required":["contractId"]}}}},"responses":{"200":{"description":"The model reply, parsed.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ModelReply"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or no access to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The model reply was unusable. See `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"AI features are not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/anomalies":{"post":{"tags":["AI"],"operationId":"detectContractAnomalies","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Detect anomalies","description":"Flags terms that depart from what the contract type and deal terms would predict.\n\nRequires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.\n\nThe `data` payload is MODEL OUTPUT, not an API-defined shape: it is whatever the prompt asked for, parsed out of the reply. Treat it as unstable.\n\nA 502 here means the reply was unusable — `code: ai_reply_truncated` (the answer was cut off) or `ai_reply_unparseable` (the model ignored the format). Neither is a bug in the request, and both are worth retrying. The unusable text is deliberately NOT echoed back.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contractId":{"type":"integer","exclusiveMinimum":0}},"required":["contractId"]}}}},"responses":{"200":{"description":"The model reply, parsed.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ModelReply"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or no access to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The model reply was unusable. See `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"AI features are not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/generate":{"post":{"tags":["AI"],"operationId":"generateContractDraft","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Draft a contract from a description","description":"Generates DRAFT content from a plain-language description. Nothing is persisted — the caller decides what to do with it.\n\nRequires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.\n\nThe `data` payload is MODEL OUTPUT, not an API-defined shape: it is whatever the prompt asked for, parsed out of the reply. Treat it as unstable.\n\nA 502 here means the reply was unusable — `code: ai_reply_truncated` (the answer was cut off) or `ai_reply_unparseable` (the model ignored the format). Neither is a bug in the request, and both are worth retrying. The unusable text is deliberately NOT echoed back.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","minLength":10},"contractType":{"type":"string","enum":["publishing","master_recording","sync_license","producer","distribution","work_for_hire","cowriting","three_sixty","management","licensing","other","platform_order_form","master_supply_agreement"]}},"required":["description","contractType"]}}}},"responses":{"200":{"description":"The model reply, parsed.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ModelReply"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The model reply was unusable. See `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"AI features are not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/negotiate":{"post":{"tags":["AI"],"operationId":"analyzeNegotiationPosition","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Analyse a negotiating position","description":"Analyses the contract from ONE party's perspective — `userRole` changes the answer, and defaults to `artist`. This is analysis, not advice.\n\nRequires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.\n\nThe `data` payload is MODEL OUTPUT, not an API-defined shape: it is whatever the prompt asked for, parsed out of the reply. Treat it as unstable.\n\nA 502 here means the reply was unusable — `code: ai_reply_truncated` (the answer was cut off) or `ai_reply_unparseable` (the model ignored the format). Neither is a bug in the request, and both are worth retrying. The unusable text is deliberately NOT echoed back.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contractId":{"type":"integer","exclusiveMinimum":0},"userRole":{"type":"string"}},"required":["contractId"]}}}},"responses":{"200":{"description":"The model reply, parsed.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ModelReply"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or no access to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The model reply was unusable. See `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"AI features are not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/actions/{id}/apply":{"post":{"tags":["AI"],"operationId":"applyAiAction","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Apply a proposed AI action","description":"Executes a proposal and opens a bounded undo window — `undoExpiresAt` is when it closes, after which the change is permanent.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Action applied.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["applied"]},"undoExpiresAt":{"type":"string","format":"date-time"}},"required":["id","status","undoExpiresAt"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such action.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The action is not in a proposed state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/actions/{id}/undo":{"post":{"tags":["AI"],"operationId":"undoAiAction","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Undo an applied AI action","description":"Restores the pre-action snapshot. Only within the undo window: afterwards this answers **410 Gone**, not 409 — the action was valid, the opportunity has passed.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Action undone.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["undone"]},"undoSnapshotJson":{"type":["string","null"]}},"required":["id","status","undoSnapshotJson"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such action.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The action is not in an undoable state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"The undo window has expired. The change is now permanent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/actions/{id}/reject":{"post":{"tags":["AI"],"operationId":"rejectAiAction","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Reject a proposed AI action","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejectActionRequest"}}}},"responses":{"200":{"description":"Action rejected.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["rejected"]}},"required":["id","status"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such action.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The action is not in a proposed state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/actions":{"get":{"tags":["AI"],"operationId":"listAiActions","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List AI actions","description":"With `contractId`, the log for that contract, gated on access to it. WITHOUT it, only actions on contracts the caller created or is a party to — this scoping is load-bearing, since a row carries `undoSnapshotJson`, the full pre-edit document. Contract-less (global) actions are excluded from the unscoped list by design. Capped at 100, newest first.","parameters":[{"schema":{"type":"string","description":"Limit to one contract."},"required":false,"name":"contractId","in":"query"}],"responses":{"200":{"description":"Recent actions.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/AiAction"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid contractId.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/conversations":{"post":{"tags":["AI"],"operationId":"createAiConversation","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Open an assistant conversation","description":"Org-scoped. May be docked to a contract, or global when `contractId` is null.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contractId":{"type":"integer","exclusiveMinimum":0}}}}}},"responses":{"201":{"description":"Conversation created.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"conversationId":{"type":"string"}},"required":["conversationId"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or no access to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/runs":{"post":{"tags":["AI"],"operationId":"createAiRun","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Plan an agentic run","description":"Produces a PLAN only — nothing executes until POST /ai/runs/{id}/start. `autonomyLevel` decides where the run will pause for a human.\n\nRequires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contractId":{"type":"integer","exclusiveMinimum":0},"goal":{"type":"string","minLength":1,"maxLength":2000},"autonomyLevel":{"type":"string","enum":["observe","propose","confirm","autonomous","confirm_all","confirm_gated","auto"]}},"required":["contractId","goal"]}}}},"responses":{"201":{"description":"Run planned, not started.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"runId":{"type":"string"},"plan":{"type":"array","items":{"type":"string"}}},"required":["runId","plan"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or no access to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"AI features are not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/runs/{id}":{"get":{"tags":["AI"],"operationId":"getAiRun","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Get run state and steps","description":"Readable with no AI key configured — this is stored state, not a model call.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The run and its steps.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/AiRun"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such run in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/runs/{id}/abort":{"post":{"tags":["AI"],"operationId":"abortAiRun","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Abort a run","description":"Marks the run aborted. Steps already executed are NOT rolled back — undo those individually through the actions endpoints.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Run aborted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["aborted"]}},"required":["id","status"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such run in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/engine/ai-rights":{"post":{"tags":["AI"],"operationId":"runAiRightsEngine","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Run the AI-rights engine","description":"Detects AI training and rights issues in the contract body.\n\nFindings are persisted, REPLACING this engine's previous findings for the contract, so the stored set always reflects the latest run.\n\nRequires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contractId":{"type":"integer","exclusiveMinimum":0}},"required":["contractId"]}}}},"responses":{"200":{"description":"Findings, exactly as emitted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/EngineFindings"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or no access to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"AI features are not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/engine/pro-mechanical":{"post":{"tags":["AI"],"operationId":"runProMechanicalEngine","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Run the PRO/mechanical engine","description":"Checks performing-rights and mechanical-royalty handling.\n\nFindings are persisted, REPLACING this engine's previous findings for the contract, so the stored set always reflects the latest run.\n\nRequires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contractId":{"type":"integer","exclusiveMinimum":0}},"required":["contractId"]}}}},"responses":{"200":{"description":"Findings, exactly as emitted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/EngineFindings"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or no access to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"AI features are not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/engine/risk":{"post":{"tags":["AI"],"operationId":"runRiskEngine","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Run the risk engine","description":"Flags clauses that carry disproportionate risk.\n\nFindings are persisted, REPLACING this engine's previous findings for the contract, so the stored set always reflects the latest run.\n\nRequires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contractId":{"type":"integer","exclusiveMinimum":0}},"required":["contractId"]}}}},"responses":{"200":{"description":"Findings, exactly as emitted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/EngineFindings"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or no access to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"AI features are not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/engine/consistency":{"post":{"tags":["AI"],"operationId":"runConsistencyEngine","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Run the consistency engine","description":"Finds internal contradictions between clauses and deal terms.\n\nFindings are persisted, REPLACING this engine's previous findings for the contract, so the stored set always reflects the latest run.\n\nRequires `ANTHROPIC_API_KEY` on the deployment; answers 503 without it. The key check runs AFTER body validation, so a malformed request still yields 400.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contractIds":{"type":"array","items":{"type":"integer","exclusiveMinimum":0},"minItems":2,"maxItems":20}},"required":["contractIds"]}}}},"responses":{"200":{"description":"Findings, exactly as emitted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/EngineFindings"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or no access to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"AI features are not configured on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/ai/engine/split-fairness":{"post":{"tags":["AI"],"operationId":"runSplitFairnessEngine","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Run the split-fairness engine","description":"Compares split allocations against curated industry benchmarks.\n\nFindings are persisted, REPLACING this engine's previous findings for the contract, so the stored set always reflects the latest run.\n\nRuns with no AI key configured — the analysis is arithmetic over stored splits, not a model call.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contractId":{"type":"integer","exclusiveMinimum":0}},"required":["contractId"]}}}},"responses":{"200":{"description":"Findings, exactly as emitted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/EngineFindings"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or no access to it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/workflows":{"get":{"tags":["Approval workflows"],"operationId":"listWorkflows","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List approval workflows","description":"Scoped to the active organization, newest first.\n\n`status` accepts two UI aggregates alongside the raw enum values: `pending` matches `in_progress` OR `pending`, and `completed` matches `approved` OR `rejected`. Any other value is passed through to the status column as-is.","parameters":[{"schema":{"type":["integer","null"]},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"A workflow status, or the aggregate `pending` / `completed`.","example":"pending"},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"A page of workflows, newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowListItem"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/workflows/{id}":{"get":{"tags":["Approval workflows"],"operationId":"getWorkflow","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Get a workflow","description":"Includes every step in `stepOrder` order, plus a contract summary.","parameters":[{"schema":{"type":["integer","null"],"example":5},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"The workflow.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/WorkflowDetail"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such workflow in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/workflows/{id}/approve":{"post":{"tags":["Approval workflows"],"operationId":"approveWorkflowStep","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Approve the current step","description":"The caller must be the step's designated approver, hold its `approverRole`, or be an org admin. Approving the LAST step approves the workflow, moves the contract to `pending_signatures`, and emails sign links to any external parties.","parameters":[{"schema":{"type":["integer","null"],"example":5},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"stepOrder":{"type":"integer","minimum":1},"comments":{"type":"string"}}}}}},"responses":{"200":{"description":"Step approved.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"workflowStatus":{"type":"string","enum":["approved","in_progress"]},"allApproved":{"type":"boolean"},"stepOrder":{"type":"integer"}},"required":["workflowStatus","allApproved","stepOrder"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"The workflow is not in progress, or the step is already decided.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such workflow, or no pending step.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/workflows/{id}/reject":{"post":{"tags":["Approval workflows"],"operationId":"rejectWorkflowStep","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Reject the current step","description":"Rejecting any step rejects the whole workflow — remaining steps are never reached. Same approver rules as approve.","parameters":[{"schema":{"type":["integer","null"],"example":5},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"stepOrder":{"type":"integer","minimum":1},"comments":{"type":"string","minLength":1}},"required":["comments"]}}}},"responses":{"200":{"description":"Step rejected, and with it the workflow.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"workflowStatus":{"type":"string","enum":["rejected"]},"stepOrder":{"type":"integer"}},"required":["workflowStatus","stepOrder"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"The workflow is not in progress, or the step is already decided.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such workflow, or no pending step.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/subscriptions":{"get":{"tags":["Webhooks"],"operationId":"listWebhookSubscriptions","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List outbound webhook subscriptions","description":"Returns all active webhook endpoint subscriptions registered for the current organization.","parameters":[{"schema":{"type":["integer","null"]},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"List of webhook subscriptions.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookSubscription"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Webhooks"],"operationId":"createWebhookSubscription","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Create an outbound webhook subscription","description":"Registers a URL to receive signature lifecycle event webhooks. If no secret is provided, a secure `whsec_` secret is generated automatically.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookSubscription"}}}},"responses":{"201":{"description":"Created webhook subscription.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/WebhookSubscription"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid subscription parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/subscriptions/{id}":{"get":{"tags":["Webhooks"],"operationId":"getWebhookSubscription","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Get a webhook subscription","description":"Fetches details of a specific webhook subscription.","parameters":[{"schema":{"type":["integer","null"],"example":1},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Webhook subscription details.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/WebhookSubscription"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Webhook subscription or delivery not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Webhooks"],"operationId":"updateWebhookSubscription","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Update a webhook subscription","description":"Updates endpoint URL, subscribed events, description, or active status.","parameters":[{"schema":{"type":["integer","null"],"example":1},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookSubscription"}}}},"responses":{"200":{"description":"Updated webhook subscription.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/WebhookSubscription"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Webhook subscription or delivery not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Webhooks"],"operationId":"deleteWebhookSubscription","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Delete a webhook subscription","description":"Soft-deletes a webhook subscription; no further events will be delivered.","parameters":[{"schema":{"type":["integer","null"],"example":1},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Webhook subscription deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]}},"required":["deleted"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Webhook subscription or delivery not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/subscriptions/{id}/rotate-secret":{"post":{"tags":["Webhooks"],"operationId":"rotateWebhookSubscriptionSecret","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Rotate webhook signing secret","description":"Generates a new HMAC signing secret for the subscription and updates it immediately. Future webhooks will be signed using the new secret.","parameters":[{"schema":{"type":["integer","null"],"example":1},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"New signing secret.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/RotateSecretResponse"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Webhook subscription or delivery not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/subscriptions/{id}/test":{"post":{"tags":["Webhooks"],"operationId":"testWebhookSubscription","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Send a test ping webhook","description":"Dispatches a synthetic `test.ping` event to the subscription endpoint, records the delivery attempt, and returns the delivery result.","parameters":[{"schema":{"type":["integer","null"],"example":1},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Test delivery result.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/TestDeliveryResponse"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Webhook subscription or delivery not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/deliveries":{"get":{"tags":["Webhooks"],"operationId":"listWebhookDeliveries","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List webhook delivery attempts","description":"Returns the delivery log across webhook endpoints, newest first.","parameters":[{"schema":{"type":["integer","null"]},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"]},"required":false,"name":"subscriptionId","in":"query"},{"schema":{"type":"string","enum":["success","failed"]},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"List of webhook delivery attempts.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/deliveries/{id}/retry":{"post":{"tags":["Webhooks"],"operationId":"retryWebhookDelivery","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Retry a webhook delivery","description":"Re-dispatches an earlier webhook delivery payload and logs a new attempt.","parameters":[{"schema":{"type":["integer","null"],"example":1},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Retry delivery result.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/TestDeliveryResponse"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Failed to retry delivery.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Webhook subscription or delivery not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/royalti/connect":{"post":{"tags":["Royalti.io"],"operationId":"connectRoyalti","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Connect a Royalti.io workspace","description":"Requires `royalti:write` — admin-only. The integration authenticates with a workspace API key pasted by an admin and stored encrypted. There is no OAuth and no inbound webhook, so every confirmation here is SYNCHRONOUS — nothing arrives later out of band.\n\nThe key is verified against Royalti before anything is stored, so a bad key fails here rather than silently later.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"apiKey":{"type":"string","minLength":1}},"required":["apiKey"]}}}},"responses":{"200":{"description":"Connected.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"connected":{"type":"boolean","enum":[true]},"workspaceName":{"type":"string"}},"required":["connected","workspaceName"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"The API key could not be verified.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server-side encryption is not configured, so no key can be stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Royalti.io was unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/royalti/disconnect":{"post":{"tags":["Royalti.io"],"operationId":"disconnectRoyalti","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Disconnect Royalti.io","description":"Requires `royalti:write` — admin-only. Symmetric with connect on purpose: someone who cannot create a workspace-wide integration must not be able to tear one down.","responses":{"200":{"description":"Disconnected.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]}},"required":["success"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/royalti/status":{"get":{"tags":["Royalti.io"],"operationId":"getRoyaltiStatus","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Integration and per-contract sync status","description":"Connection state, revenue aggregates, and EVERY syncable contract with its real state — errored, synced, or pending — not just the synced ones. An errored contract can be retried individually.","responses":{"200":{"description":"Integration status.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/RoyaltiStatus"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/royalti/events":{"get":{"tags":["Royalti.io"],"operationId":"listRoyaltiEvents","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Sync event log","description":"Royalti-related audit entries for this organization. Entries the integration made itself are attributed to 'Royalti.io' rather than a user.","responses":{"200":{"description":"Recent sync events.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/RoyaltiEvent"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/royalti/contracts/{id}/retry-sync":{"post":{"tags":["Royalti.io"],"operationId":"retryRoyaltiSync","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Retry syncing one contract","description":"Answers 200 even when the push FAILS — read `pushed` and `error`, not the status code. The call succeeded; the sync may not have.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"Attempt made. Check `pushed`.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"pushed":{"type":"boolean"},"error":{"type":["string","null"]}},"required":["pushed","error"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid contract id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is not visible to the caller.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/royalti/contracts/{id}/link":{"post":{"tags":["Royalti.io"],"operationId":"linkRoyaltiAsset","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Link a contract to a Royalti asset","description":"Attaches an ISRC, or clears it with null. A contract cannot sync until it is linked — this is what separates \"needs an asset\" from \"pending sync\".","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"isrc":{"type":["string","null"]}},"required":["isrc"]}}}},"responses":{"200":{"description":"Link updated.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"linked":{"type":"boolean","enum":[true]}},"required":["linked"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid contract id or body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract, or it is not visible to the caller.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/royalti/sync":{"post":{"tags":["Royalti.io"],"operationId":"syncRoyaltiSplits","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Push splits for every eligible contract","description":"Active, linked, not-yet-synced contracts only. `attempted` is what distinguishes \"nothing to sync\" (0/0/0) from \"everything failed\" (attempted high, pushed 0) — a distinction `pushed`/`failed` alone cannot make.","responses":{"200":{"description":"Sync run complete.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"pushed":{"type":"integer"},"failed":{"type":"integer"},"attempted":{"type":"integer"}},"required":["pushed","failed","attempted"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/royalti/revenue/refresh":{"post":{"tags":["Royalti.io"],"operationId":"refreshRoyaltiRevenue","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Pull the latest revenue figures","description":"Polls Royalti and persists a snapshot — there is no revenue webhook, so figures are only as fresh as the last call to this.\n\n`updated` counts assets that RETURNED revenue, not calls that succeeded. As with sync, `attempted` separates \"nothing to refresh\" from \"every call was rejected\".","responses":{"200":{"description":"Refresh complete.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"updated":{"type":"integer"},"attempted":{"type":"integer"},"failed":{"type":"integer"}},"required":["updated","attempted","failed"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/royalti/fairness/{contractId}":{"get":{"tags":["Royalti.io"],"operationId":"getSplitFairness","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Compare splits against industry benchmarks","description":"Returns a verdict and a signed delta from market per role.\n\nThis is a comparison against a curated benchmark set, NOT advice and not a judgement about a specific deal. When `message` is present the analysis could not be grounded — a contract with no splits, for instance — and the verdict should not be read as evidence.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"contractId","in":"path"}],"responses":{"200":{"description":"The fairness analysis.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/FairnessResult"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid contract id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/royalti/splits":{"get":{"tags":["Royalti.io"],"operationId":"exportSplits","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Export a contract's splits and parties","description":"The payload Royalti receives on sync, in the shape it is sent.","parameters":[{"schema":{"type":["integer","null"],"description":"Required."},"required":false,"name":"contractId","in":"query"}],"responses":{"200":{"description":"Splits and parties.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/SplitsExport"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Missing or invalid contractId.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/royalti/contracts/{id}/export":{"get":{"tags":["Royalti.io"],"operationId":"exportContractForRoyalti","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Export a contract with its sync state","description":"The full contract with parties, splits, obligations and versions, plus a `royaltiSync` block summarising its integration state.","parameters":[{"schema":{"type":["integer","null"],"example":12},"required":false,"name":"id","in":"path"}],"responses":{"200":{"description":"The contract and its sync state. Deliberately unmodelled — this is an export envelope over several relations.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/RoyaltiContractExport"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid contract id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such contract.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/integrations/google-drive/oauth/start":{"get":{"tags":["Google Drive"],"operationId":"startGoogleDriveOAuth","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Begin the Google Drive OAuth flow","description":"Redirects the browser to Google's consent screen with a signed state parameter. Not callable as an API — open it in a browser.","responses":{"302":{"description":"Redirect to Google's consent screen."},"503":{"description":"Google OAuth is not configured on this deployment."}}}},"/integrations/google-drive/oauth/callback":{"get":{"tags":["Google Drive"],"operationId":"completeGoogleDriveOAuth","security":[],"summary":"OAuth callback","description":"Where Google returns the user. Exchanges the code, stores the encrypted tokens, and redirects back to the web app. ALWAYS answers 302 — failures are reported as an `error` query parameter on the redirect target, never as a status code.","responses":{"302":{"description":"Redirect back to the web app, success or failure."}}}},"/integrations/google-drive/status":{"get":{"tags":["Google Drive"],"operationId":"getGoogleDriveStatus","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Is Google Drive connected?","description":"Per-organization, not per-user — one connection serves the whole workspace.","responses":{"200":{"description":"Connection state.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"connected":{"type":"boolean"}},"required":["connected"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/integrations/google-drive/files":{"get":{"tags":["Google Drive"],"operationId":"listGoogleDriveFiles","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List importable Drive files","description":"The 30 most recently modified files, filtered to the document types this app can import (plain text, Google Docs, DOCX).\n\nThe connection uses the `drive.file` scope, so this sees ONLY files the user explicitly picked for this app — not their whole Drive.","responses":{"200":{"description":"Recent importable files.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/DriveFile"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Google Drive is not connected for this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Google Drive rejected the request or was unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/integrations/google-drive/import":{"post":{"tags":["Google Drive"],"operationId":"importFromGoogleDrive","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Import a Drive file","description":"Downloads the file, stores it, and creates a media record — the same end state as a direct upload, so the returned `mediaId` can be handed straight to POST /contracts/imports.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DriveImportRequest"}}}},"responses":{"201":{"description":"File imported and recorded as media.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"mediaId":{"type":"integer"},"name":{"type":"string"},"size":{"type":"integer"},"mimeType":{"type":"string"},"driveFileId":{"type":"string"}},"required":["mediaId","name","size","mimeType","driveFileId"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Not connected, or an invalid file id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such file in the connected Drive.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Google Drive rejected the request or was unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/integrations/google-drive/disconnect":{"delete":{"tags":["Google Drive"],"operationId":"disconnectGoogleDrive","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Disconnect Google Drive","description":"Marks the connection disconnected. Answers 200 whether or not one existed, and does NOT revoke the grant at Google — the user must do that in their Google account.","responses":{"200":{"description":"Disconnected.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"disconnected":{"type":"boolean","enum":[true]}},"required":["disconnected"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/users":{"get":{"tags":["Admin"],"operationId":"listAdminUsers","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"List workspace members","description":"Requires the `admin` professional role in the active workspace. Everything here is scoped to that workspace — there is no cross-tenant admin surface.\n\nEach row carries the count of contracts that member created here.","parameters":[{"schema":{"type":"string","description":"User id from the previous page."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"A page of members.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminUser"}},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/users/{id}":{"patch":{"tags":["Admin"],"operationId":"updateAdminUserRole","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Change a member's professional role","description":"Requires the `admin` professional role in the active workspace. Everything here is scoped to that workspace — there is no cross-tenant admin surface.\n\nSets the per-workspace role, so the same user can hold different roles in different workspaces. The workspace's LAST admin cannot be demoted — that would leave nobody able to manage it.","parameters":[{"schema":{"type":"string","description":"Target user id."},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["admin","artist","manager","lawyer","producer","publisher","label"]}},"required":["role"]}}}},"responses":{"200":{"description":"Role updated.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"userId":{"type":"string"},"organizationId":{"type":"string"},"role":{"type":"string","enum":["admin","artist","manager","lawyer","producer","publisher","label"]}},"required":["userId","organizationId","role"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Invalid body, or an attempt to demote the last admin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such member in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Admin"],"operationId":"removeAdminUser","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Remove a member from the workspace","description":"Requires the `admin` professional role in the active workspace. Everything here is scoped to that workspace — there is no cross-tenant admin surface.\n\nRemoves the MEMBERSHIP only. The user account and their global profile survive, because both are shared with every other workspace they belong to. The last admin cannot be removed.","parameters":[{"schema":{"type":"string","description":"Target user id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Membership removed.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Attempted to remove the last admin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such member in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/metrics":{"get":{"tags":["Admin"],"operationId":"getAdminMetrics","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Workspace metrics","description":"Requires the `admin` professional role in the active workspace. Everything here is scoped to that workspace — there is no cross-tenant admin surface.\n\nNote `storageUsedMB` is an ESTIMATE (~50 KB per contract), not a measurement of object storage.","responses":{"200":{"description":"Aggregated metrics.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/AdminMetrics"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/activity":{"get":{"tags":["Admin"],"operationId":"getAdminActivity","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Workspace-wide activity feed","description":"Requires the `admin` professional role in the active workspace. Everything here is scoped to that workspace — there is no cross-tenant admin surface.\n\nIncludes entries with no contract (role changes, template publishes) as well as those tied to a live contract in this workspace.","parameters":[{"schema":{"type":["integer","null"]},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"Defaults to 100, which is also the cap."},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"A page of activity.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"activity":{"type":"array","items":{"$ref":"#/components/schemas/AdminActivityEntry"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"null"},{"type":"null"}]}},"required":["activity","nextCursor"]},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/system/health":{"get":{"tags":["Admin"],"operationId":"getAdminSystemHealth","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Database connectivity check","description":"Requires the `admin` professional role in the active workspace. Everything here is scoped to that workspace — there is no cross-tenant admin surface.\n\nLegacy. The unauthenticated GET /health serves the same purpose and is what uptime probes should point at.","responses":{"200":{"description":"Database reachable.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/SystemHealth"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/search":{"get":{"tags":["Search"],"operationId":"search","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Search contracts, templates and people","description":"Case-insensitive substring match, scoped to the active organization, capped at 8 results per group. Contracts follow the same visibility rule as the contracts list: admins see the whole org, everyone else only what they created or are a party on. Templates and people are org-scoped for all callers.","parameters":[{"schema":{"type":"string","description":"Search term. Required and non-empty."},"required":true,"name":"q","in":"query"}],"responses":{"200":{"description":"Up to 8 matches per group.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/SearchResults"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"400":{"description":"Missing or empty `q`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/dashboard/summary":{"get":{"tags":["Dashboard"],"operationId":"getDashboardSummary","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"summary":"Aggregates for the main dashboard","description":"Everything the home page renders, in one call: counts, a draft to resume, recent contracts, the pending-actions queue, and a short activity feed.\n\nScoped to the active organization and, for non-admins, to contracts the caller created or is a party on — so two members of the same org legitimately see different numbers. This is a VIEW MODEL: ids are strings, timestamps are pre-formatted, messages are pre-rendered, and `progress` is a crude heuristic over content length rather than a real measure of readiness.","responses":{"200":{"description":"The dashboard aggregates.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/DashboardSummary"},"meta":{"$ref":"#/components/schemas/Meta"}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but lacking the required permission or organization context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unhandled server error. Carries a requestId for support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{}}