{"components":{"schemas":{"AcceptInviteRequest":{"properties":{"display_name":{"description":"What the invitee wants to be called.\n\nThe invitation carries an ADDRESS, not a name, so this is the first\nmoment a person can state their own name for their own account. Absent\nor blank leaves whatever the inviter recorded untouched, so an older\nclient (or a link opened by someone who did not fill the field in)\nchanges nothing rather than blanking a name somebody else set.","type":["string","null"]},"password":{"description":"First password (argon2-hashed server-side).","type":"string"},"timezone":{"type":["string","null"]},"token":{"description":"The one-time setup token from the invite response.","type":"string"}},"required":["token","password"],"type":"object"},"AcceptObservedRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first mutation parameters (IF1)."},{"properties":{"field_key":{"type":"string"}},"required":["field_key"],"type":"object"}]},"Actor":{"description":"Who performed the audited action. Append-only provenance record.\nPortal variants carry the client-side tier so audit trails distinguish\nstaff actions from client-portal actions at the storage layer\n(actor_kind portal_contact / portal_approver / portal_it_manager).","oneOf":[{"properties":{"id":{"format":"uuid","type":"string"},"kind":{"enum":["human"],"type":"string"}},"required":["id","kind"],"type":"object"},{"properties":{"id":{"type":"string"},"kind":{"enum":["agent"],"type":"string"}},"required":["id","kind"],"type":"object"},{"properties":{"id":{"type":"string"},"kind":{"enum":["plugin"],"type":"string"}},"required":["id","kind"],"type":"object"},{"properties":{"id":{"type":"string"},"kind":{"enum":["connector"],"type":"string"}},"required":["id","kind"],"type":"object"},{"properties":{"kind":{"enum":["system"],"type":"string"}},"required":["kind"],"type":"object"},{"properties":{"id":{"format":"uuid","type":"string"},"kind":{"enum":["portal_contact"],"type":"string"}},"required":["id","kind"],"type":"object"},{"properties":{"id":{"format":"uuid","type":"string"},"kind":{"enum":["portal_approver"],"type":"string"}},"required":["id","kind"],"type":"object"},{"properties":{"id":{"format":"uuid","type":"string"},"kind":{"enum":["portal_it_manager"],"type":"string"}},"required":["id","kind"],"type":"object"},{"description":"Bulk-import runs (the private build tracker D2): the descriptor names the driving\nidentity without pretending it row-walked — `\"user:<uuid>\"` for\nadmin JWT runs, `\"api-key:<key-id>\"\"` for machine pushes. Row-level\nmutations still emit their ordinary domain events; the import actor\nowns batch-lifecycle provenance (kind \"import\" in audit storage).","properties":{"id":{"description":"Bulk-import runs (the private build tracker D2): the descriptor names the driving\nidentity without pretending it row-walked — `\"user:<uuid>\"` for\nadmin JWT runs, `\"api-key:<key-id>\"\"` for machine pushes. Row-level\nmutations still emit their ordinary domain events; the import actor\nowns batch-lifecycle provenance (kind \"import\" in audit storage).","type":"string"},"kind":{"enum":["import"],"type":"string"}},"required":["id","kind"],"type":"object"}]},"AddChecklistItemRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive."},{"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"}]},"AddCommentRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key. Absent in\nlegacy clients — purely additive. (Comments are fresh entities, so\nthere is no version to match.)"},{"properties":{"attachment_ids":{"description":"Attachments uploaded earlier against this ticket (composer flow:\nupload first, link when the comment is created). Absent/empty in\nolder clients — purely additive.","items":{"format":"uuid","type":"string"},"type":"array"},"body":{"type":"string"},"draft_key":{"description":"IF2 delete-on-submit (Wave 17 lane 11, §5.2): the composer autosave\ndraft's idempotency key. When a comment with this key commits, the\nauthor's `note_drafts` row for the ticket is deleted in the SAME\ntransaction — the draft never resurrects after submit. Absent in\nlegacy clients — purely additive.","type":["string","null"]},"email_envelope":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/EmailEnvelopeOverride","description":"Per-message greeting/signature the composer wrote (Wave 42). Never a\nstored preference. A locked org refuses it: an override is a\npersonal signature that was never stored, so a workspace that\nlocked signatures must not be routable around one."}]},"kind":{"$ref":"#/components/schemas/NoteKind"}},"required":["body","kind"],"type":"object"}]},"AddKitMemberRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"qty_milli":{"description":"Whole units of the member tool (defaults to one unit = 1000 milli).","format":"int64","type":["integer","null"]},"stock_item_id":{"format":"uuid","type":"string"}},"required":["stock_item_id"],"type":"object"}],"description":"POST /api/tool-kits/{id}/members."},"AddMemberRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"user_id":{"format":"uuid","type":"string"}},"required":["user_id"],"type":"object"}]},"AddOrderLineRequest":{"allOf":[{"$ref":"#/components/schemas/OrderLineInput"},{"$ref":"#/components/schemas/MutationParams"}],"description":"POST /purchase-orders/{id}/lines body: the line plus IF1 params."},"AdjustRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"item_id":{"format":"uuid","type":"string"},"location_id":{"format":"uuid","type":"string"},"note":{"type":["string","null"]},"qty_milli":{"description":"SIGNED milli delta (negative = shrink). The counter adjustment is\nthe audit trail, not a rewrite.","format":"int64","type":"integer"}},"required":["item_id","location_id","qty_milli"],"type":"object"}]},"AgentProposalPage":{"description":"GET /api/agent-proposals envelope.","properties":{"next_cursor":{"type":["string","null"]},"proposals":{"items":{"$ref":"#/components/schemas/AgentProposalView"},"type":"array"}},"required":["proposals"],"type":"object"},"AgentProposalView":{"description":"One IF8 proposal with the full snapshot the approval decision needs.","properties":{"agent_kind":{"type":"string"},"agent_ref":{"type":"string"},"applied_at":{"type":["string","null"]},"base_version":{"description":"The entity write-seq the proposal was created against.","format":"int64","type":"integer"},"before_value":{"description":"The value the agent saw (`null` when the record did not exist).","type":["object","null"]},"capability":{"type":["string","null"]},"classification":{"$ref":"#/components/schemas/ProposalClassification"},"created_at":{"type":"string"},"decided_at":{"type":["string","null"]},"decided_by":{"format":"uuid","type":["string","null"]},"decision_note":{"type":["string","null"]},"dial_mode":{"$ref":"#/components/schemas/CapabilityMode","description":"Involvement-dial snapshot at proposal time."},"entity":{"description":"Record coordinates (v1 applies `ticket`/`update` only; the store is\ngeneric).","type":"string"},"entity_id":{"format":"uuid","type":["string","null"]},"for_user":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"op":{"type":"string"},"proposed_value":{"description":"The proposed after value.","type":"object"},"rationale":{"type":["string","null"]},"rule_set_hash":{"type":["string","null"]},"session_id":{"type":["string","null"]},"skill_slug":{"type":["string","null"]},"skill_version":{"format":"int32","type":["integer","null"]},"status":{"$ref":"#/components/schemas/ProposalStatus"},"tool":{"type":["string","null"]},"updated_at":{"type":"string"}},"required":["id","entity","op","before_value","proposed_value","classification","dial_mode","base_version","status","agent_kind","agent_ref","created_at","updated_at"],"type":"object"},"AgentSkillList":{"properties":{"skills":{"items":{"$ref":"#/components/schemas/AgentSkillView"},"type":"array"}},"required":["skills"],"type":"object"},"AgentSkillVersion":{"description":"One skill VERSION row (the migration-0040 shape + the distribution\ncolumns from migration 0079). History is append-only; rollback\nappends a NEW version carrying an old manifest. Enablement rides the\norg's active version row — deny-by-default.","properties":{"artifact_digest":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"description":{"type":"string"},"enabled":{"description":"Deny-by-default enable gate on the active row.","type":"boolean"},"enabled_at":{"format":"date-time","type":["string","null"]},"enabled_by":{"format":"uuid","type":["string","null"]},"fetched_ref":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"manifest":{"$ref":"#/components/schemas/SkillManifest"},"org_id":{"format":"uuid","type":"string"},"slug":{"type":"string"},"source_kind":{"$ref":"#/components/schemas/PluginSourceKind"},"source_url":{"type":"string"},"version":{"format":"int32","type":"integer"}},"required":["id","org_id","slug","version","manifest","description","source_kind","source_url","artifact_digest","enabled","created_at"],"type":"object"},"AgentSkillView":{"description":"One immutable skill version.","properties":{"created_at":{"type":"string"},"created_by":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"manifest":{"description":"`{capabilities, prompts, rules}` — declarative manifest (§4).","type":"object"},"slug":{"type":"string"},"version":{"format":"int32","type":"integer"}},"required":["id","slug","version","manifest","created_by","created_at"],"type":"object"},"Aggregate":{"description":"Aggregation function of a measure. `Count` may omit the column\n(`COUNT(*)`); every other aggregate requires one, and the catalog\ndecides which column types each accepts.","enum":["count","sum","avg","min","max"],"type":"string"},"AiAllowedModelDto":{"properties":{"label":{"type":"string"},"model":{"type":"string"},"provider":{"type":"string"}},"required":["provider","model","label"],"type":"object"},"AiAuthorizeRequest":{"properties":{"model":{"type":["string","null"]},"provider":{"description":"Optional explicit (provider, model) ask — must sit inside the\nadmin allowlist (an out-of-list ask is REFUSED, never redirected).","type":["string","null"]},"surface_key":{"type":"string"}},"required":["surface_key"],"type":"object"},"AiAuthorizeResponse":{"properties":{"caps":{"$ref":"#/components/schemas/AiCapState"},"credential":{"$ref":"#/components/schemas/AiAuthorizedCredential"},"model":{"type":"string"},"provider":{"type":"string"},"resolution":{"description":"`requested` | `user_pick` | `org_pick` | `legacy_org_default` |\n`catalog_first`.","type":"string"},"surface_key":{"type":"string"},"warnings":{"description":"Non-empty when a SOFT cap was crossed: the call proceeds; the\nwarn also rides an `ai_cap_warn` notification.","items":{"type":"string"},"type":"array"}},"required":["surface_key","provider","model","resolution","credential","caps","warnings"],"type":"object"},"AiAuthorizedCredential":{"properties":{"id":{"format":"uuid","type":"string"},"kind":{"type":"string"},"personal":{"description":"true when a personal (per-user) credential was selected.","type":"boolean"}},"required":["id","kind","personal"],"type":"object"},"AiCapDto":{"properties":{"hard_token_limit":{"format":"int64","type":["integer","null"]},"id":{"format":"uuid","type":"string"},"period":{"type":"string"},"soft_token_limit":{"format":"int64","type":["integer","null"]},"user_id":{"type":["string","null"]}},"required":["id","user_id","period","soft_token_limit","hard_token_limit"],"type":"object"},"AiCapMutationResponse":{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"status":{"$ref":"#/components/schemas/AiCapDto"}},"required":["status","mutation"],"type":"object"},"AiCapState":{"properties":{"hard_token_limit":{"format":"int64","type":["integer","null"]},"pct_hard":{"format":"double","type":["number","null"]},"soft_token_limit":{"format":"int64","type":["integer","null"]},"used_tokens":{"format":"int64","type":"integer"}},"required":["used_tokens","soft_token_limit","hard_token_limit","pct_hard"],"type":"object"},"AiCatalogDto":{"properties":{"capabilities":{},"cost_weight":{"format":"int32","type":"integer"},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"label":{"type":"string"},"model":{"type":"string"},"provider":{"type":"string"}},"required":["id","provider","model","label","capabilities","cost_weight","enabled"],"type":"object"},"AiCatalogMutationResponse":{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"status":{"$ref":"#/components/schemas/AiCatalogDto"}},"required":["status","mutation"],"type":"object"},"AiCredentialDto":{"description":"Presence-only credential descriptor — never secret material.","properties":{"credential_meta":{},"id":{"format":"uuid","type":"string"},"kind":{"description":"`api_key` | `oauth_token` | `subscription_account`.","type":"string"},"label":{"type":"string"},"owner_user_id":{"type":["string","null"]},"provider":{"type":"string"},"status":{"description":"`active` | `revoked`.","type":"string"}},"required":["id","owner_user_id","provider","kind","label","credential_meta","status"],"type":"object"},"AiCredentialMutationResponse":{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"status":{"$ref":"#/components/schemas/AiCredentialDto"}},"required":["status","mutation"],"type":"object"},"AiDecisionOperator":{"description":"How an `ai_decision` condition judges one recorded answer entry.","enum":["probability_above","level_at_least","confidence_above"],"type":"string"},"AiGrantDto":{"properties":{"grantee_kind":{"description":"`role` | `user`.","type":"string"},"id":{"format":"uuid","type":"string"},"role_id":{"type":["string","null"]},"surface_id":{"format":"uuid","type":"string"},"surface_key":{"type":"string"},"user_id":{"type":["string","null"]}},"required":["id","surface_id","surface_key","grantee_kind","role_id","user_id"],"type":"object"},"AiGrantMutationResponse":{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"status":{"$ref":"#/components/schemas/AiGrantDto"}},"required":["status","mutation"],"type":"object"},"AiMatrixResponse":{"properties":{"caps":{"items":{"$ref":"#/components/schemas/AiCapDto"},"type":"array"},"catalog":{"items":{"$ref":"#/components/schemas/AiCatalogDto"},"type":"array"},"credentials":{"items":{"$ref":"#/components/schemas/AiCredentialDto"},"type":"array"},"grants":{"items":{"$ref":"#/components/schemas/AiGrantDto"},"type":"array"},"personal_credentials_enabled":{"description":"The `ai.personal_credentials_enabled` admin policy flag state.","type":"boolean"},"surfaces":{"items":{"$ref":"#/components/schemas/AiSurfaceDto"},"type":"array"}},"required":["surfaces","grants","catalog","caps","credentials","personal_credentials_enabled"],"type":"object"},"AiMySurfacesResponse":{"properties":{"surfaces":{"items":{"$ref":"#/components/schemas/AiSurfaceAccessDto"},"type":"array"}},"required":["surfaces"],"type":"object"},"AiMyUsageResponse":{"properties":{"hard_token_limit":{"format":"int64","type":["integer","null"]},"pct_hard":{"format":"double","type":["number","null"]},"pct_soft":{"format":"double","type":["number","null"]},"period":{"type":"string"},"soft_token_limit":{"format":"int64","type":["integer","null"]},"used_tokens":{"format":"int64","type":"integer"}},"required":["period","used_tokens","soft_token_limit","hard_token_limit","pct_hard","pct_soft"],"type":"object"},"AiOrgUsageResponse":{"properties":{"active_users":{"format":"int64","type":"integer"},"hard_token_limit":{"format":"int64","type":["integer","null"]},"pct_hard":{"format":"double","type":["number","null"]},"period":{"type":"string"},"soft_token_limit":{"format":"int64","type":["integer","null"]},"tokens_by_surface":{"description":"surface → token totals for the period."},"used_tokens":{"format":"int64","type":"integer"}},"required":["period","used_tokens","active_users","soft_token_limit","hard_token_limit","pct_hard","tokens_by_surface"],"type":"object"},"AiPickMutationResponse":{"properties":{"model":{"type":"string"},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"provider":{"type":"string"},"scope":{"description":"`user` | `org`.","type":"string"},"surface_key":{"type":"string"}},"required":["surface_key","scope","provider","model","mutation"],"type":"object"},"AiPluginSurfaceMutationResponse":{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"status":{"$ref":"#/components/schemas/AiSurfaceDto"}},"required":["status","mutation"],"type":"object"},"AiPluginSurfacesResponse":{"properties":{"surfaces":{"items":{"$ref":"#/components/schemas/AiSurfaceDto"},"type":"array"}},"required":["surfaces"],"type":"object"},"AiSurfaceAccessDto":{"properties":{"granted":{"type":"boolean"},"kind":{"type":"string"},"label":{"type":"string"},"models":{"description":"The admin allowlist this surface resolves against — enabled\ncatalog entries as (provider, model, label). The per-surface\nmodel pickers list exactly these; the server still refuses an\noff-list ask typed (`ai_model_not_allowed`).","items":{"$ref":"#/components/schemas/AiAllowedModelDto"},"type":"array"},"models_available":{"type":"boolean"},"picked":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AiAllowedModelDto","description":"The caller's recorded sticky pick for this surface (so the picker\ndisplays what sticks); null = the org default still applies."}]},"surface_key":{"type":"string"}},"required":["surface_key","label","kind","granted","models_available","models"],"type":"object"},"AiSurfaceDto":{"properties":{"description":{"type":"string"},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"key":{"type":"string"},"kind":{"description":"`native` | `internal` | `plugin`.","type":"string"},"label":{"type":"string"},"org_id":{"type":["string","null"]}},"required":["id","org_id","key","label","kind","description","enabled"],"type":"object"},"AlertListQuery":{"properties":{"limit":{"format":"int64","type":"integer"},"offset":{"format":"int64","type":"integer"},"source_id":{"format":"uuid","type":["string","null"]},"status":{"description":"Triage filter (0089): \"open\" | \"acknowledged\" | \"resolved\" —\nabsent returns all states.","type":["string","null"]},"ticket_id":{"format":"uuid","type":["string","null"]}},"type":"object"},"AlertListResponse":{"properties":{"alerts":{"items":{"$ref":"#/components/schemas/AlertView"},"type":"array"}},"required":["alerts"],"type":"object"},"AlertMutationResponse":{"description":"Instant-first mutation response for a triage action (0089). The alert\nIS the read-your-write result; `mutation` carries the envelope metadata\n(idempotency replay, entity seq).","properties":{"alert":{"$ref":"#/components/schemas/AlertView"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["alert","mutation"],"type":"object"},"AlertSeverity":{"description":"Alert severity vocabulary. Maps onto ticket priorities when an alert\nanchors an incident ticket: critical→P1, warning→P2, info→P3.","enum":["info","warning","critical"],"type":"string"},"AlertSourceList":{"properties":{"sources":{"items":{"$ref":"#/components/schemas/AlertSourceView"},"type":"array"}},"required":["sources"],"type":"object"},"AlertSourceMutationResponse":{"allOf":[{"$ref":"#/components/schemas/AlertSourceView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"secret":{"type":["string","null"]}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response for a source (IF2). `secret` is the\nraw HMAC signing secret — populated ONLY by the create response and\nnever again (same show-once contract as webhooks/api-keys)."},"AlertSourceView":{"description":"An ingest source as the admin surface sees it. The HMAC secret is\nNEVER here — it is returned exactly once, in the create response.","properties":{"board_id":{"format":"uuid","type":"string"},"client_id":{"format":"uuid","type":["string","null"]},"correlation_window_secs":{"format":"int32","type":"integer"},"created_at":{"type":"string"},"enabled":{"type":"boolean"},"event_type":{"description":"When set, this source is a generic M2M webhook receiver: a signed\nPOST emits a domain-event envelope onto the outbox stream instead\nof creating an alert row (Wave 43 lane 1). NULL = alert source (Wave 9).","type":["string","null"]},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"payload_schema_ref":{"description":"Payload contract pin; NULL defaults to `core-domain/v1` at ingest.","type":["string","null"]},"source_ref":{"description":"Path segment of the ingest URL (public routing id).","type":"string"},"updated_at":{"type":"string"}},"required":["id","org_id","name","source_ref","board_id","correlation_window_secs","enabled","created_at","updated_at"],"type":"object"},"AlertView":{"description":"One normalized alert (canonical ingest document + ledger row view).","properties":{"acked_at":{"type":["string","null"]},"acked_by":{"format":"uuid","type":["string","null"]},"attributes":{"type":"object"},"correlation_key":{"description":"Storm grouping key (defaults to the dedup key server-side).","type":"string"},"dedup_key":{"description":"Provider's per-event identity; the replay gate per source.","type":"string"},"description":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"incident_ticket_id":{"format":"uuid","type":["string","null"]},"occurred_at":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"received_at":{"type":"string"},"resolved_at":{"type":["string","null"]},"resolved_by":{"format":"uuid","type":["string","null"]},"severity":{"type":"string"},"source_id":{"format":"uuid","type":"string"},"status":{"description":"Triage state (0089): \"open\" | \"acknowledged\" | \"resolved\".","type":"string"},"title":{"type":"string"}},"required":["id","org_id","source_id","dedup_key","correlation_key","title","severity","occurred_at","attributes","received_at","status"],"type":"object"},"ApiKeyCreatedResponse":{"description":"The ONLY response carrying raw key material. Lost keys are rotated by\ncreating a new key and revoking this one — never recovered.","properties":{"key":{"$ref":"#/components/schemas/ApiKeyView"},"raw_key":{"description":"Raw bearer material: `mrl_<32 hex>`.","type":"string"}},"required":["key","raw_key"],"type":"object"},"ApiKeyList":{"properties":{"keys":{"items":{"$ref":"#/components/schemas/ApiKeyView"},"type":"array"}},"required":["keys"],"type":"object"},"ApiKeyView":{"properties":{"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"last_used_at":{"format":"date-time","type":["string","null"]},"name":{"type":"string"},"prefix":{"description":"Non-secret display prefix, e.g. \"mrl_ab12cd34\".","type":"string"},"revoked_at":{"format":"date-time","type":["string","null"]},"scopes":{"items":{"type":"string"},"type":"array"}},"required":["id","name","prefix","scopes","created_at"],"type":"object"},"AppendVersionRequest":{"properties":{"manifest":{"$ref":"#/components/schemas/PluginManifestBody"},"version":{"type":"string"}},"required":["version","manifest"],"type":"object"},"ApplyCreditNoteRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"amount_minor":{"description":"Allocation amount, minor units; must not exceed the credit's\nremaining value.","format":"int64","type":"integer"},"invoice_id":{"description":"The invoice receiving the allocation (same client + currency).","format":"uuid","type":"string"}},"required":["invoice_id","amount_minor"],"type":"object"}],"description":"POST /api/credit-notes/{id}/apply body."},"ApplyTagRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"tag_id":{"format":"uuid","type":"string"}},"required":["tag_id"],"type":"object"}]},"ApplyWorkTypeMapRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"category_slug":{"type":"string"},"legacy_value":{"description":"Free-text value to recategorize (case-insensitive, trimmed).","type":"string"}},"required":["legacy_value","category_slug"],"type":"object"}]},"ApplyWorkTypeMapResponse":{"properties":{"entries_updated":{"format":"int64","type":"integer"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["entries_updated","mutation"],"type":"object"},"Appointment":{"description":"A booked slot on one user's calendar, optionally tied to a ticket and\nclient/site. `travel_minutes` is travel time booked immediately BEFORE\n`starts_at` (Wave 32 lane 7): it ADDS to the appointment and occupies\ndispatch capacity (`crate::scheduling::occupied_minutes`), never\nreplacing or shrinking the booked span. The `travel_*` fields carry\nthe estimate's provenance so a heuristic number is never mistaken for\na measured one.","properties":{"client_id":{"format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"ends_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"kind":{"$ref":"#/components/schemas/AppointmentKind"},"notes":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"scheduling_link_id":{"description":"Set when the booking came through a Calendly-style scheduling\nlink (Wave 11 lane 4); NULL for every other creation path.","format":"uuid","type":["string","null"]},"site_id":{"format":"uuid","type":["string","null"]},"starts_at":{"format":"date-time","type":"string"},"status":{"$ref":"#/components/schemas/AppointmentStatus"},"ticket_id":{"format":"uuid","type":["string","null"]},"title":{"type":"string"},"travel_distance_meters":{"description":"Provider-reported leg distance in metres.","format":"int32","type":["integer","null"]},"travel_from":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TravelOrigin","description":"Where the estimate travelled FROM; `None` when no provider\nprovenance exists (manual minutes or no estimate)."}]},"travel_minutes":{"format":"int32","type":"integer"},"travel_provider":{"description":"Provider id that produced `travel_minutes` (`none` = built-in\nheuristic); `None` for manual values.","type":["string","null"]},"travel_quality":{"description":"`heuristic` | `measured`; `None` whenever `travel_provider` is.","type":["string","null"]},"updated_at":{"format":"date-time","type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["id","org_id","user_id","title","starts_at","ends_at","kind","status","travel_minutes","created_at","updated_at"],"type":"object"},"AppointmentKind":{"description":"What an appointment is physically for. Drives rendering color-coding on\ncalendars and future travel-time logic (on_site only).","enum":["on_site","remote","internal","personal"],"type":"string"},"AppointmentRequest":{"description":"A client's request for a slot on one staff member's calendar.","properties":{"appointment_id":{"description":"The real booking created by approval (null until then).","format":"uuid","type":["string","null"]},"client_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"decided_at":{"format":"date-time","type":["string","null"]},"decided_by":{"format":"uuid","type":["string","null"]},"decision_note":{"description":"Note attached on approve/decline.","type":["string","null"]},"ends_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"kind":{"$ref":"#/components/schemas/AppointmentKind"},"notes":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"portal_user_id":{"description":"The requesting portal identity (NOT a staff row).","format":"uuid","type":"string"},"starts_at":{"format":"date-time","type":"string"},"status":{"$ref":"#/components/schemas/AppointmentRequestStatus"},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"user_id":{"description":"The staff member whose calendar was requested.","format":"uuid","type":"string"}},"required":["id","org_id","client_id","portal_user_id","user_id","title","starts_at","ends_at","kind","status","created_at","updated_at"],"type":"object"},"AppointmentRequestListQuery":{"properties":{"limit":{"format":"int64","type":"integer"},"offset":{"format":"int64","type":"integer"},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AppointmentRequestStatus","description":"Exact status: `pending | approved | declined | cancelled`."}]}},"type":"object"},"AppointmentRequestStatus":{"description":"Lifecycle of an appointment request. `approved`/`declined`/`cancelled`\nare terminal; approved requests carry the created appointment's id.","enum":["pending","approved","declined","cancelled"],"type":"string"},"AppointmentStatus":{"description":"Lifecycle of a booking. `cancelled` frees the slot (the DB exclusion\nconstraint's partial predicate); `done` still occupies it.","enum":["scheduled","in_progress","done","cancelled"],"type":"string"},"ApprovalStatus":{"description":"Approval lifecycle of a time entry (Wave 14 lane 1): `draft` entries\nare still being worked on, `submitted` waits in the approver queue,\n`approved` entries are the only ones billing reads may consume, and\n`rejected` entries carry a typed rejection reason until reopened.\nEntries consumed by an invoice are additionally LOCKED (enforced by\nthe persistence layer) regardless of status.","enum":["draft","submitted","approved","rejected"],"type":"string"},"ApproveStagedRequest":{"properties":{"granted_capabilities":{"description":"Capability keys granted at adopt time (a subset of the staged\nmanifest's declared set; unknown keys are refused).","items":{"type":"string"},"type":"array"},"review_note":{"type":["string","null"]}},"type":"object"},"ArchiveCatalogItemRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"}]},"ArchiveStockItemRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"}]},"ArticleDetail":{"allOf":[{"$ref":"#/components/schemas/KbArticle"},{"properties":{"content_html":{"description":"Sanitized server-rendered HTML (core-kb render whitelist).","type":["string","null"]}},"type":"object"}],"description":"The single-article read surface (GET): article fields at the top\nlevel plus the sanitized read-only HTML projection the web client\nrenders server-side — the editor package never ships in bundles that\nonly read."},"ArticleListQuery":{"properties":{"category_id":{"format":"uuid","type":["string","null"]},"client_visible":{"description":"Only articles passing the effective client-facing gate.","type":"boolean"},"q":{"description":"Title substring narrowing; ranked full-text search rides /api/search.","type":["string","null"]},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/KbArticleStatus"}]}},"type":"object"},"ArticleMutationResponse":{"allOf":[{"$ref":"#/components/schemas/KbArticle"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2)."},"AssetLifecycleInput":{"description":"Rental tracking + depreciation inputs on an asset (money is integer\nminor units + currency code per ADR 0002; no conversion happens here).","properties":{"depreciation_method":{"description":"none | straight_line | declining_balance","type":["string","null"]},"depreciation_rate_bps":{"description":"basis points for declining_balance (1..9999)","format":"int32","type":["integer","null"]},"ownership":{"description":"owned | leased | rented","type":["string","null"]},"purchase_cost_minor":{"format":"int64","type":["integer","null"]},"purchase_currency":{"type":["string","null"]},"purchase_date":{"format":"date","type":["string","null"]},"rental_cost_minor":{"format":"int64","type":["integer","null"]},"rental_currency":{"type":["string","null"]},"rental_cycle":{"description":"monthly | quarterly | annual","type":["string","null"]},"rental_end":{"format":"date","type":["string","null"]},"rental_start":{"format":"date","type":["string","null"]},"rental_vendor":{"type":["string","null"]},"salvage_value_minor":{"format":"int64","type":["integer","null"]},"useful_life_months":{"format":"int32","type":["integer","null"]}},"type":"object"},"AssetMutationResponse":{"allOf":[{"$ref":"#/components/schemas/DeviceAsset"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): asset fields at the top level\nplus the additive `mutation` metadata."},"AssignGroupRequest":{"properties":{"group_id":{"description":"`Some(group)` moves the board into that group; `None` ungroups it.","format":"uuid","type":["string","null"]}},"type":"object"},"AssignRequest":{"properties":{"ends_at":{"format":"date-time","type":"string"},"starts_at":{"format":"date-time","type":"string"},"ticket_id":{"format":"uuid","type":"string"},"travel_minutes":{"format":"int32","type":"integer"},"user_id":{"format":"uuid","type":"string"}},"required":["ticket_id","user_id","starts_at","ends_at"],"type":"object"},"Attachment":{"description":"One uploaded file's metadata row; the payload lives behind `storage_key`.","properties":{"content_type":{"description":"Declared + magic-byte-verified content type.","type":"string"},"created_at":{"format":"date-time","type":"string"},"email_message_id":{"description":"Set only when kind is email_message.","format":"uuid","type":["string","null"]},"filename":{"description":"Original filename, sanitized (no path components/control chars).","type":"string"},"id":{"format":"uuid","type":"string"},"kind":{"$ref":"#/components/schemas/AttachmentKind"},"org_id":{"format":"uuid","type":"string"},"sha256":{"description":"Lowercase hex sha256 of the exact bytes; dedup + integrity anchor.","type":"string"},"size_bytes":{"format":"int64","type":"integer"},"storage_key":{"description":"Object-store key (`org/<id>/attachments/<sha256>`).","type":"string"},"ticket_comment_id":{"description":"Set only when kind is ticket_comment.","format":"uuid","type":["string","null"]},"ticket_id":{"description":"Set when kind is ticket_comment or ticket.","format":"uuid","type":["string","null"]},"uploaded_by_portal_user_id":{"description":"Portal actor when uploaded through a portal surface (v1: none —\nreserved so portal uploads need no schema change).","format":"uuid","type":["string","null"]},"uploaded_by_user_id":{"description":"Staff uploader when uploaded through the API.","format":"uuid","type":["string","null"]}},"required":["id","org_id","kind","filename","content_type","size_bytes","sha256","storage_key","created_at"],"type":"object"},"AttachmentDeleted":{"description":"attachment.deleted — the metadata row went away; `object_removed`\nreports whether the backing bytes were reclaimed too (refcount hit 0).","properties":{"attachment_id":{"format":"uuid","type":"string"},"object_removed":{"type":"boolean"},"ticket_id":{"format":"uuid","type":["string","null"]}},"required":["attachment_id","object_removed"],"type":"object"},"AttachmentDownloadTicket":{"description":"What a downloader gets instead of the bytes: where to fetch them, plus the\nmetadata it would otherwise read off the streamed response's headers.","properties":{"content_type":{"type":"string"},"expires_at":{"description":"When `url` stops working. The client need not act on it: the next\ndownload asks for a fresh ticket.","type":["string","null"]},"filename":{"description":"The name the streamed response would have put in\n`content-disposition`. A presigned GET names the object key, which is\na digest, so a client that saves the file must use this instead.","type":"string"},"mode":{"$ref":"#/components/schemas/DownloadMode","description":"`presigned` — GET `url` directly and send it NO bearer token.\n`stream` — this deployment cannot sign one; call the endpoint again\nwithout `presign`, or read the bytes out of that same request."},"sha256":{"type":"string"},"size_bytes":{"format":"int64","type":"integer"},"url":{"description":"Signed URL, present exactly when `mode` is `presigned`.","type":["string","null"]}},"required":["mode","filename","content_type","size_bytes","sha256"],"type":"object"},"AttachmentKind":{"description":"What an attachment belongs to. The enum value is persisted verbatim.","enum":["ticket_comment","email_message","ticket","sign_off"],"type":"string"},"AuditEvent":{"properties":{"action":{"description":"Dot-path of the action, e.g. \"auth.login\", \"ticket.status_change\".","type":"string"},"actor":{"$ref":"#/components/schemas/Actor"},"detail":{"description":"Structured detail — never contains secrets."},"entity_id":{"type":["string","null"]},"entity_type":{"description":"Entity type + id the action targeted, when applicable.","type":["string","null"]},"id":{"format":"uuid","type":"string"},"ip":{"type":["string","null"]},"occurred_at":{"format":"date-time","type":"string"},"org_id":{"format":"uuid","type":"string"}},"required":["id","org_id","actor","action","detail","occurred_at"],"type":"object"},"AuditPage":{"properties":{"events":{"items":{"$ref":"#/components/schemas/AuditEvent"},"type":"array"},"next_cursor":{"description":"Opaque cursor for the next page; absent when exhausted. In ascending\nmode pass it back as `after` to resume the export exactly here.","type":["string","null"]}},"required":["events"],"type":"object"},"AuthorCatalogueInfo":{"description":"The registry provenance the draft was validated against.","properties":{"digest":{"type":"string"},"node_count":{"minimum":0,"type":"integer"},"version":{"type":"string"}},"required":["version","digest","node_count"],"type":"object"},"AuthorModelInfo":{"description":"Model-provenance summary for the response.","properties":{"model":{"type":"string"},"provider":{"type":"string"},"resolution":{"type":"string"}},"required":["provider","model","resolution"],"type":"object"},"AuthorUsage":{"properties":{"completion_tokens":{"format":"int32","minimum":0,"type":"integer"},"prompt_tokens":{"format":"int32","minimum":0,"type":"integer"},"steps":{"minimum":0,"type":"integer"}},"required":["prompt_tokens","completion_tokens","steps"],"type":"object"},"AuthorWorkflowRequest":{"properties":{"document":{"description":"The current document, when editing an existing workflow. Validated\nfirst, so the returned diff is against a real document.","type":["object","null"]},"max_steps":{"format":"int32","minimum":0,"type":["integer","null"]},"model":{"type":["string","null"]},"prompt":{"description":"The plain-English description of the workflow to author.","type":"string"},"provider":{"type":["string","null"]},"surface_key":{"description":"The AI surface the call is admitted on (e.g. `general_chat`). The org\ngrants it like any other; there is no workflow-specific surface and no\nnew admission path.","type":"string"}},"required":["surface_key","prompt","document"],"type":"object"},"AuthorWorkflowResponse":{"properties":{"catalogue":{"$ref":"#/components/schemas/AuthorCatalogueInfo"},"diff":{"description":"The changes the draft makes against `document` (or against an empty\ndocument when generating fresh).","items":{"$ref":"#/components/schemas/DiffEntry"},"type":"array"},"draft":{"description":"The validated document, when the model produced one.","type":["object","null"]},"error":{"description":"Set when the model transport failed; the caller reports this honestly.","type":["string","null"]},"issues":{"description":"Typed validation issues; empty means `draft` is runnable.","items":{"$ref":"#/components/schemas/AuthoringIssue"},"type":"array"},"model_info":{"$ref":"#/components/schemas/AuthorModelInfo"},"name":{"description":"Advisory name from the model's answer envelope.","type":["string","null"]},"saved":{"description":"Always false — this endpoint never writes (spec D).","type":"boolean"},"tool_calls":{"items":{"$ref":"#/components/schemas/AuthoringToolCall"},"type":"array"},"trigger_event":{"description":"Advisory trigger event type from the envelope.","type":["string","null"]},"usage":{"$ref":"#/components/schemas/AuthorUsage"},"valid":{"description":"True when `draft` is present and validated against the catalogue.","type":"boolean"}},"required":["valid","saved","draft","issues","diff","tool_calls","usage","model_info","catalogue"],"type":"object"},"AuthoringIssue":{"description":"One author-time refusal, pointed and stable so a UI can render it.","properties":{"code":{"description":"A stable machine code (`unknown_node_type`, `node_params`, …).","type":"string"},"message":{"type":"string"},"path":{"description":"The offending node id or a JSON path, when known.","type":["string","null"]}},"required":["code","message"],"type":"object"},"AuthoringToolCall":{"description":"One tool call the model made, as the response reports it.","properties":{"argument":{"description":"The `node_id` argument for `get_node_schema`, echoed for the trace.","type":"string"},"name":{"type":"string"},"outcome":{"description":"`ok` or `not_found` / `rejected`.","type":"string"}},"required":["name","argument","outcome"],"type":"object"},"AutomationAction":{"description":"Effects a rule may request. Every variant maps onto an existing core-api\nwrite path — automations never gain private write routes.","oneOf":[{"properties":{"type":{"enum":["assign_user"],"type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","type"],"type":"object"},{"properties":{"priority":{"$ref":"#/components/schemas/Priority"},"type":{"enum":["set_priority"],"type":"string"}},"required":["priority","type"],"type":"object"},{"properties":{"body":{"type":"string"},"type":{"enum":["add_internal_comment"],"type":"string"}},"required":["body","type"],"type":"object"},{"properties":{"kind":{"$ref":"#/components/schemas/NotificationKind"},"title":{"type":"string"},"type":{"enum":["notify_users"],"type":"string"},"user_ids":{"items":{"format":"uuid","type":"string"},"type":"array"}},"required":["user_ids","kind","title","type"],"type":"object"},{"description":"§4.6 (Wave 34 lane 4): a plugin-contributed action kind. `entry_key`\nnames a `plugin_surface_entries` row (`plugin.<slug>.<id>`); the\nexecutor re-reads the registry entry AT EXECUTION TIME — a consented\neffect runs only while its install is enabled and the grant stands,\nand a missing entry degrades to a typed skip, never a guess.\n`params` validate against the entry's `params_schema` before the\neffect runs.","properties":{"entry_key":{"type":"string"},"params":{},"type":{"enum":["plugin_action"],"type":"string"}},"required":["entry_key","type"],"type":"object"}]},"AutomationCondition":{"description":"One predicate; a rule matches only when EVERY condition holds.\n\nDeterministic predicates evaluate against the ticket snapshot; the\n[`AutomationCondition::AiDecision`] kind is pulled out by\n[`split_conditions`] and evaluated by the worker against recorded\ndecision answers (ADR 0010 #2).\n\nThe vocabulary is closed and each variant is a whole predicate, so the\neditor's field/operator grammar can only offer what a saved rule can\ncarry. Wave 43 lane 17b widened it from equality-only to the operators\nMSPs actually reach for — negation, set membership and the positive\nassignee test — because \"priority is not P1\" and \"priority is any of P1,\nP2\" had no way to be written down.","oneOf":[{"properties":{"priority":{"$ref":"#/components/schemas/Priority"},"type":{"enum":["priority_is"],"type":"string"}},"required":["priority","type"],"type":"object"},{"description":"Priority is not this one. The complement of [`Self::PriorityIs`].","properties":{"priority":{"$ref":"#/components/schemas/Priority"},"type":{"enum":["priority_is_not"],"type":"string"}},"required":["priority","type"],"type":"object"},{"description":"Priority is any one of these. An empty list matches nothing and is\nrefused by [`validate_conditions`].","properties":{"priorities":{"items":{"$ref":"#/components/schemas/Priority"},"type":"array"},"type":{"enum":["priority_in"],"type":"string"}},"required":["priorities","type"],"type":"object"},{"properties":{"category":{"$ref":"#/components/schemas/TicketStatusCategory"},"type":{"enum":["category_is"],"type":"string"}},"required":["category","type"],"type":"object"},{"properties":{"category":{"$ref":"#/components/schemas/TicketStatusCategory"},"type":{"enum":["category_is_not"],"type":"string"}},"required":["category","type"],"type":"object"},{"description":"Status category is any one of these. An empty list matches nothing\nand is refused by [`validate_conditions`].","properties":{"categories":{"items":{"$ref":"#/components/schemas/TicketStatusCategory"},"type":"array"},"type":{"enum":["category_in"],"type":"string"}},"required":["categories","type"],"type":"object"},{"properties":{"needle":{"type":"string"},"type":{"enum":["title_contains"],"type":"string"}},"required":["needle","type"],"type":"object"},{"description":"Title does not contain the needle (case-insensitive). An empty needle\nis refused by [`validate_conditions`]: \"does not contain nothing\" is\nfalse for every ticket, so the rule could never fire.","properties":{"needle":{"type":"string"},"type":{"enum":["title_not_contains"],"type":"string"}},"required":["needle","type"],"type":"object"},{"description":"Assignee is currently unset.","properties":{"type":{"enum":["unassigned"],"type":"string"}},"required":["type"],"type":"object"},{"description":"Assignee is set to somebody. The complement of [`Self::Unassigned`].","properties":{"type":{"enum":["assignee_is_set"],"type":"string"}},"required":["type"],"type":"object"},{"description":"CSAT surveys on resolution (Wave 34 lane 2): fires only on\n`csat.response_recorded` events whose score is BELOW the threshold\n(strictly less — threshold 3 fires on 1 and 2). Evaluated against\nthe event payload score, not the ticket snapshot; on any other\ntrigger event the condition is a no-match.","properties":{"threshold":{"format":"int32","type":"integer"},"type":{"enum":["csat_score_below"],"type":"string"}},"required":["threshold","type"],"type":"object"},{"description":"Semantic condition answered by the decision model (ADR 0010 §3\n#2): references (surface, question, operator, threshold,\nconfidence-path) and is evaluated by the worker AGAINST THE\nNEWEST RECORDED DECISION ANSWERS for the ticket — never by a\nlive per-rule fan-out (the one-fan-out-per-trigger doctrine is\nowned by the worker decision subsystem). Missing/stale answers\nevaluate as NO-MATCH with a warn — the exact skip-with-warn\nposture a malformed deterministic rule gets today.","properties":{"confidence_path":{"description":"Dot-path into the answer entry overriding the default\n`\"confidence\"` key when the rule gates on a specific path.","type":["string","null"]},"operator":{"$ref":"#/components/schemas/AiDecisionOperator"},"question":{"type":"string"},"surface":{"type":"string"},"threshold":{"format":"double","type":"number"},"type":{"enum":["ai_decision"],"type":"string"}},"required":["surface","question","operator","threshold","type"],"type":"object"}]},"AutomationDraftSummaryRequest":{"properties":{"actions_text":{"description":"The operator's read-back of the actions, in order.","items":{"type":"string"},"type":"array"},"conditions_text":{"description":"The operator's own read-back of the conditions, e.g. \"it has no\nassignee and its priority is P1 — critical\". The client renders this\nsentence from the same clauses its controls show, so the summary is\nwritten from what the person already sees rather than from a second\nrenderer in Rust that could disagree with it.","type":"string"},"incomplete_conditions":{"description":"How many condition rows are still incomplete. The summary must be\nable to say the rule is unfinished rather than describing it as\nthough it were finished.","minimum":0,"type":"integer"},"model":{"type":["string","null"]},"name":{"description":"The rule's name as typed, when it has one.","type":["string","null"]},"provider":{"type":["string","null"]},"scope":{"description":"The board the rule is scoped to, when it is not org-wide.","type":["string","null"]},"subject_kind":{"description":"`ticket` or `board`.","type":["string","null"]},"surface_key":{"description":"The AI surface the call is admitted on. The web sends `general_chat`,\nthe native surface. NOT `automation_writing`: that key is a seeded\nPLUGIN surface reserved for the premium plugin, and core implements\nonly the registry seam.","type":"string"},"trigger_event":{"type":"string"}},"required":["surface_key","trigger_event","conditions_text"],"type":"object"},"AutomationTestRunMatch":{"description":"POST /api/automation-rules/{id}/test-run — dry-run one rule against the\norg's most recent tickets (board-scoped when the rule binds a board),\nPROPOSAL-shaped: what WOULD match and what the actions would do. The\nrule editor renders the result; nothing is ever applied and no event is\nemitted — a test-run writes nothing (Wave 17 lane 13 contract: the rule\neditor never pretends to execute).","properties":{"assignee_id":{"format":"uuid","type":["string","null"]},"number":{"type":"string"},"priority":{"$ref":"#/components/schemas/Priority","description":"The ticket's priority when the run sampled it (Wave 43 lane 17b).\n\nThe run is a PROPOSAL, so the useful thing to show is not only that a\nticket matched but what the rule's effects WOULD change on it. Both\n`set_priority` and `assign_user` write a value the snapshot already\ncarries, so a preview can read `P2 -> P1` instead of `matched`. The\nvalues are the sampled state, never the post-effect state, because no\neffect ran."},"ticket_id":{"format":"uuid","type":"string"},"title":{"type":"string"}},"required":["ticket_id","number","title","priority"],"type":"object"},"AutomationTestRunResponse":{"properties":{"actions_preview":{"description":"The rule's CURRENT action documents, returned verbatim — the\neditor renders them as the proposal preview (never re-derived)."},"matched_count":{"minimum":0,"type":"integer"},"matches":{"items":{"$ref":"#/components/schemas/AutomationTestRunMatch"},"type":"array"},"rule_id":{"format":"uuid","type":"string"},"sampled_count":{"description":"What the run sampled (the org's recent tickets, board-filtered\nwhen the rule binds a board).","minimum":0,"type":"integer"}},"required":["rule_id","sampled_count","matched_count","matches","actions_preview"],"type":"object"},"AvailabilityListResponse":{"properties":{"user_id":{"format":"uuid","type":"string"},"windows":{"items":{"$ref":"#/components/schemas/AvailabilityWindow"},"type":"array"}},"required":["user_id","windows"],"type":"object"},"AvailabilityQuery":{"properties":{"user_id":{"description":"Whose windows. Defaults to the caller; another user requires owner/admin.","format":"uuid","type":["string","null"]}},"type":"object"},"AvailabilityWindow":{"description":"One recurring weekly availability template: `weekday` (0 = Monday …\n6 = Sunday), local wall-clock `[start_local, end_local)` in `timezone`,\noptionally bounded by an effective date range. This is a TEMPLATE —\noccurrences are computed, never stored.","properties":{"created_at":{"format":"date-time","type":"string"},"effective_from":{"type":["string","null"]},"effective_until":{"type":["string","null"]},"end_local":{"type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"start_local":{"type":"string"},"timezone":{"description":"IANA timezone the wall-clock bounds live in.","type":"string"},"user_id":{"format":"uuid","type":"string"},"weekday":{"format":"int32","type":"integer"}},"required":["id","org_id","user_id","weekday","start_local","end_local","timezone","created_at"],"type":"object"},"BatchCandidateOutcome":{"properties":{"detail":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"merge":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CiMerge","description":"The per-candidate audit row for accepts (each merge is individually\nundoable via its split endpoint)."}]},"outcome":{"description":"\"applied\" | \"skipped\" | \"failed\"","type":"string"}},"required":["id","outcome"],"type":"object"},"BatchCandidateRequest":{"properties":{"action":{"description":"accept | reject | defer","type":"string"},"ids":{"items":{"format":"uuid","type":"string"},"type":"array"}},"required":["ids","action"],"type":"object"},"BatchCandidateResponse":{"properties":{"accepted":{"minimum":0,"type":"integer"},"deferred":{"minimum":0,"type":"integer"},"failed":{"minimum":0,"type":"integer"},"outcomes":{"items":{"$ref":"#/components/schemas/BatchCandidateOutcome"},"type":"array"},"rejected":{"minimum":0,"type":"integer"},"skipped":{"minimum":0,"type":"integer"}},"required":["accepted","rejected","deferred","skipped","failed","outcomes"],"type":"object"},"BatchCreatedResponse":{"properties":{"batch_id":{"format":"uuid","type":"string"},"label":{"type":"string"}},"required":["batch_id","label"],"type":"object"},"BatchViewResponse":{"properties":{"conflict_policy":{"$ref":"#/components/schemas/ConflictPolicy"},"dry_run":{"type":"boolean"},"entity_kind":{"$ref":"#/components/schemas/EntityKind"},"finished_at":{"format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"label":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"queued_at":{"format":"date-time","type":["string","null"]},"rows_created":{"format":"int32","type":"integer"},"rows_failed":{"format":"int32","type":"integer"},"rows_skipped":{"format":"int32","type":"integer"},"rows_total":{"format":"int32","type":"integer"},"rows_updated":{"format":"int32","type":"integer"},"source_note":{"type":["string","null"]},"staged_chunks":{"description":"Async giant-batch bookkeeping (D2 remainder): staged chunk count and\nthe once-only worker-enqueue stamp.","format":"int32","type":"integer"},"started_at":{"format":"date-time","type":"string"}},"required":["id","org_id","label","entity_kind","conflict_policy","dry_run","rows_total","rows_created","rows_updated","rows_skipped","rows_failed","started_at","staged_chunks"],"type":"object"},"BillMutationResponse":{"properties":{"bill":{"$ref":"#/components/schemas/PurchaseBill"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["bill","mutation"],"type":"object"},"BillingCandidateLine":{"description":"One draft invoice-line candidate. This is a PREVIEW shape: nothing\npersists until Wave 9's invoice run.","properties":{"amount_minor":{"description":"Line total in minor units (currency on the parent evaluation).\nNEGATIVE only for `sla_credit` lines — the one line kind whose\nsigned amount is meaningful; everything else is ≥ 0 and the\nengine's i128 total fold accepts the mix.","format":"int64","type":"integer"},"client_id":{"format":"uuid","type":"string"},"contract_id":{"format":"uuid","type":"string"},"contract_number":{"type":"string"},"description":{"type":"string"},"kind":{"$ref":"#/components/schemas/BillingLineKind"},"period_end":{"format":"date","type":"string"},"period_start":{"description":"The billing period (recurring/retainer) or the effective slice of\nthe evaluation window (T&M) or the fee's due date mirrored into\nboth ends (fixed fee). Inclusive start, exclusive end for\nperiod-derived lines.","format":"date","type":"string"},"quantity_minutes":{"description":"Labor minutes on the line (T&M/overage); 0 for amount lines.","format":"int64","type":"integer"},"unit_amount_minor":{"description":"Unit price in minor units: the hourly rate for labor lines, the\nfull amount otherwise.","format":"int64","type":"integer"}},"required":["contract_id","contract_number","client_id","kind","period_start","period_end","description","quantity_minutes","unit_amount_minor","amount_minor"],"type":"object"},"BillingEvaluation":{"description":"The result of evaluating one contract's billing rules over a window.","properties":{"block_consumption":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BlockConsumption","description":"Block draw-down detail (prepaid blocks only; empty otherwise)."}]},"cap_warnings":{"description":"Cap warnings (`warn` behavior): one human-readable marker per\n(cap, period) whose labor reached the cap. `stop`/`overage_billable`\ncommunicate through their line shapes instead.","items":{"type":"string"},"type":"array"},"client_id":{"format":"uuid","type":"string"},"contract_id":{"format":"uuid","type":"string"},"contract_number":{"type":"string"},"contract_type":{"$ref":"#/components/schemas/ContractType"},"currency":{"type":"string"},"from":{"description":"The evaluation window (inclusive bounds).","format":"date","type":"string"},"lines":{"items":{"$ref":"#/components/schemas/BillingCandidateLine"},"type":"array"},"to":{"format":"date","type":"string"},"total_minor":{"description":"Sum of line amounts, minor units.","format":"int64","type":"integer"}},"required":["contract_id","contract_number","client_id","contract_type","currency","from","to","lines","total_minor"],"type":"object"},"BillingFrequency":{"description":"Billing period length. Period boundaries derive from the contract's\n`billing_anchor`: the anchor is the first period's start date and each\nsubsequent period starts one frequency step later (calendar-aware\nclamping lives in core-billing's period arithmetic).","enum":["weekly","monthly","quarterly","semiannual","annual"],"type":"string"},"BillingLineKind":{"description":"Which billing rule produced a candidate line.","enum":["recurring_base","retainer_base","retainer_overage","time_materials","fixed_fee","per_unit_base","per_incident","block_sale","block_drawdown","cap_overage","minimum_trueup","change_order","sla_credit","escalator","project_release"],"type":"string"},"BillingMethod":{"description":"How the project turns delivered work into money. The value rides the\nmilestone event payload so core-billing can route without re-reading the\nproject row (event payloads are additive-only, policy §2).","enum":["milestone","time_and_materials","fixed_fee","non_billable"],"type":"string"},"BlockConsumption":{"description":"The per-period consumption summary a block contract's evaluation\ncarries back: draws in order, blocks that expired unused inside the\nwindow (for depletion/expiry alerts), and any minutes that found no\nopen block (`unblocked_minutes` — they bill as T&M).","properties":{"draws":{"items":{"format":"uuid","type":"string"},"type":"array"},"unblocked_minutes":{"format":"int64","type":"integer"}},"required":["draws","unblocked_minutes"],"type":"object"},"BlockDrawView":{"description":"One draw: which entry consumed how many block minutes.","properties":{"minutes":{"format":"int64","type":"integer"},"spilled_minutes":{"format":"int64","type":"integer"},"time_entry_id":{"format":"uuid","type":["string","null"]}},"required":["minutes","spilled_minutes"],"type":"object"},"BlockPanelView":{"description":"The retainer / prepaid block-consumption panel (report V7): which\nentries drew the included block versus billed as overage, the\nremaining balance, and the depletion alert.","properties":{"alert":{"description":"`depleted` when the balance is gone and work is spilling over;\n`expiring` when a balance remains but the block expires in-window;\n`none` when healthy.","type":"string"},"block_id":{"format":"uuid","type":"string"},"drawn_minutes":{"description":"Minutes this window's entries drew.","format":"int64","type":"integer"},"draws":{"description":"Per-entry draws: which entries consumed the block.","items":{"$ref":"#/components/schemas/BlockDrawView"},"type":"array"},"expires_on":{"description":"Expiry, when the block carries one.","format":"date","type":["string","null"]},"label":{"type":"string"},"overage_minutes":{"description":"Minutes of this window's work that found no open block — they\nbill at card rates (the overage the admin must see).","format":"int64","type":"integer"},"prior_drawn_minutes":{"description":"Minutes drawn BEFORE this window (earlier runs).","format":"int64","type":"integer"},"remaining_minutes":{"description":"Balance still available (never negative).","format":"int64","type":"integer"},"total_minutes":{"format":"int64","type":"integer"}},"required":["block_id","label","total_minutes","prior_drawn_minutes","drawn_minutes","remaining_minutes","overage_minutes","draws","alert"],"type":"object"},"BlockSpec":{"description":"Prepaid-block spec (A): an hour bank sold up front.","properties":{"block_rate_minor":{"description":"Optional discounted draw-down rate, minor units per hour.","format":"int64","type":["integer","null"]},"expires_on":{"format":"date","type":["string","null"]},"label":{"type":"string"},"rollover_policy":{"description":"`expire|rollover|none`.","type":"string"},"sale_price_minor":{"format":"int64","type":"integer"},"total_minutes":{"format":"int32","type":"integer"}},"required":["label","total_minutes","sale_price_minor","rollover_policy"],"type":"object"},"Board":{"properties":{"created_at":{"description":"Users whose favorite this board is (board groups/favorites, Stage 2).","format":"date-time","type":"string"},"description":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_default":{"type":"boolean"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"require_checklist_close":{"description":"Pre-close checklist enforcement toggle (defaults OFF; closing is\ngated only when this AND a checklist template are both present —\ndefaults-first doctrine, migration 0016).","type":"boolean"}},"required":["id","org_id","name","is_default","require_checklist_close","created_at"],"type":"object"},"BoardAddressResponse":{"properties":{"board_id":{"format":"uuid","type":"string"},"inbound_address":{"type":["string","null"]}},"required":["board_id"],"type":"object"},"BoardExposureView":{"properties":{"board_id":{"format":"uuid","type":"string"},"client_id":{"format":"uuid","type":"string"},"exposed":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"visible_status_ids":{"items":{"format":"uuid","type":"string"},"type":"array"}},"required":["id","board_id","client_id","exposed","visible_status_ids"],"type":"object"},"BoardGroup":{"description":"A named, ordered nav grouping of boards within one org (the private build tracker 2.11).\nGroups are organization-wide; `owner_id` records who controls the group.","properties":{"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"owner_id":{"format":"uuid","type":"string"},"position":{"format":"int32","type":"integer"}},"required":["id","org_id","owner_id","name","position","created_at"],"type":"object"},"BoardIdentityView":{"properties":{"from_address":{"type":["string","null"]},"from_name":{"type":["string","null"]},"reply_to":{"type":["string","null"]},"signature":{"type":["string","null"]}},"type":"object"},"BoardMember":{"description":"An explicit per-user membership on one board carrying a board-level\nrole tier (§6 board-level RBAC). Rows NARROW org grants; absence of a\nrow leaves org-level governance untouched.","properties":{"board_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"org_id":{"format":"uuid","type":"string"},"role":{"$ref":"#/components/schemas/BoardRole"},"user_id":{"format":"uuid","type":"string"}},"required":["org_id","board_id","user_id","role","created_at"],"type":"object"},"BoardMemberView":{"properties":{"created_at":{"format":"date-time","type":"string"},"display_name":{"type":"string"},"role":{"$ref":"#/components/schemas/BoardRole"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","display_name","role","created_at"],"type":"object"},"BoardNavEntry":{"description":"One left-nav entry: the board plus the CALLER's annotations.","properties":{"description":{"type":["string","null"]},"favorite":{"description":"Whether THIS user favorited the board (star).","type":"boolean"},"group_id":{"description":"Group assignment for the collapsible tree (`None` = ungrouped).","format":"uuid","type":["string","null"]},"group_name":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_default":{"type":"boolean"},"my_role":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BoardRole","description":"The caller's board role (`None` = no membership row ⇒ org-governed)."}]},"name":{"type":"string"}},"required":["id","name","is_default","favorite"],"type":"object"},"BoardRole":{"description":"Per-board role tier stored on `board_members` rows (migration 0019,\nthe private build tracker 2.11). Board roles sit BESIDE the org matrix and can only\nnarrow it — see `authorize_board_scoped`.","enum":["viewer","member","admin"],"type":"string"},"BoardRouteView":{"properties":{"board_id":{"format":"uuid","type":"string"},"identity":{"$ref":"#/components/schemas/BoardIdentityView"},"inbound_address":{"type":["string","null"]},"name":{"type":"string"}},"required":["board_id","name","identity"],"type":"object"},"BoardStatus":{"description":"A per-board status. `category` is the binding contract — labels are\npresentation, category is behavior.","properties":{"board_id":{"format":"uuid","type":"string"},"category":{"$ref":"#/components/schemas/TicketStatusCategory"},"client_visible":{"description":"Wave 41 lane 12: whether the portal shows this status's own label to\na client. `false` shows the semantic category word instead, so an\ninternal label (\"Escalated to L2\") never reaches a client while the\nticket still reports where it stands.","type":"boolean"},"color":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_entry":{"description":"Whether tickets may enter this status directly from New.","type":"boolean"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"sort_order":{"format":"int32","type":"integer"},"wip_limit":{"description":"Per-column WIP cap (0089): occupancy of this column is enforced\nserver-side at ticket status change. `None` = no limit.","format":"int32","type":["integer","null"]}},"required":["id","board_id","org_id","name","category","sort_order","is_entry","client_visible"],"type":"object"},"BoardStatusUsage":{"description":"GET /api/boards/{id}/statuses/usage — per-status ticket counts and\nautomation references (Wave 41 lane 12).\n\nOne read serves both the settings table's TICKETS column and the delete\nconfirm's \"12 tickets use this status\" and \"2 automations mention this\nstatus\", so the table and the confirm can never quote different numbers for\nthe same status.","properties":{"automation_count":{"description":"Automation rules naming this status. They are PAUSED, never silently\nrepointed, if the status is deleted.","format":"int64","type":"integer"},"status_id":{"format":"uuid","type":"string"},"ticket_count":{"description":"Tickets currently in this status, closed ones included: the column is\nwhere work has been, not a live queue.","format":"int64","type":"integer"}},"required":["status_id","ticket_count","automation_count"],"type":"object"},"BoardTransitionsResponse":{"description":"GET response for a board's workflow: either its explicit matrix or the\nmarker that the semantic-category floor governs.","properties":{"edges":{"items":{"$ref":"#/components/schemas/TransitionEdge"},"type":"array"},"mode":{"description":"`\"explicit\"` when the listed edges are authoritative; `\"floor\"` when\nthe semantic-category default governs (any→any except closed→open).","type":"string"}},"required":["mode","edges"],"type":"object"},"BookRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): debounce/retry safe."},{"properties":{"booker_email":{"type":["string","null"]},"booker_name":{"type":["string","null"]},"booker_notes":{"type":["string","null"]},"starts_at":{"description":"Slot start (the template's duration extends it). Must sit fully\ninside ONE free interval of the scope at book time.","format":"date-time","type":"string"}},"required":["starts_at"],"type":"object"}]},"BookResponse":{"properties":{"appointment":{"$ref":"#/components/schemas/Appointment"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["appointment","mutation"],"type":"object"},"BulkDecisionItem":{"description":"Per-item outcome of a bulk decision: applied entries carry the new\nsnapshot; refused entries name exactly why (`own_entry`, not-in-queue,\nlocked, or not visible in scope), so the client flags rows instead of\nguessing.","properties":{"entry":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TimeEntry"}]},"entry_id":{"format":"uuid","type":"string"},"outcome":{"type":"string"}},"required":["entry_id","outcome"],"type":"object"},"BulkDecisionRequest":{"properties":{"decision":{"description":"`approve` or `reject`. Rejections require a typed `reason_code`.","type":"string"},"entry_ids":{"items":{"format":"uuid","type":"string"},"type":"array"},"note":{"type":["string","null"]},"reason_code":{"description":"Typed rejection vocabulary (`missing_notes`, `wrong_ticket`, ...).","type":["string","null"]}},"required":["entry_ids","decision"],"type":"object"},"BulkDecisionResponse":{"properties":{"results":{"items":{"$ref":"#/components/schemas/BulkDecisionItem"},"type":"array"}},"required":["results"],"type":"object"},"BulkEntrySuggestionsResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/EntrySuggestionsResponse"},"type":"array"}},"required":["items"],"type":"object"},"BulkMoveRequest":{"properties":{"moves":{"items":{"$ref":"#/components/schemas/MoveSpec"},"type":"array"}},"required":["moves"],"type":"object"},"BulkMoveResponse":{"properties":{"moved":{"items":{"$ref":"#/components/schemas/DispatchAppointment"},"type":"array"}},"required":["moved"],"type":"object"},"BulkRowOutcome":{"enum":["created","replayed","refused"],"type":"string"},"BulkRowResult":{"properties":{"idempotency_key":{"type":"string"},"outcome":{"$ref":"#/components/schemas/BulkRowOutcome"},"reason":{"description":"Typed refusal reason on `refused` rows (validation/unique/key\nreuse); never carries secret material.","type":["string","null"]},"record_id":{"format":"uuid","type":["string","null"]}},"required":["idempotency_key","outcome"],"type":"object"},"BulkSecretIngestRequest":{"properties":{"client_id":{"description":"NULL = org-scope records.","format":"uuid","type":["string","null"]},"folder_id":{"format":"uuid","type":"string"},"kind":{"$ref":"#/components/schemas/SecretRecordKind"},"records":{"description":"The chunk: 1..=[`BULK_CHUNK_CAP`] rows.","items":{"$ref":"#/components/schemas/BulkSecretRecord"},"type":"array"}},"required":["folder_id","kind","records"],"type":"object"},"BulkSecretIngestResponse":{"properties":{"created":{"description":"Convenience counts for the driver loop.","format":"int64","type":"integer"},"refused":{"format":"int64","type":"integer"},"replayed":{"format":"int64","type":"integer"},"results":{"items":{"$ref":"#/components/schemas/BulkRowResult"},"type":"array"},"rows_per_sec_ceiling":{"description":"True when the caller should send another chunk (informational —\nthe driver knows its own queue; the row ceiling is the pacing\nsignal to stay under).","format":"int64","type":"integer"}},"required":["results","created","replayed","refused","rows_per_sec_ceiling"],"type":"object"},"BulkSecretRecord":{"properties":{"expires_at":{"format":"date-time","type":["string","null"]},"fields":{"description":"Material fields — sealed with coordinate AAD before persistence.","items":{"$ref":"#/components/schemas/SecretFieldValue"},"type":"array"},"idempotency_key":{"description":"Stable per-row idempotency key — THE resume checkpoint. Generate\nonce per source row (e.g. the legacy row's stable id) and reuse it\nverbatim on every chunk retry.","type":"string"},"title":{"type":"string"}},"required":["title","idempotency_key"],"type":"object"},"BusinessCalendar":{"description":"Weekly business hours in a named IANA timezone, plus holiday dates.\n`None` window = non-business day.","properties":{"holidays":{"description":"Local dates (YYYY-MM-DD) with no business hours.","items":{"type":"string"},"type":"array"},"maintenance":{"description":"Downtime stretches subtracted from every measurement. Absent in\nlegacy documents (`default`) so stored policies keep deserializing.","items":{"$ref":"#/components/schemas/MaintenanceWindow"},"type":"array"},"name":{"type":"string"},"timezone":{"type":"string"},"windows":{"description":"[Mon..Sun] open/close in local wall-clock \"HH:MM\".","items":{"items":false,"prefixItems":[{"type":"string"},{"type":"string"}],"type":["array","null"]},"type":"array"}},"required":["name","timezone","windows","holidays"],"type":"object"},"ByokOverviewResponse":{"description":"Summary of all configured AI providers for the org.","properties":{"active_model":{"type":"string"},"active_provider":{"type":"string"},"configured_providers":{"items":{"type":"string"},"type":"array"}},"required":["active_provider","active_model","configured_providers"],"type":"object"},"ByokSecretMutationResponse":{"allOf":[{"$ref":"#/components/schemas/ByokSecretStatus"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Mutation response: status fields plus the instant-first mutation\nmetadata (`mutation.version` echoes into the next write)."},"ByokSecretStatus":{"description":"Non-secret status of a stored BYOK key (presence flags only — never\nkey material). Flattened into mutation responses so a stored replay\ndeserializes byte-identically.","properties":{"has_key":{"type":"boolean"},"provider":{"type":"string"},"updated_at":{"type":["string","null"]}},"required":["provider","has_key","updated_at"],"type":"object"},"Cadence":{"description":"The typed cadence of a schedule row (the stored slug's Rust shape).","enum":["daily","weekly","monthly"],"type":"string"},"CadenceSpec":{"description":"The typed cadence + wall-clock shape (same columns as report\nschedules; the DB CHECKs pin the same consistency rules).","properties":{"cadence":{"description":"\"daily\" | \"weekly\" | \"monthly\"","type":"string"},"day_of_month":{"description":"1–28; monthly only.","format":"int32","type":["integer","null"]},"day_of_week":{"description":"0 = Monday .. 6 = Sunday; weekly only.","format":"int32","type":["integer","null"]},"time_of_day":{"description":"Local wall-clock fire time in `timezone` (HH:MM:SS).","type":"string"},"timezone":{"description":"IANA zone the wall clock reads in.","type":"string"}},"required":["cadence","time_of_day"],"type":"object"},"CalendarDeadlineEntry":{"description":"One deadline the overlay renders. Hydration lives in the row.","properties":{"assignee_id":{"format":"uuid","type":["string","null"]},"board_id":{"format":"uuid","type":"string"},"category":{"description":"Semantic category (informational — closed tickets never arrive).","type":"string"},"client_id":{"format":"uuid","type":["string","null"]},"due_at":{"type":"string"},"number":{"type":"string"},"ticket_id":{"format":"uuid","type":"string"},"title":{"type":"string"}},"required":["ticket_id","board_id","number","title","due_at","category"],"type":"object"},"CalendarDeadlinesResponse":{"properties":{"deadlines":{"items":{"$ref":"#/components/schemas/CalendarDeadlineEntry"},"type":"array"},"from":{"format":"date-time","type":"string"},"to":{"format":"date-time","type":"string"}},"required":["from","to","deadlines"],"type":"object"},"CalendarExclusion":{"description":"Absolute UTC time-off punching holes in availability.","properties":{"created_at":{"format":"date-time","type":"string"},"ends_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"reason":{"type":["string","null"]},"starts_at":{"format":"date-time","type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["id","org_id","user_id","starts_at","ends_at","created_at"],"type":"object"},"CalendarItem":{"description":"Flat merged entry so calendars render one ordered list without caring\nwhich table produced it.","properties":{"category":{"description":"On-call-only field.","type":["string","null"]},"ends_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"item_type":{"description":"\"appointment\" | \"on_call\"","type":"string"},"kind":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AppointmentKind","description":"Appointment-only fields."}]},"starts_at":{"format":"date-time","type":"string"},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AppointmentStatus"}]},"ticket_id":{"format":"uuid","type":["string","null"]},"title":{"type":["string","null"]},"travel_minutes":{"description":"Appointment-only (Wave 32 lane 7): booked travel minutes sitting\nimmediately BEFORE the appointment — the calendar renders them\nvisually distinct from the booking block.","format":"int32","type":["integer","null"]},"travel_quality":{"description":"`heuristic` | `measured` when the estimate carries provenance.","type":["string","null"]},"user_id":{"format":"uuid","type":"string"}},"required":["item_type","id","user_id","starts_at","ends_at"],"type":"object"},"CalendarSyncConnectionView":{"description":"One connection as the API returns it: the stored row minus the cached\nprovider credential.\n\nA credential is written and never read back through a view (Wave 55\ndecision 9). The row's `access_token` is read by the poll and by nothing\nelse, so what a client gets is presence — a token is held or it is not —\nand never the token itself. An access token a client can read leaves the\nproduct through a screenshot, a copied log line or a support bundle, and\nit reads the whole of that person's calendar until it expires.\n\nNo hint field: the tree exposes no last-four pattern for a credential\nanywhere else, so this surface does not invent one. `token_expires_at`\nstays, because an expiry is not a credential and Settings needs it to say\nwhen the grant lapses.\n\nThis is a view, not a mirror: a field added to the entity does not appear\nhere until someone decides the API should carry it. That is the fail-closed\ndirection for a response shape.","properties":{"created_at":{"format":"date-time","type":"string"},"direction":{"$ref":"#/components/schemas/CalendarSyncDirection"},"external_account":{"description":"The provider's own account id (Google `sub`, Microsoft `oid`).","type":["string","null"]},"feed_path":{"description":"Outbound: relative feed path clients render against the API origin.","type":["string","null"]},"feed_url":{"description":"Inbound: the external iCal URL the provider polls.","type":["string","null"]},"has_access_token":{"description":"Whether the row holds a usable cached provider token. Presence only;\nthe token itself never crosses the wire.","type":"boolean"},"id":{"format":"uuid","type":"string"},"last_error":{"description":"Last poll failure message (fixed provider-side copy, no internals).","type":["string","null"]},"last_synced_at":{"format":"date-time","type":["string","null"]},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"provider":{"type":"string"},"scopes":{"description":"Scopes actually granted at consent.","type":["string","null"]},"status":{"$ref":"#/components/schemas/CalendarSyncStatus"},"token_expires_at":{"format":"date-time","type":["string","null"]},"updated_at":{"format":"date-time","type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["id","org_id","user_id","name","provider","direction","status","has_access_token","created_at","updated_at"],"type":"object"},"CalendarSyncDirection":{"description":"What a connection does: `outbound` serves the org's appointments to\nthe external calendar (private token-scoped iCal feed), `inbound`\npolls the external feed for busy time, `two_way` does both.","enum":["outbound","inbound","two_way"],"type":"string"},"CalendarSyncEvent":{"description":"The inbound mirror of one external event. `exclusion_id` names the\n`calendar_exclusions` row the event currently feeds into the 2.12\navailability foundation (NULL while not busy — cancelled or transparent).","properties":{"connection_id":{"format":"uuid","type":"string"},"ends_at":{"format":"date-time","type":"string"},"exclusion_id":{"format":"uuid","type":["string","null"]},"external_uid":{"description":"The external event's UID — the duplicate key per connection.","type":"string"},"first_seen_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"last_seen_at":{"format":"date-time","type":"string"},"org_id":{"format":"uuid","type":"string"},"sequence":{"description":"Highest external sequence wins on re-poll.","format":"int32","type":"integer"},"starts_at":{"format":"date-time","type":"string"},"title":{"type":["string","null"]},"user_id":{"format":"uuid","type":"string"}},"required":["id","org_id","connection_id","user_id","external_uid","sequence","starts_at","ends_at","first_seen_at","last_seen_at"],"type":"object"},"CalendarSyncStatus":{"description":"Connection health: `error` records the last poll failure while the\nconnection stays configured; writes resume when a poll succeeds.","enum":["active","error","disabled"],"type":"string"},"CalendarViewResponse":{"properties":{"availability_windows":{"description":"Present on personal views only (templates are per-user).","items":{"$ref":"#/components/schemas/AvailabilityWindow"},"type":"array"},"exclusions":{"items":{"$ref":"#/components/schemas/CalendarExclusion"},"type":"array"},"from":{"format":"date-time","type":"string"},"items":{"description":"Merged appointments + on-call, ordered by starts_at then id.","items":{"$ref":"#/components/schemas/CalendarItem"},"type":"array"},"timezone":{"type":"string"},"to":{"format":"date-time","type":"string"},"user_id":{"description":"None on team-wide views.","format":"uuid","type":["string","null"]}},"required":["from","to","timezone","items","exclusions","availability_windows"],"type":"object"},"CancelDeadWorkRequest":{"description":"The cancel verb's body. Required, for the same reason the quarantine\nrelease reason is: the row will be read a month later and \"why did we give\nup on this\" is the only question it will be asked.","properties":{"reason":{"type":"string"}},"required":["reason"],"type":"object"},"CancelOrderRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"reason":{"type":["string","null"]}},"type":"object"}]},"CancelResponse":{"description":"What a cancel did, as the caller sees it.","properties":{"changed":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"outcome":{"description":"`cancelled` — the decision is recorded. `already_cancelled` — an earlier\ndecision stands and nothing was written.","type":"string"},"source":{"type":"string"}},"required":["source","id","outcome","changed"],"type":"object"},"CandidateView":{"allOf":[{"$ref":"#/components/schemas/CiMergeCandidate"},{"properties":{"candidate_tag":{"type":"string"},"primary_tag":{"type":"string"}},"required":["primary_tag","candidate_tag"],"type":"object"}]},"CapReachedBehavior":{"description":"What evaluation does when a T&M contract's cap is reached (B: never a\nsilent cutoff — the behavior is a typed choice on the cap).","enum":["stop","warn","overage_billable"],"type":"string"},"CapSpec":{"description":"NTE cap spec (B): typed not-to-exceed behavior.","properties":{"cap_minor":{"format":"int64","type":"integer"},"overage_rate_minor":{"format":"int64","type":["integer","null"]},"reached_behavior":{"$ref":"#/components/schemas/CapReachedBehavior","description":"`stop|warn|overage_billable`."},"scope":{"description":"`period|contract`.","type":"string"},"work_type":{"description":"Optional work_type bucket the cap covers (`None` = all labor).","type":["string","null"]}},"required":["scope","cap_minor","reached_behavior"],"type":"object"},"CapabilityGrantList":{"properties":{"grants":{"items":{"$ref":"#/components/schemas/CapabilityGrantView"},"type":"array"}},"required":["grants"],"type":"object"},"CapabilityGrantView":{"description":"One granted capability with its dial position.","properties":{"capability":{"type":"string"},"granted_at":{"type":"string"},"granted_by":{"format":"uuid","type":"string"},"mode":{"$ref":"#/components/schemas/CapabilityMode"},"updated_at":{"type":"string"}},"required":["capability","mode","granted_by","granted_at","updated_at"],"type":"object"},"CapabilityMode":{"description":"Wire involvement-dial position of a capability grant.","enum":["proposed","autonomous"],"type":"string"},"CapacityForecastEntryView":{"description":"One technician's line in the forecast: committed project load beside\nbooked appointments.","properties":{"allocation_count":{"format":"int64","type":"integer"},"available_percent_days":{"description":"The window's total available percent-days for this technician, from\ntheir availability windows minus calendar exclusions. Zero means the\ntechnician has no declared availability in the window, which is a\ndifferent state from being fully booked and must not render as 100%.","format":"double","type":"number"},"committed_percent_days":{"description":"Committed project load in PERCENT-DAYS. 200.0 = \"100% of one\ntechnician's time for two days\", or two phases at 100% for one day.\nA plain percentage would flatten a month-long 20% plan and a\nweek-long 80% plan into the same number, and they are not the same\ncommitment.","format":"double","type":"number"},"display_name":{"type":"string"},"over_allocated":{"type":"boolean"},"phase_count":{"format":"int64","type":"integer"},"user_id":{"format":"uuid","type":"string"},"utilization":{"description":"`committed / available`, or `None` when availability is zero. A\nratio with a zero denominator is the classic way a forecast reports\n\"infinite overload\" for a technician nobody scheduled.","format":"double","type":["number","null"]}},"required":["user_id","display_name","committed_percent_days","allocation_count","phase_count","available_percent_days","over_allocated"],"type":"object"},"CapacityForecastView":{"description":"The forecast-shaped read model: committed project load per technician\nacross a window, which is what `/dispatch/capacity` could never show\nbecause it only knew about appointments.","properties":{"days":{"format":"int64","type":"integer"},"entries":{"items":{"$ref":"#/components/schemas/CapacityForecastEntryView"},"type":"array"},"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to","days","entries"],"type":"object"},"CapacityHorizonView":{"description":"The assembled horizon payload. `unassigned_count` is the current open\nunassigned queue depth so planners see demand next to capacity.","properties":{"days":{"format":"int32","minimum":0,"type":"integer"},"from":{"format":"date","type":"string"},"technicians":{"items":{"$ref":"#/components/schemas/TechnicianCapacity"},"type":"array"},"unassigned_count":{"format":"int64","type":"integer"}},"required":["from","days","technicians","unassigned_count"],"type":"object"},"CapacityQuery":{"description":"GET /api/dispatch/capacity query: `from` date (defaults to today UTC)\nand the horizon length in days (1..=31, default 7).","properties":{"days":{"description":"Horizon length in days (1..=31).","format":"int32","minimum":0,"type":["integer","null"]},"from":{"format":"date","type":["string","null"]}},"type":"object"},"CaptureDefaultsResponse":{"description":"The capture surface's read: ordered picker + theHELP defaults.","properties":{"categories":{"description":"Non-retired categories ordered by position (the picker).","items":{"$ref":"#/components/schemas/TimeCategory"},"type":"array"},"work_type_map":{"description":"The backfill/manual mapping (legacy value → slug) so capture\nsurfaces can normalize pasted free text honestly.","items":{"$ref":"#/components/schemas/WorkTypeMapRowWire"},"type":"array"}},"required":["categories","work_type_map"],"type":"object"},"CatalogColumn":{"description":"One catalog column: the static identifier plus its value class slug\n(`uuid | text | timestamp | date | int | bigint | bool`). The slug is\nwhat the builder UI keys operand typing on.","properties":{"name":{"type":"string"},"value_type":{"type":"string"}},"required":["name","value_type"],"type":"object"},"CatalogEntryView":{"properties":{"created_at":{"format":"date-time","type":"string"},"description":{"type":"string"},"id":{"format":"uuid","type":"string"},"interface_version":{"format":"int32","minimum":0,"type":"integer"},"latest_version":{"type":["string","null"]},"name":{"type":"string"},"publisher":{"type":"string"},"revoked":{"type":"boolean"},"revoked_reason":{"type":["string","null"]},"slug":{"type":"string"}},"required":["id","slug","name","description","publisher","interface_version","revoked","created_at"],"type":"object"},"CatalogItemMutationResponse":{"allOf":[{"$ref":"#/components/schemas/CatalogItemView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2) for catalog mutations."},"CatalogItemView":{"description":"RB2 (ADR 0004): a catalog item as the CALLER may see it. `unit_price`\nis the commercial surface; `cost_minor` (and the gross margin derived\nfrom it) is financial data — it serializes `null` for callers without\n`financial:read` (dispatchers and technicians by default), which is\nthe \"those values render blank\" rule. The write trail keeps full\nfidelity server-side; only client-facing views redact.","properties":{"active":{"type":"boolean"},"category":{"type":["string","null"]},"cost_minor":{"description":"`null` without `financial:read` (RB2, ADR 0004).","format":"int64","type":["integer","null"]},"created_at":{"type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"currency":{"type":"string"},"description":{"type":["string","null"]},"group_name":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"sku":{"type":["string","null"]},"taxable":{"type":"boolean"},"unit":{"type":"string"},"unit_price_minor":{"format":"int64","type":"integer"},"updated_at":{"type":"string"}},"required":["id","org_id","name","unit","currency","unit_price_minor","taxable","active","created_at","updated_at"],"type":"object"},"CatalogListQuery":{"properties":{"active":{"description":"`None` = all items (active + archived).","type":["boolean","null"]},"category":{"type":["string","null"]},"group_name":{"type":["string","null"]},"search":{"description":"Substring search over name/sku/category/group.","type":["string","null"]}},"type":"object"},"CatalogView":{"description":"The field catalog of ONE versioned view: every projected column plus the\nnarrower filterable inventory. Identifiers here are the ONLY ones a\nbuilder may reference — the server re-validates every spec against the\nsame Rust catalog at save and run time.","properties":{"columns":{"items":{"$ref":"#/components/schemas/CatalogColumn"},"type":"array"},"filterable":{"items":{"$ref":"#/components/schemas/CatalogColumn"},"type":"array"},"view":{"$ref":"#/components/schemas/ViewId"}},"required":["view","columns","filterable"],"type":"object"},"CatalogueNode":{"description":"One node as the authoring prompt sees it: NO schemas. Schemas are fetched\nper node, on demand, through [`NODE_SCHEMA_TOOL`]. `idempotent` and\n`agent_description` ride here so the model can reason about repeat safety\nbefore it ever fetches a schema.","properties":{"agent_description":{"type":"string"},"emits":{"items":{"type":"string"},"type":"array"},"executable":{"description":"Whether the executor can run this node today (Wave 43 lane 20, item\nC). The authoring model is told, so it does not place a node the\nvalidator will refuse.","type":"boolean"},"family":{"type":"string"},"id":{"type":"string"},"idempotent":{"type":"boolean"},"label":{"type":"string"},"tier":{"type":"string"}},"required":["id","label","agent_description","family","tier","idempotent","emits","executable"],"type":"object"},"CategoryMutationResponse":{"allOf":[{"$ref":"#/components/schemas/KbCategory"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): category fields at the top level\nplus the additive `mutation` metadata."},"ChangeStatusRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive."},{"properties":{"status_id":{"format":"uuid","type":"string"}},"required":["status_id"],"type":"object"}]},"ChatTurnRequest":{"properties":{"messages":{"description":"The conversation so far (system prompt first). The client owns the\ntranscript; the runner persists none of it.","items":{"$ref":"#/components/schemas/LoopMessage"},"type":"array"},"model":{"type":["string","null"]},"provider":{"description":"Optional explicit (provider, model) ask — must sit inside the\nadmin allowlist, exactly like the authorize edge.","type":["string","null"]},"session_opener":{"description":"True only on a session's first turn — the\n`ai.chat_session_started` event rides that turn alone.","type":"boolean"},"surface_key":{"type":"string"},"ticket_id":{"description":"`ticket_note_assist` only: the ticket the assist drafts against\n(the IF8 proposal target).","type":["string","null"]}},"required":["surface_key","messages"],"type":"object"},"CheckinLineRequest":{"properties":{"line_id":{"format":"uuid","type":"string"},"return_condition":{"description":"good | fair | damaged.","type":["string","null"]},"return_notes":{"type":["string","null"]}},"required":["line_id"],"type":"object"},"CheckinRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"lines":{"description":"Lines to return; empty/absent = every outstanding unit.","items":{"$ref":"#/components/schemas/CheckinLineRequest"},"type":"array"},"notes":{"type":["string","null"]},"to_location_id":{"description":"Where the tools land; absent = the checkout's own location.","format":"uuid","type":["string","null"]}},"type":"object"}],"description":"POST /api/tool-checkouts/{id}/checkin."},"ChecklistItem":{"description":"One line of a ticket's pre-close checklist. Items instantiated from the\nboard's template carry `template_item_id`; those are REQUIRED when the\nboard enforces pre-close checklists (see `crate::notes`). Ad-hoc items\n(`template_item_id == None`) never block closure.","properties":{"checked":{"type":"boolean"},"checked_at":{"format":"date-time","type":["string","null"]},"checked_by":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"na":{"description":"Shared \"not applicable\" state (Wave 12 lane 6): server truth for the\ntasks-panel tri-state, set by the toggle endpoint in the same tx as\n`checked`. Mutually exclusive with `checked` (enforced at the API);\nN-A REQUIRED items are exempt from pre-close enforcement and derived\nprogress. `#[serde(default)]` keeps older payloads (no `na` key)\ndeserializing as false — additive-only contract.","type":"boolean"},"org_id":{"format":"uuid","type":"string"},"sort_order":{"format":"int32","type":"integer"},"template_item_id":{"description":"Template provenance: `Some` ⇒ required under enforcement.","format":"uuid","type":["string","null"]},"text":{"type":"string"},"ticket_id":{"format":"uuid","type":"string"}},"required":["id","org_id","ticket_id","text","checked","sort_order"],"type":"object"},"ChecklistProgress":{"description":"Derived-at-read-time checklist progress (never materialized — see the\n0016 migration header for the justification). `required_open` counts\nunchecked template-derived instances: the close-blocking set when the\nboard enforces.","properties":{"checked":{"description":"Checked rows across both classes.","format":"int64","type":"integer"},"required_open":{"description":"Unchecked REQUIRED (template-derived) rows.","format":"int64","type":"integer"},"total":{"description":"All instance rows (template-derived + ad-hoc).","format":"int64","type":"integer"}},"required":["total","checked","required_open"],"type":"object"},"ChecklistTemplate":{"description":"A board's pre-close checklist template. Exactly one per board\n(UNIQUE in storage): the deterministic gate consulted at close time.","properties":{"board_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","board_id","name","created_at","updated_at"],"type":"object"},"ChecklistTemplateItem":{"description":"One required step in a template. Instances on tickets link back to this\nid (`checklist_items.template_item_id`), so enforcement follows identity,\nnever fragile text matching.","properties":{"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"sort_order":{"format":"int32","type":"integer"},"template_id":{"format":"uuid","type":"string"},"text":{"type":"string"}},"required":["id","org_id","template_id","text","sort_order"],"type":"object"},"ChecklistTemplateView":{"description":"GET /api/boards/{id}/checklist-template response: the template (if any),\nits items, and the enforcement posture actually applied at close time.","properties":{"items":{"items":{"$ref":"#/components/schemas/ChecklistTemplateItem"},"type":"array"},"require_checklist_close":{"description":"Mirrors `boards.require_checklist_close`.","type":"boolean"},"template":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ChecklistTemplate"}]}},"required":["items","require_checklist_close"],"type":"object"},"CheckoutItemRequest":{"properties":{"serial_id":{"format":"uuid","type":["string","null"]},"stock_item_id":{"format":"uuid","type":"string"}},"required":["stock_item_id"],"type":"object"},"CheckoutListQuery":{"properties":{"holder_user_id":{"format":"uuid","type":["string","null"]},"kit_id":{"format":"uuid","type":["string","null"]},"limit":{"format":"int64","type":["integer","null"]},"overdue_only":{"description":"Only open checkouts past their due date.","type":["boolean","null"]},"status":{"description":"open | returned (absent = both).","type":["string","null"]}},"type":"object"},"ChunkRequest":{"allOf":[{"$ref":"#/components/schemas/RowsPayload"},{"properties":{"template_id":{"description":"Optional saved field-mapping template applied before validation.","format":"uuid","type":["string","null"]}},"type":"object"}],"description":"POST /imports/batches/{id}/chunks request body."},"ChunkSummaryWire":{"description":"u32-friendly mirror of [`core_domain::import_core::ChunkSummary`] so the\ngenerated SDK gets plain integers without overflow caveats.","properties":{"failed":{"format":"int32","minimum":0,"type":"integer"},"ok":{"format":"int32","minimum":0,"type":"integer"},"total":{"format":"int32","minimum":0,"type":"integer"},"would_skip":{"format":"int32","minimum":0,"type":"integer"},"would_update":{"format":"int32","minimum":0,"type":"integer"}},"required":["total","ok","would_update","would_skip","failed"],"type":"object"},"CiFieldPrecedence":{"description":"A per-type/per-source precedence override (CI5).","properties":{"created_at":{"format":"date-time","type":"string"},"field_key":{"type":"string"},"id":{"format":"uuid","type":"string"},"layout_id":{"format":"uuid","type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"priority":{"format":"int32","type":"integer"},"semantic_class":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CiSemanticClass"}]},"source_system":{"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","field_key","source_system","priority","created_at","updated_at"],"type":"object"},"CiInterface":{"description":"A NIC attribute of a config item (ADR 0003 lifecycle rule).","properties":{"config_item_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"ips":{"items":{"type":"string"},"type":"array"},"is_primary":{"type":"boolean"},"mac":{"description":"Normalized lower colon form, e.g. `aa:bb:cc:dd:ee:ff`.","type":["string","null"]},"name":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"source_link_id":{"format":"uuid","type":["string","null"]},"updated_at":{"format":"date-time","type":"string"},"vlans":{"items":{"type":"string"},"type":"array"}},"required":["id","org_id","config_item_id","created_at","updated_at"],"type":"object"},"CiMerge":{"description":"An audited, undoable merge (the split substrate).","properties":{"candidate_id":{"format":"uuid","type":["string","null"]},"confidence":{"format":"double","type":["number","null"]},"evidence":{},"id":{"format":"uuid","type":"string"},"merged_at":{"format":"date-time","type":"string"},"merged_by":{"format":"uuid","type":["string","null"]},"merged_config_item_id":{"format":"uuid","type":"string"},"moved":{},"org_id":{"format":"uuid","type":"string"},"primary_config_item_id":{"format":"uuid","type":"string"},"reason":{"type":"string"},"status":{"type":"string"},"undone_at":{"format":"date-time","type":["string","null"]},"undone_by":{"format":"uuid","type":["string","null"]}},"required":["id","org_id","primary_config_item_id","merged_config_item_id","reason","status","merged_at"],"type":"object"},"CiMergeCandidate":{"description":"A reviewable ambiguous match (ADR 0003): visible evidence, one-click\naccept/reject, auto-merge only above the org threshold.","properties":{"candidate_config_item_id":{"format":"uuid","type":"string"},"confidence":{"format":"double","type":"number"},"created_at":{"format":"date-time","type":"string"},"decided_at":{"format":"date-time","type":["string","null"]},"decided_by":{"format":"uuid","type":["string","null"]},"evidence":{},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"primary_config_item_id":{"format":"uuid","type":"string"},"reason":{"type":"string"},"status":{"$ref":"#/components/schemas/CiMergeStatus"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","primary_config_item_id","candidate_config_item_id","confidence","reason","status","created_at","updated_at"],"type":"object"},"CiMergeRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first mutation parameters (IF1)."},{"properties":{"candidate_id":{"format":"uuid","type":["string","null"]},"merged_config_item_id":{"description":"The item to absorb into `{id}`.","format":"uuid","type":"string"},"reason":{"type":["string","null"]}},"required":["merged_config_item_id"],"type":"object"}]},"CiMergeStatus":{"description":"CI4: lifecycle of a reviewable merge candidate. `deferred` parks a\ncandidate out of the pending queue without deciding it (the workbench's\nbatch defer; reversible — a feed upsert re-activates it via upsert).","enum":["pending","auto_merged","accepted","rejected","deferred"],"type":"string"},"CiSemanticClass":{"description":"CI6: a semantic class is the behavior-bearing identity of a CI type.\nLayouts (types) are org-configurable and renameable; the class is not.","enum":["workstation","server","vm","network_appliance","printer","iot"],"type":"string"},"CiSettingsView":{"properties":{"auto_merge_threshold":{"format":"double","type":"number"}},"required":["auto_merge_threshold"],"type":"object"},"ClaimShareLinkRequest":{"properties":{"token":{"description":"The one-time token, verbatim from the create response.","type":"string"}},"required":["token"],"type":"object"},"Client":{"properties":{"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"notes":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"restricted_to":{"description":"Optional per-client visibility: when set, only these users (plus\nadmins) can see the client. Empty = all org members.","items":{"format":"uuid","type":"string"},"type":"array"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","name","restricted_to","created_at","updated_at"],"type":"object"},"ClientCompletenessView":{"description":"The backend documentation-completeness read model — the weights live\nhere; the web card renders the facts.","properties":{"basis":{"description":"One human sentence, honest about the untouched legs (n/a).","type":"string"},"gaps":{"items":{"$ref":"#/components/schemas/CompletenessGap"},"type":"array"},"score":{"description":"0..=100, pro-rated over the legs the facts CAN speak about\n(never a silent zero and never a padded 100).","format":"int32","type":"integer"}},"required":["score","gaps","basis"],"type":"object"},"ClientCsatRollup":{"description":"One client's CSAT rollup over the window (responses scored 1-5;\n\"low\" = score ≤ 2).","properties":{"avg_score":{"format":"double","type":["number","null"]},"client_id":{"format":"uuid","type":"string"},"client_name":{"type":"string"},"low_scores":{"format":"int64","type":"integer"},"responses":{"format":"int64","type":"integer"}},"required":["client_id","client_name","responses","low_scores"],"type":"object"},"ClientCsatRollupResponse":{"properties":{"available":{"type":"boolean"},"rollups":{"items":{"$ref":"#/components/schemas/ClientCsatRollup"},"type":"array"},"window":{"$ref":"#/components/schemas/ScorecardWindow"}},"required":["window","available","rollups"],"type":"object"},"ClientLoanDetailResponse":{"properties":{"lines":{"items":{"$ref":"#/components/schemas/ClientLoanLineView"},"type":"array"},"loan":{"$ref":"#/components/schemas/ClientLoanView"}},"required":["loan","lines"],"type":"object"},"ClientLoanLineView":{"properties":{"device_asset_id":{"description":"The device's row in the client's fleet (instantiated on issue for\nstock-sourced devices; ADR 0003 identity keys converge later feeds).","format":"uuid","type":["string","null"]},"from_stock":{"description":"True when the device came from org stock (rode the ledger).","type":"boolean"},"id":{"format":"uuid","type":"string"},"movement_in_id":{"format":"uuid","type":["string","null"]},"movement_out_id":{"format":"uuid","type":["string","null"]},"return_condition":{"type":["string","null"]},"return_notes":{"type":["string","null"]},"returned_at":{"type":["string","null"]},"serial_id":{"format":"uuid","type":["string","null"]},"stock_item_id":{"format":"uuid","type":["string","null"]}},"required":["id","from_stock"],"type":"object"},"ClientLoanMutationResponse":{"properties":{"lines":{"items":{"$ref":"#/components/schemas/ClientLoanLineView"},"type":"array"},"loan":{"$ref":"#/components/schemas/ClientLoanView"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["loan","lines","mutation"],"type":"object"},"ClientLoanView":{"properties":{"agreement_ref":{"type":["string","null"]},"client_id":{"format":"uuid","type":"string"},"contract_id":{"format":"uuid","type":["string","null"]},"created_at":{"type":"string"},"expected_return_at":{"type":"string"},"id":{"format":"uuid","type":"string"},"is_lease":{"type":"boolean"},"issued_by":{"format":"uuid","type":["string","null"]},"lease_cost_minor":{"format":"int64","type":["integer","null"]},"lease_currency":{"type":["string","null"]},"lease_cycle":{"type":["string","null"]},"notes":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"out_at":{"type":"string"},"overdue":{"description":"Derived: open past the expected return date.","type":"boolean"},"return_notes":{"type":["string","null"]},"returned_at":{"type":["string","null"]},"status":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","org_id","client_id","out_at","expected_return_at","status","is_lease","overdue","created_at","updated_at"],"type":"object"},"ClientMutationResponse":{"allOf":[{"$ref":"#/components/schemas/Client"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): client fields at the top level\n(unchanged shape) plus the additive `mutation` metadata."},"ClientOrgMap":{"description":"The durable operator mapping a reconciliation accept writes: one row\nper (source system, vendor org id), pointing at one Marula client.","properties":{"client_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"source_system":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"vendor_label":{"type":["string","null"]},"vendor_org_id":{"type":"string"}},"required":["id","org_id","source_system","vendor_org_id","client_id","created_at","updated_at"],"type":"object"},"ClientOverrideRow":{"description":"The stored client row, as provenance on a resolved entry (who last set it\nand when — the audit subject; the append-only trail keeps the full history).","properties":{"changed_by":{"format":"uuid","type":["string","null"]},"state":{"description":"\"set\" | \"disabled\".","type":"string"},"updated_at":{"type":"string"},"value":{"type":["object","null"]}},"required":["state","value","updated_at"],"type":"object"},"ClientWorkflowResolution":{"description":"The client's full resolved workflow: one entry per registry declaration,\nregistry order preserved.","properties":{"client_id":{"format":"uuid","type":"string"},"entries":{"items":{"$ref":"#/components/schemas/OverrideEntry"},"type":"array"}},"required":["client_id","entries"],"type":"object"},"CloseKind":{"enum":["full","short"],"type":"string"},"CloseOrderRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"}]},"CommentListQuery":{"properties":{"kind":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/NoteKind","description":"Optional taxonomy filter (`internal`, `client_visible`,\n`in_house_update`). Omit for all kinds (staff view)."}]}},"type":"object"},"CommittedChunkRef":{"description":"One committed chunk's position in the resume view.","properties":{"chunk_index":{"format":"int64","type":"integer"},"committed_at":{"type":"string"}},"required":["chunk_index","committed_at"],"type":"object"},"CompleteAllRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + version."},{"properties":{"task_ids":{"description":"Checklist items to complete. Items already checked/N-A are skipped\n(idempotent); unknown ids 404 the whole call (batch atomicity).","items":{"format":"uuid","type":"string"},"type":"array"}},"required":["task_ids"],"type":"object"}],"description":"POST /api/tickets/{id}/checklist/complete-all request (Wave 17 lane 11\n§5.3): the close gate's Option A batch — every listed item flips to\n`checked` and each lands its auto-append note, ALL in one transaction\nwith per-item idempotency keys (duplicate-proof at the envelope, scored\nas REPORTED auto notes via `workflow_meta.auto = true`)."},"CompletenessGap":{"description":"One named scoring leg that couldn't earn its weight.","properties":{"hint":{"type":"string"},"key":{"description":"Stable vocabulary the web card maps to copy (same keys the client\nlane's card used — the render contract did not move).","type":"string"},"label":{"type":"string"}},"required":["key","label","hint"],"type":"object"},"ConfigItemMutationResponse":{"allOf":[{"$ref":"#/components/schemas/DeviceAsset"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2)."},"ConfigItemView":{"allOf":[{"$ref":"#/components/schemas/DeviceAsset"},{"properties":{"interfaces":{"items":{"$ref":"#/components/schemas/CiInterface"},"type":"array"},"semantic_class":{"description":"The layout's CI6 semantic class (None = untyped).","type":["string","null"]},"source_links":{"items":{"$ref":"#/components/schemas/ConfigSourceLink"},"type":"array"}},"type":"object"}]},"ConfigSourceLink":{"description":"A feeder's per-source record link (ADR 0003). 0..N per config item; the\nunique key is `(org, source_system, external_id)`.","properties":{"config_item_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"external_id":{"type":"string"},"id":{"format":"uuid","type":"string"},"last_seen_at":{"format":"date-time","type":"string"},"org_id":{"format":"uuid","type":"string"},"payload_ref":{"type":["string","null"]},"source_guid":{"type":["string","null"]},"source_system":{"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","config_item_id","source_system","external_id","last_seen_at","created_at","updated_at"],"type":"object"},"ConflictCheckRequest":{"properties":{"ends_at":{"format":"date-time","type":"string"},"exclude_appointment_id":{"description":"Appointment excluded from the check (its own id when rescheduling).","format":"uuid","type":["string","null"]},"starts_at":{"format":"date-time","type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","starts_at","ends_at"],"type":"object"},"ConflictCheckResponse":{"properties":{"conflicts":{"items":{"$ref":"#/components/schemas/ConflictView"},"type":"array"},"has_conflict":{"type":"boolean"}},"required":["has_conflict","conflicts"],"type":"object"},"ConflictPolicy":{"description":"Per-entity conflict semantics when an external key already maps to a\nlive row whose content differs from the incoming spec.","enum":["skip","update","fail"],"type":"string"},"ConflictView":{"description":"One blocking booking named by the dry-run.","properties":{"appointment_id":{"format":"uuid","type":"string"},"ends_at":{"format":"date-time","type":"string"},"starts_at":{"format":"date-time","type":"string"},"title":{"type":"string"}},"required":["appointment_id","title","starts_at","ends_at"],"type":"object"},"ConnectionView":{"properties":{"connection":{"$ref":"#/components/schemas/CalendarSyncConnectionView"},"feed_token":{"description":"The raw outbound feed token — returned ONLY here (creation) and\nby the rotate endpoint. Never readable again afterwards.","type":["string","null"]}},"required":["connection"],"type":"object"},"ConnectorAppointment":{"description":"One booking as the connector sees it.","properties":{"client_id":{"format":"uuid","type":["string","null"]},"client_name":{"type":["string","null"]},"dispatch_status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ConnectorStatusView","description":"Latest pushed field-service status, when any."}]},"ends_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"kind":{"type":"string"},"starts_at":{"format":"date-time","type":"string"},"status":{"type":"string"},"technician_name":{"type":"string"},"technician_user_id":{"format":"uuid","type":"string"},"ticket_id":{"format":"uuid","type":["string","null"]},"ticket_number":{"type":["string","null"]},"title":{"type":"string"},"travel_minutes":{"format":"int32","type":"integer"}},"required":["id","technician_user_id","technician_name","title","starts_at","ends_at","kind","status","travel_minutes"],"type":"object"},"ConnectorIndexResponse":{"properties":{"installed":{"description":"Installed connectors (typed kinds only, presence summaries).","items":{"$ref":"#/components/schemas/ConnectorInstallationView"},"type":"array"}},"required":["installed"],"type":"object"},"ConnectorInstallRequest":{"properties":{"config":{"description":"Provider configuration document — WHOLESALE sealed (never\nlogged, never echoed). The adapters expect, per kind:\nslack/teams `{ \"webhook_url\": \"https://…\" }`,\nrewst `{ \"webhook_url\": \"https://…\", \"secret\": \"…\" }`,\nvoip `{ \"access_token\": \"…\", \"provider\": \"…\" }`."}},"required":["config"],"type":"object"},"ConnectorInstallationView":{"properties":{"capability_scopes":{"items":{"type":"string"},"type":"array"},"kind":{"type":"string"},"updated_at":{"format":"date-time","type":["string","null"]}},"required":["kind","capability_scopes"],"type":"object"},"ConnectorProfile":{"description":"ADR 0009 §6 (Wave 19 lane 2): the modeled connector governance\nprofile — WHERE the connector speaks (webhook endpoints) and what it\nwears outward (SaaS scopes). Structured, validated DECLARATIONS, not\nprose: the consent screen renders them verbatim and connectors inherit\nthe platform's egress posture from the declared endpoint list.","properties":{"outbound_scopes":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"description":"ADR 0023 Decision 3 (Wave 53 lane 2): the EGRESS allowlist a wasm\nguest's `http_request` reads — profile name -> consented scopes. Each\nscope is `https://host[:port][/path-prefix]`; a request whose scheme is\nnot https, whose authority is not listed, or whose path is outside the\nscope's prefix is refused (`plugin_egress_scheme_denied`,\n`plugin_egress_host_denied`). The map is what makes\n`plugin_egress_profile_missing` a real refusal: a guest names a profile,\nand a name the install did not consent has no list to fall back to.\n\nThis one field is why the profile is no longer connector-only. The\nrelaxation is narrow on purpose: `webhook_endpoints` and `routing` stay\nconnector-only, so the rule that stopped a connector faking a UI\nsurface still holds, and a wasm manifest gains exactly the declaration\nthe executor needs.","propertyNames":{"type":"string"},"type":"object"},"routing":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RoutingProfile","description":"Wave 32 lane 7 (ADR 0014): present when the pack answers the\nMarula Routing Contract endpoints (`/routing/matrix`, …). The\ndeclaration mirrors the operator config 1:1 so consent shows\nexactly what the adapter will honor."}]},"scopes":{"description":"The outbound scopes the connector asks the upstream SaaS for\n(e.g. `graph:mail.read`); opaque strings to the core — they exist\nfor consent-time visibility and install-record provenance, max 32.","items":{"type":"string"},"type":"array"},"webhook_endpoints":{"description":"The webhook URLs traffic from the platform sends toward. https-only\nabsolute URLs, host required, no credentials in the URL, max 8.","items":{"type":"string"},"type":"array"}},"type":"object"},"ConnectorScheduleQuery":{"description":"GET /api/dispatch-connector/schedule query. The window is bounded at\n62 days so one pull cannot scan the whole calendar.","properties":{"days":{"description":"Window length in days (1..=62, default 7).","format":"int32","minimum":0,"type":["integer","null"]},"from":{"format":"date","type":["string","null"]}},"type":"object"},"ConnectorScheduleView":{"properties":{"appointments":{"items":{"$ref":"#/components/schemas/ConnectorAppointment"},"type":"array"},"days":{"format":"int32","minimum":0,"type":"integer"},"from":{"format":"date","type":"string"}},"required":["from","days","appointments"],"type":"object"},"ConnectorStatusView":{"description":"The latest status push for one booking.","properties":{"note":{"type":["string","null"]},"occurred_at":{"format":"date-time","type":"string"},"source":{"description":"`connector` | `staff`.","type":"string"},"status":{"type":"string"}},"required":["status","source","occurred_at"],"type":"object"},"ConnectorTechnician":{"description":"GET /api/dispatch-connector/technicians entry.","properties":{"display_name":{"type":"string"},"skills":{"description":"Current (non-expired) skills — lets the external planner respect\ncertifications before proposing a dispatch.","items":{"type":"string"},"type":"array"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","display_name","skills"],"type":"object"},"ConnectorTechniciansView":{"properties":{"technicians":{"items":{"$ref":"#/components/schemas/ConnectorTechnician"},"type":"array"}},"required":["technicians"],"type":"object"},"ConsentDiffView":{"properties":{"added":{"items":{"type":"string"},"type":"array"},"removed":{"items":{"type":"string"},"type":"array"},"unchanged":{"items":{"type":"string"},"type":"array"}},"required":["added","removed","unchanged"],"type":"object"},"ConsentView":{"description":"The consent screen payload — ONE component shared by every source\n(same shape the update re-consent flow shows).","properties":{"artifact_digest":{"type":"string"},"artifact_kind":{"type":"string"},"connector_profile":{"description":"ADR 0009 §6 full semantics — the modeled connector governance\nprofile (webhook endpoints + scopes) when the manifest declared\none, for verbatim consent rendering."},"declared_capabilities":{"items":{"type":"string"},"type":"array"},"fetched_ref":{"type":["string","null"]},"manifest_authenticated":{"description":"Whether the publisher's signature covers this manifest (the registry\nv2 statement) or only the artifact bytes (legacy v1, or no signature at\nall). The consent screen shows it so an approver can tell a\npublisher-signed manifest from one that merely travelled beside a\nsignature.","type":"boolean"},"settings_schema":{},"source_kind":{"type":"string"},"source_url":{"type":"string"},"surface_profile":{"description":"§4.6 (Wave 34 lane 4) — the declarative UI-surface profile\n(widgets / automation action kinds / report render kinds /\nfield validators), rendered on consent verbatim so an admin\nconsents to exactly the surfaces the plugin contributes."}},"required":["artifact_kind","source_kind","source_url","artifact_digest","manifest_authenticated","declared_capabilities"],"type":"object"},"ConsumeRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"item_id":{"format":"uuid","type":"string"},"location_id":{"format":"uuid","type":"string"},"note":{"type":["string","null"]},"qty_milli":{"description":"MILLI to consume (positive; the ledger stores it negative).","format":"int64","type":"integer"},"serial_id":{"description":"Serialized consumption moves ONE unit.","format":"uuid","type":["string","null"]},"ticket_id":{"description":"The on-site job consuming the parts (traceability anchor).","format":"uuid","type":["string","null"]}},"required":["item_id","location_id","qty_milli"],"type":"object"}]},"Contact":{"properties":{"client_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"curated_fields":{},"email":{"type":["string","null"]},"field_sources":{},"id":{"format":"uuid","type":"string"},"is_primary":{"type":"boolean"},"merged_into_id":{"description":"Merge tombstone: when set, this row was folded into the target and\nonly its audit row keeps it queryable.","format":"uuid","type":["string","null"]},"name":{"type":"string"},"observed_fields":{"description":"CI5 tiers generalized to contacts (ADR 0003 extension): observed\nfeed-written fields, human-curated sticky fields, per-field source\nprovenance. Empty maps for pre-0101 rows."},"org_id":{"format":"uuid","type":"string"},"phone":{"type":["string","null"]},"site_id":{"format":"uuid","type":["string","null"]},"title":{"type":["string","null"]}},"required":["id","org_id","client_id","name","is_primary","created_at"],"type":"object"},"ContactFeedUpsertRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first mutation parameters (IF1)."},{"properties":{"client_id":{"format":"uuid","type":"string"},"email":{"type":["string","null"]},"external_id":{"type":"string"},"name":{"type":"string"},"payload_ref":{"type":["string","null"]},"phone":{"type":["string","null"]},"source_guid":{"type":["string","null"]},"source_system":{"type":"string"},"title":{"type":["string","null"]}},"required":["source_system","external_id","client_id","name"],"type":"object"}]},"ContactFeedUpsertResponse":{"properties":{"auto_merged":{"type":"boolean"},"contact":{"$ref":"#/components/schemas/Contact"},"contradictions":{"items":{"type":"string"},"type":"array"},"created_contact":{"type":"boolean"},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"queued_candidates":{"items":{"$ref":"#/components/schemas/EntityMergeCandidate"},"type":"array"},"resolved_existing":{"type":"boolean"},"source_link":{"$ref":"#/components/schemas/EntitySourceLink"}},"required":["contact","source_link","mutation"],"type":"object"},"ContactsMutationResponse":{"properties":{"contacts":{"items":{"$ref":"#/components/schemas/VendorContact"},"type":"array"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["contacts","mutation"],"type":"object"},"Contract":{"description":"A commercial agreement with one client. Money fields are integer minor\nunits (cents for USD) — floats are forbidden near amounts (§5).","properties":{"amount_minor":{"description":"Per-period price (recurring/retainer), minor units.","format":"int64","type":["integer","null"]},"billing_anchor":{"description":"Required for recurring/retainer; the first billing period's start.","format":"date","type":["string","null"]},"billing_frequency":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BillingFrequency","description":"Required for recurring/retainer; the period cadence."}]},"client_id":{"format":"uuid","type":"string"},"contract_type":{"$ref":"#/components/schemas/ContractType"},"created_at":{"format":"date-time","type":"string"},"currency":{"description":"ISO 4217 code; every amount on the contract uses this currency.","type":"string"},"effective_from":{"description":"First day the agreement can generate billable candidates.","format":"date","type":"string"},"effective_until":{"description":"Last day it can; `None` = open-ended.","format":"date","type":["string","null"]},"hourly_rate_minor":{"description":"Default hourly rate for T&M when no rate card row matches the\nentry's `work_type`, minor units per hour.","format":"int64","type":["integer","null"]},"id":{"format":"uuid","type":"string"},"incident_price_minor":{"description":"Per-incident: the fixed price per closed ticket in the covered\nband, minor units.","format":"int64","type":["integer","null"]},"incident_priority":{"description":"Per-incident: which closed tickets price — the four-tier priority\n(`p1|p2|p3|p4`), or `any` for every closed ticket. `NULL` = any.","type":["string","null"]},"included_minutes":{"description":"Retainer: labor minutes included in each period's base price.","format":"int32","type":["integer","null"]},"name":{"type":"string"},"notes":{"description":"Per-ticket capped bands (T&M, retainer, per-incident on the same\ncontract type is fine too): when set, evaluation enforces the cap.\nStored denormalized from `contract_caps` for read-path simplicity\n(the cap rows carry the authoritative state).","type":["string","null"]},"number":{"description":"Human contract number, unique per org (e.g. \"C-2026-014\").","type":"string"},"org_id":{"format":"uuid","type":"string"},"overage_rate_minor":{"description":"Retainer: hourly rate for minutes beyond the included block.","format":"int64","type":["integer","null"]},"po_number":{"description":"Client purchase-order reference printed on the invoices this\ncontract produces (F: PO number rides the contract so runs carry\nit forward without per-invoice entry).","type":["string","null"]},"status":{"$ref":"#/components/schemas/ContractStatus"},"unit_basis":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/UnitBasis","description":"Per-unit recurring: which inventory counts the units — `devices`\n(device_assets for the client) or `users` (org users billed to the\nclient's contract). Defaults to `devices` at evaluation when unset."}]},"unit_price_minor":{"description":"Per-unit recurring: the price per unit per period, minor units.","format":"int64","type":["integer","null"]},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","client_id","number","name","contract_type","status","currency","effective_from","created_at","updated_at"],"type":"object"},"ContractBlock":{"description":"A prepaid block (hour bank): sold up front, drawn down on use, with\nexpiry + rollover policies (A). Rows are immutable once sold —\nrollover MINTS a successor block at expiry, it never edits the old\nrow (additive-ledger doctrine).","properties":{"block_rate_minor":{"description":"Optional discounted hourly rate for labor priced against this\nblock; `None` bills draws at the card/default rate with the block\nabsorbing the minutes (a pure prepaid balance).","format":"int64","type":["integer","null"]},"contract_id":{"format":"uuid","type":"string"},"created_at":{"description":"Draw-down ledger: each consumption row subtracts from the block.\nThe remaining balance is computed, never stored.","format":"date-time","type":"string"},"expires_on":{"description":"Last day draws may land inside this block.","format":"date","type":["string","null"]},"id":{"format":"uuid","type":"string"},"label":{"description":"Human label (\"50-hour prepaid block, March 2026\").","type":"string"},"org_id":{"format":"uuid","type":"string"},"rollover_policy":{"description":"`expire` — unused minutes lapse at expiry; `rollover` — unused\nminutes mint a successor block (12 months) at expiry; `none` —\nno expiry, draws allowed indefinitely.","type":"string"},"sale_price_minor":{"description":"Sale price, minor units (usually total_minutes/60 × block rate).","format":"int64","type":"integer"},"total_minutes":{"description":"Total minutes sold.","format":"int32","type":"integer"}},"required":["id","org_id","contract_id","label","total_minutes","sale_price_minor","rollover_policy","created_at"],"type":"object"},"ContractCap":{"description":"A T&M not-to-exceed cap. `scope` picks the boundary:\n`period` enforces per billing period, `contract` enforces over the\ncontract's whole effective window.","properties":{"cap_minor":{"description":"The not-to-exceed amount, minor units of billable work per scope.","format":"int64","type":"integer"},"contract_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"overage_rate_minor":{"description":"Hourly rate for `overage_billable` beyond the cap; `None` bills\nthe matched card/default rate.","format":"int64","type":["integer","null"]},"reached_behavior":{"$ref":"#/components/schemas/CapReachedBehavior","description":"What happens when the cap is reached (typed, never silent)."},"scope":{"description":"`period` or `contract` (ARP-guarded CHECK in 0112).","type":"string"},"work_type":{"description":"Optional work_type filter: the cap applies only to that bucket.","type":["string","null"]}},"required":["id","org_id","contract_id","scope","cap_minor","reached_behavior","created_at"],"type":"object"},"ContractChangeOrder":{"description":"An approved change order: a billable amendment with its own lifecycle\n(E). Riding existing approval/audit patterns: statuses\n`draft → approved → billed`, rejected/cancelled terminal.","properties":{"amount_minor":{"format":"int64","type":"integer"},"approved_at":{"format":"date-time","type":["string","null"]},"approved_by":{"format":"uuid","type":["string","null"]},"billed_invoice_id":{"format":"uuid","type":["string","null"]},"contract_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"description":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"number":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"status":{"description":"`draft|approved|rejected|billed|cancelled`.","type":"string"},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","contract_id","number","title","amount_minor","status","created_at","updated_at"],"type":"object"},"ContractDetail":{"allOf":[{"$ref":"#/components/schemas/Contract"},{"properties":{"blocks":{"items":{"$ref":"#/components/schemas/ContractBlock"},"type":"array"},"caps":{"items":{"$ref":"#/components/schemas/ContractCap"},"type":"array"},"change_orders":{"description":"Lifecycle aggregates ride as counts on the detail (their own\nsurfaces list them; this keeps the aggregate view honest about\nattached commercial state).","items":{"$ref":"#/components/schemas/ContractChangeOrder"},"type":"array"},"escalator":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ContractEscalator"}]},"fees":{"items":{"$ref":"#/components/schemas/ContractFixedFee"},"type":"array"},"minimum_commitment_minor":{"description":"Per-period minimum commitment, minor units (`None` = none).","format":"int64","type":["integer","null"]},"rates":{"items":{"$ref":"#/components/schemas/ContractRate"},"type":"array"},"sla_credits":{"items":{"$ref":"#/components/schemas/ContractSlaCredit"},"type":"array"}},"required":["rates","fees","blocks","caps","change_orders","sla_credits"],"type":"object"}],"description":"The contract aggregate as one view: the agreement plus its rate card,\nfee schedule, prepaid blocks, and caps."},"ContractEscalator":{"description":"A scheduled escalator: the contract's per-period price uplifts by\n`uplift_bps` basis points each anniversary of the billing anchor\n(E). Pure math at evaluation — no state.","properties":{"contract_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"first_uplift_on":{"description":"First anniversary that applies (defaults to evaluation at the\nanchor itself when `None` — the anchor anniversary).","format":"date","type":["string","null"]},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"uplift_bps":{"description":"Uplift per anniversary, basis points (300 = +3.0%).","format":"int32","type":"integer"}},"required":["id","org_id","contract_id","uplift_bps","created_at"],"type":"object"},"ContractFixedFee":{"description":"Fixed-fee schedule entry: becomes a candidate line when `due_on` falls\ninside the evaluation window (and the effective window).","properties":{"amount_minor":{"format":"int64","type":"integer"},"contract_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"due_on":{"format":"date","type":"string"},"id":{"format":"uuid","type":"string"},"label":{"type":"string"},"notes":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"}},"required":["id","org_id","contract_id","label","amount_minor","due_on","created_at"],"type":"object"},"ContractListQuery":{"properties":{"client_id":{"format":"uuid","type":["string","null"]},"effective_on":{"description":"Only contracts effective on this date.","format":"date","type":["string","null"]},"status":{"description":"Exact status: `draft|active|ended|cancelled`.","type":["string","null"]}},"type":"object"},"ContractMutationResponse":{"allOf":[{"$ref":"#/components/schemas/ContractDetail"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): contract aggregate at the top\nlevel plus the additive `mutation` metadata."},"ContractRate":{"description":"T&M rate card row: the hourly rate for time entries whose `work_type`\nmatches `work_type`. Entries with no matching row bill at the\ncontract's default `hourly_rate_minor`.","properties":{"contract_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"rate_minor":{"description":"Hourly rate in minor units.","format":"int64","type":"integer"},"work_type":{"type":"string"}},"required":["id","org_id","contract_id","work_type","rate_minor","created_at"],"type":"object"},"ContractRemovedAck":{"description":"Acknowledgement for `DELETE /api/contracts/{id}`. Deliberately carries no\nrate, fee, cap, escalator or commitment: the delete is a commercial\naction, not a rate read (RB2), so the caller never receives money it did\nnot ask to see. `deleted` mirrors the vault/asset delete shape.","properties":{"contract_id":{"format":"uuid","type":"string"},"deleted":{"type":"boolean"},"number":{"type":"string"}},"required":["contract_id","number","deleted"],"type":"object"},"ContractSlaCredit":{"description":"An SLA service credit the admin or verification flow applies for a\nmissed SLA (E): becomes a NEGATIVE `sla_credit` line at the next\nrun window covering `applies_on`. Never silent — the record names\nthe ticket, the policy, and the missed target.","properties":{"amount_minor":{"description":"Credit amount, minor units — stored POSITIVE, applied as a\nnegative line (the ledger records magnitude; direction is the\nline kind's meaning).","format":"int64","type":"integer"},"applied_invoice_id":{"format":"uuid","type":["string","null"]},"applies_on":{"description":"The billing window the credit lands in (inclusive-day semantics\nat evaluation: `from <= applies_on <= to`).","format":"date","type":"string"},"contract_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"currency":{"type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"reason":{"description":"What was missed (\"resolution target breached by 2d 4h\").","type":"string"},"status":{"description":"`pending|applied|cancelled`.","type":"string"},"ticket_id":{"format":"uuid","type":["string","null"]},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","contract_id","reason","amount_minor","currency","applies_on","status","created_at","updated_at"],"type":"object"},"ContractStatus":{"description":"Lifecycle. `draft` contracts evaluate in previews (plan before\nactivation) but Wave 9's invoice runs will refuse them; `cancelled`\ncontracts never evaluate. `ended` keeps evaluating candidates whose\ndates fall inside the (closed) effective window.","enum":["draft","active","ended","cancelled"],"type":"string"},"ContractType":{"description":"The supported commercial agreement shapes. Wave 39 adds the MSP\nrelationship types beyond the original four: per-unit recurring\n(per-seat / per-device) and per-incident (per closed ticket) pricing.\nPrepaid blocks, NTE caps, minimums, and escalators ride the existing\ntypes as attached configuration rather than new shapes.","enum":["recurring","time_and_materials","retainer","fixed_fee","per_unit","per_incident"],"type":"string"},"ConvertQuoteRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"}],"description":"POST /api/quotes/{id}/convert-to-invoice body."},"ConvertQuoteResponse":{"description":"Convert-to-invoice response: the quote with its seam targets.","properties":{"contract":{"$ref":"#/components/schemas/Contract"},"invoice":{"$ref":"#/components/schemas/Invoice"},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"quote":{"$ref":"#/components/schemas/Quote"}},"required":["quote","contract","invoice","mutation"],"type":"object"},"CostLineKind":{"description":"One money line on a ticket: `parts`, `flat`, or `other`.","enum":["parts","flat","other"],"type":"string"},"CostLineMutationResponse":{"properties":{"line":{"$ref":"#/components/schemas/TicketCostLine"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["line","mutation"],"type":"object"},"CreateAlertSourceRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1) — idempotency key."},{"properties":{"board_id":{"description":"Board incident tickets land on.","format":"uuid","type":"string"},"client_id":{"description":"Optional client context stamped onto created incident tickets.","format":"uuid","type":["string","null"]},"correlation_window_secs":{"description":"Storm window in seconds (10..=86400; default 900).","format":"int32","type":["integer","null"]},"event_type":{"description":"When set, this source is a generic M2M webhook receiver that emits\ndomain-event envelopes onto the outbox stream instead of creating\nalert rows (Wave 43 lane 1). Leave NULL for the classic alert path.","type":["string","null"]},"name":{"type":"string"},"payload_schema_ref":{"description":"Payload contract pin (e.g. `\"acme-io/webhook-v1\"`); NULL defaults\nto `core-domain/v1`. Advisory — the ingest envelope always carries\nthe resolved schema ref.","type":["string","null"]},"source_ref":{"description":"Optional custom routing id (3–64 chars of `[a-z0-9-]`); generated\nwhen absent. Globally unique — collisions are a typed 409.","type":["string","null"]}},"required":["name","board_id"],"type":"object"}]},"CreateApiKeyRequest":{"properties":{"name":{"type":"string"},"scopes":{"description":"Capability scopes from [`KNOWN_SCOPES`].","items":{"type":"string"},"type":"array"}},"required":["name","scopes"],"type":"object"},"CreateAppointmentRequest":{"properties":{"client_id":{"format":"uuid","type":["string","null"]},"ends_at":{"format":"date-time","type":"string"},"kind":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AppointmentKind","description":"Defaults to on_site when omitted."}]},"notes":{"type":["string","null"]},"site_id":{"format":"uuid","type":["string","null"]},"starts_at":{"format":"date-time","type":"string"},"ticket_id":{"format":"uuid","type":["string","null"]},"title":{"type":"string"},"travel_minutes":{"description":"Dispatch placeholder, stored verbatim (RoutingProvider comes later).","format":"int32","type":"integer"},"user_id":{"description":"Assignee whose calendar holds the slot. Defaults to the caller;\nbooking for someone else requires owner/admin.","format":"uuid","type":["string","null"]}},"required":["title","starts_at","ends_at"],"type":"object"},"CreateArticleRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"body":{"type":["string","null"]},"category_id":{"format":"uuid","type":["string","null"]},"client_visible":{"description":"Portal v2 gate: effective client visibility additionally requires a\npublished status and a client-visible category (core_kb rule).","type":"boolean"},"content_jsonb":{"description":"Canonical rich content (ADR 0007): the versioned Tiptap-JSON\nenvelope `{\"schema_ver\": 1, \"doc\": <tiptap-doc>}`. Validated\nserver-side on write; when present it WINS over `body` (the\nplain-text projection is derived deterministically)."},"slug":{"description":"Stable per-org handle; derived from the title when omitted.","type":["string","null"]},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/KbArticleStatus"}]},"title":{"type":"string"}},"required":["title"],"type":"object"}]},"CreateAssetLinkRequest":{"properties":{"to_id":{"format":"uuid","type":"string"},"to_kind":{"description":"One of: document | password | client | ticket | kb_article | asset.","type":"string"}},"required":["to_kind","to_id"],"type":"object"},"CreateAssetRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first mutation parameters (IF1)."},{"properties":{"client_id":{"description":"Required for client-scoped assets; absent for internal-org ones.","format":"uuid","type":["string","null"]},"fields":{},"kind":{"description":"Free-text connector-compat column (typed classification rides\n`layout_id`).","type":"string"},"layout_id":{"description":"Typed layout; when set, `fields` is validated against its schema.","format":"uuid","type":["string","null"]},"lifecycle":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AssetLifecycleInput","description":"Inventory lifecycle inputs (rental tracking + depreciation,\nmigration 0061); all optional, absent = SQL default."}]},"name":{"type":"string"},"scope":{"description":"client | internal_org (migration 0103). Defaults to `client` when\na client_id is present — internal-org assets (the MSP's own gear)\ncarry no client, never a fake one.","type":["string","null"]},"site_id":{"format":"uuid","type":["string","null"]},"status":{"type":"string"},"tag":{"description":"Human asset tag, e.g. \"EXCH-01\".","type":"string"}},"required":["tag","name"],"type":"object"}]},"CreateAvailabilityRequest":{"properties":{"effective_from":{"type":["string","null"]},"effective_until":{"type":["string","null"]},"end_local":{"type":"string"},"start_local":{"description":"Local wall-clock \"HH:MM\" (window is `[start, end)`).","type":"string"},"timezone":{"description":"IANA timezone the wall-clock bounds live in.","type":"string"},"user_id":{"description":"Whose calendar. Defaults to the caller; another user requires\nowner/admin.","format":"uuid","type":["string","null"]},"weekday":{"description":"0 = Monday … 6 = Sunday.","format":"int32","type":"integer"}},"required":["weekday","start_local","end_local","timezone"],"type":"object"},"CreateBatchRequest":{"description":"POST /api/imports/batches — opens a committed-run span. Chunks then land\nthrough `/chunks` (synchronous) or `/stage` + `/run` (async, D2\nremainder) until the batch finishes.","properties":{"conflict_policy":{"$ref":"#/components/schemas/ConflictPolicy"},"entity_kind":{"$ref":"#/components/schemas/EntityKind"},"label":{"type":["string","null"]},"source_note":{"type":["string","null"]}},"required":["entity_kind","conflict_policy"],"type":"object"},"CreateBoardRequest":{"properties":{"description":{"type":["string","null"]},"is_default":{"type":"boolean"},"name":{"type":"string"}},"required":["name","is_default"],"type":"object"},"CreateCalendarRequest":{"properties":{"calendar":{"$ref":"#/components/schemas/BusinessCalendar","description":"Timezone/windows/holidays/maintenance document."},"name":{"type":"string"}},"required":["name","calendar"],"type":"object"},"CreateCatalogItemRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"category":{"type":["string","null"]},"cost_minor":{"format":"int64","type":"integer"},"currency":{"type":"string"},"description":{"type":["string","null"]},"group_name":{"type":["string","null"]},"name":{"type":"string"},"sku":{"type":["string","null"]},"taxable":{"type":"boolean"},"unit":{"description":"Unit of measure the price applies to (default `each`).","type":"string"},"unit_price_minor":{"format":"int64","type":"integer"}},"required":["name","currency"],"type":"object"}],"description":"POST /api/catalog-items body."},"CreateCategoryRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"client_visible":{"description":"Portal v2 gate: only categories with this flag can expose articles\nto client users.","type":"boolean"},"description":{"type":["string","null"]},"name":{"type":"string"},"position":{"format":"int32","type":["integer","null"]},"slug":{"description":"Stable per-org handle; derived from the name when omitted.","type":["string","null"]}},"required":["name"],"type":"object"}]},"CreateChangeOrderRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1). (Creates carry no version.)"},{"properties":{"amount_minor":{"format":"int64","type":"integer"},"description":{"type":["string","null"]},"number":{"description":"Org-unique per contract (\"CO-2026-004\").","type":"string"},"title":{"type":"string"}},"required":["number","title","amount_minor"],"type":"object"}]},"CreateCheckoutRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"due_at":{"description":"When the tools are due back (RFC 3339).","type":"string"},"from_location_id":{"format":"uuid","type":"string"},"holder_user_id":{"format":"uuid","type":"string"},"items":{"description":"Individual tools; an absent serial auto-picks the oldest in-stock\nunit at the checkout location.","items":{"$ref":"#/components/schemas/CheckoutItemRequest"},"type":"array"},"kit_id":{"description":"Set for a whole-kit (toolbox) checkout; `items` must then be empty.","format":"uuid","type":["string","null"]},"notes":{"type":["string","null"]}},"required":["holder_user_id","from_location_id","due_at"],"type":"object"}],"description":"POST /api/tool-checkouts."},"CreateClientRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key. Absent in\nlegacy clients — purely additive. (Creates carry no version.)"},{"properties":{"name":{"type":"string"},"notes":{"type":["string","null"]}},"required":["name"],"type":"object"}]},"CreateConnectionRequest":{"properties":{"direction":{"description":"`outbound` | `inbound` | `two_way`.","type":"string"},"feed_url":{"description":"Required for inbound/two_way: the external iCal URL to poll.","type":["string","null"]},"name":{"type":"string"},"provider":{"description":"Provider discriminator; 'ical' is the only one today.","type":"string"},"user_id":{"description":"Whose calendar. Defaults to the caller; another user requires\nowner/admin.","format":"uuid","type":["string","null"]}},"required":["name","direction"],"type":"object"},"CreateConsentRequest":{"description":"POST /api/org/sign-off-consents","properties":{"body":{"type":"string"}},"required":["body"],"type":"object"},"CreateContactRequest":{"properties":{"email":{"type":["string","null"]},"name":{"type":"string"},"phone":{"type":["string","null"]},"title":{"type":["string","null"]}},"required":["name"],"type":"object"},"CreateContractRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key. Absent\nin legacy clients — purely additive. (Creates carry no version.)"},{"properties":{"amount_minor":{"description":"Per-period price (recurring/retainer), minor units.","format":"int64","type":["integer","null"]},"billing_anchor":{"description":"Required for recurring/retainer; first billing period's start.","format":"date","type":["string","null"]},"billing_frequency":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BillingFrequency","description":"Required for recurring/retainer."}]},"blocks":{"description":"Prepaid hour banks (only valid on T&M and retainer contracts).","items":{"$ref":"#/components/schemas/BlockSpec"},"type":"array"},"caps":{"description":"NTE caps (only valid on time_and_materials contracts).","items":{"$ref":"#/components/schemas/CapSpec"},"type":"array"},"client_id":{"format":"uuid","type":"string"},"contract_type":{"$ref":"#/components/schemas/ContractType"},"currency":{"description":"ISO 4217 code (e.g. \"USD\"); every amount uses this currency.","type":"string"},"effective_from":{"format":"date","type":"string"},"effective_until":{"format":"date","type":["string","null"]},"escalator":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/EscalatorSpec","description":"Scheduled escalator (E); `None` = none."}]},"fees":{"description":"Fee schedule (only valid on fixed_fee contracts).","items":{"$ref":"#/components/schemas/FeeSpec"},"type":"array"},"hourly_rate_minor":{"description":"T&M default hourly rate, minor units.","format":"int64","type":["integer","null"]},"incident_price_minor":{"description":"Per-incident (D): fixed price per closed ticket.","format":"int64","type":["integer","null"]},"incident_priority":{"description":"Per-incident (D): `any` or `p1`..`p4`.","type":["string","null"]},"included_minutes":{"description":"Retainer included minutes per period.","format":"int32","type":["integer","null"]},"minimum_commitment_minor":{"description":"Minimum commitment per period (E); `None` = none.","format":"int64","type":["integer","null"]},"name":{"type":"string"},"notes":{"type":["string","null"]},"number":{"type":"string"},"overage_rate_minor":{"description":"Retainer hourly overage rate, minor units.","format":"int64","type":["integer","null"]},"po_number":{"description":"Client PO reference printed on produced invoices (F).","type":["string","null"]},"rates":{"description":"T&M rate card (only valid on time_and_materials contracts).","items":{"$ref":"#/components/schemas/RateSpec"},"type":"array"},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ContractStatus","description":"Defaults to `draft`."}]},"unit_basis":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/UnitBasis","description":"Per-unit recurring (C): `devices|users`."}]},"unit_price_minor":{"description":"Per-unit recurring (C): price per unit per period.","format":"int64","type":["integer","null"]}},"required":["client_id","number","name","contract_type","currency","effective_from"],"type":"object"}]},"CreateCostLineRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"billable":{"type":"boolean"},"currency":{"type":"string"},"description":{"type":"string"},"kind":{"$ref":"#/components/schemas/CostLineKind"},"quantity":{"format":"double","type":"number"},"unit_cost_minor":{"format":"int64","type":"integer"}},"required":["kind","description","unit_cost_minor","currency"],"type":"object"}]},"CreateDealRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"amount_minor":{"format":"int64","type":"integer"},"client_id":{"format":"uuid","type":"string"},"currency":{"type":"string"},"expected_close_on":{"format":"date","type":["string","null"]},"name":{"type":"string"},"notes":{"type":["string","null"]},"owner_user_id":{"format":"uuid","type":["string","null"]},"quote_id":{"description":"Optional originating quote (same client).","format":"uuid","type":["string","null"]},"stage_id":{"format":"uuid","type":"string"}},"required":["client_id","stage_id","name","currency"],"type":"object"}],"description":"POST /api/pipeline/deals body."},"CreateDirectoryGroupRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key. Creates carry\nno version."},{"properties":{"description":{"description":"Free-text purpose, up to 500 characters.","type":["string","null"]},"external_id":{"description":"The directory's own identifier for this group. Supply it when a sync\nowns the row, so the next pass updates rather than duplicates it.","type":["string","null"]},"name":{"description":"Display name, 1–64 characters, unique in the org.","type":"string"}},"required":["name"],"type":"object"}]},"CreateDocumentRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"body":{"type":["string","null"]},"change_summary":{"type":["string","null"]},"client_id":{"description":"NULL = org-global (docs-platform v1).","format":"uuid","type":["string","null"]},"fields":{},"layout_id":{"description":"Typed layout; when set, `fields` is validated against its schema.","format":"uuid","type":["string","null"]},"title":{"type":"string"}},"required":["title"],"type":"object"}]},"CreateEdgeLinkRequest":{"properties":{"email":{"description":"The identity's email, recorded for audit/display only.","type":"string"},"subject":{"description":"The edge assertion's `sub` (Cloudflare Access user id).","type":"string"},"user_id":{"description":"Local Marula user to bind. The admin is the verified-email\nauthority here: linking asserts \"this local account is this\nedge identity\".","format":"uuid","type":"string"}},"required":["user_id","subject","email"],"type":"object"},"CreateEmbedRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"fetch":{"description":"Fetch the provider title now; empty when the fetch is skipped.","type":"boolean"},"url":{"description":"Must resolve against the allowlist — a create with an unmatched\nURL is a typed 422, never a stored row.","type":"string"}},"required":["url"],"type":"object"}]},"CreateExclusionRequest":{"properties":{"ends_at":{"format":"date-time","type":"string"},"reason":{"type":["string","null"]},"starts_at":{"format":"date-time","type":"string"},"user_id":{"format":"uuid","type":["string","null"]}},"required":["starts_at","ends_at"],"type":"object"},"CreateExpenseRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"amount_minor":{"format":"int64","type":"integer"},"billable":{"type":"boolean"},"currency":{"type":"string"},"description":{"type":"string"}},"required":["description","amount_minor","currency"],"type":"object"}]},"CreateExternalDocumentRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"article_id":{"description":"Optional attachment to one KB article. When attached, the row\nrides that article's client-facing gate for portal reads.","format":"uuid","type":["string","null"]},"permission_note":{"description":"Who may open the link and what they need (e.g. \"M365 group\nACME-IT\"). Documentation of access, never enforcement.","type":"string"},"title":{"type":"string"},"url":{"description":"Absolute https URL; SharePoint/OneDrive/Drive-class hosts are\nclassified server-side — the client cannot lie about the kind.","type":"string"}},"required":["title","url"],"type":"object"}]},"CreateExternalLinkRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"carrier":{"type":["string","null"]},"kind":{"$ref":"#/components/schemas/ExternalLinkKind"},"label":{"type":["string","null"]},"line_id":{"description":"PO-line links only: the line inside the route's order.","format":"uuid","type":["string","null"]},"url":{"type":"string"}},"required":["kind","url"],"type":"object"}]},"CreateFieldDefRequest":{"properties":{"kind":{"$ref":"#/components/schemas/FieldKindWire"},"name":{"type":"string"},"options":{"items":{"type":"string"},"type":"array"},"required":{"type":"boolean"},"sort_order":{"format":"int32","type":"integer"},"validator_entry_key":{"description":"§4.6: registry key of a plugin field_validator entry\n(`plugin.<slug>.<id>`); stored in config and enforced on every\nvalue write (fail-closed when the entry disappears).","type":["string","null"]}},"required":["name","kind"],"type":"object"},"CreateGroupRequest":{"properties":{"description":{"type":["string","null"]},"name":{"type":"string"}},"required":["name"],"type":"object"},"CreateImportTemplateRequest":{"description":"POST /api/imports/templates request.","properties":{"entity_kind":{"$ref":"#/components/schemas/EntityKind"},"mapping":{"description":"Source column → canonical Marula column. Targets are validated\nagainst the canonical field catalog for the entity kind.","type":"object"},"name":{"description":"Operator-facing template name (unique per source system + kind).","type":"string"},"source_system":{"description":"Source-system label this template belongs to (e.g. \"connectwise\").","type":"string"}},"required":["name","source_system","entity_kind","mapping"],"type":"object"},"CreateInterfaceRequest":{"properties":{"ips":{"items":{"type":"string"},"type":"array"},"is_primary":{"type":"boolean"},"mac":{"description":"Normalized to lower colon form server-side.","type":["string","null"]},"name":{"type":["string","null"]},"vlans":{"items":{"type":"string"},"type":"array"}},"type":"object"},"CreateInvoiceRunRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key."},{"properties":{"client_id":{"description":"Restrict the run to one client's billable contracts.","format":"uuid","type":["string","null"]},"due_in_days":{"description":"Payment terms for the issued invoices, in days (default 30).","format":"int32","type":["integer","null"]},"from":{"description":"Window start (inclusive), ISO date.","format":"date","type":"string"},"to":{"description":"Window end (inclusive), ISO date.","format":"date","type":"string"}},"required":["from","to"],"type":"object"}]},"CreateKitRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"code":{"type":["string","null"]},"name":{"type":"string"},"note":{"type":["string","null"]}},"required":["name"],"type":"object"}],"description":"POST /api/tool-kits."},"CreateLabelTemplateRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"barcode_token":{"type":["string","null"]},"dpi":{"format":"int32","type":["integer","null"]},"fields":{"items":{"type":"string"},"type":"array"},"height_mm":{"format":"double","type":["number","null"]},"include_qr":{"type":"boolean"},"is_default":{"type":"boolean"},"name":{"type":"string"},"purpose":{"type":"string"},"size_preset":{"type":"string"},"width_mm":{"format":"double","type":["number","null"]}},"required":["name","purpose","size_preset"],"type":"object"}],"description":"Create body. `width_mm`/`height_mm` are required exactly when\n`size_preset = \"custom\"`; millimetres are rounded to a tenth."},"CreateLayoutRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"description":{"type":"string"},"fields":{"items":{"$ref":"#/components/schemas/LayoutField"},"type":"array"},"icon":{"type":"string"},"name":{"type":"string"},"slug":{"description":"Stable handle; derived from the name when absent.","type":["string","null"]}},"required":["name"],"type":"object"}]},"CreateLinkRequest":{"properties":{"to_id":{"format":"uuid","type":"string"},"to_kind":{"description":"One of: document | password | client | ticket | kb_article | asset.","type":"string"}},"required":["to_kind","to_id"],"type":"object"},"CreateOnCallRequest":{"properties":{"category":{"description":"Rotation label ('primary', 'secondary', 'escalation', …).","type":"string"},"ends_at":{"format":"date-time","type":"string"},"notes":{"type":["string","null"]},"starts_at":{"format":"date-time","type":"string"},"user_id":{"format":"uuid","type":["string","null"]}},"required":["starts_at","ends_at","category"],"type":"object"},"CreateOrderRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"currency":{"description":"Absent → the vendor's default currency.","type":["string","null"]},"expected_at":{"format":"date","type":["string","null"]},"lines":{"items":{"$ref":"#/components/schemas/OrderLineInput"},"type":"array"},"notes":{"type":["string","null"]},"number":{"description":"Absent → generated `PO-NNNNNN` from the org sequence.","type":["string","null"]},"project_id":{"format":"uuid","type":["string","null"]},"vendor_id":{"format":"uuid","type":"string"},"vendor_reference":{"type":["string","null"]}},"required":["vendor_id"],"type":"object"}]},"CreatePasswordRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"client_id":{"description":"NULL = org-level vault entry.","format":"uuid","type":["string","null"]},"expires_at":{"format":"date-time","type":["string","null"]},"notes":{"description":"Encrypted at rest; returned only through the reveal endpoint.","type":["string","null"]},"rotate_at":{"description":"Rotation schedule (Wave 19 lane 3): absent/null = never remide.","format":"date-time","type":["string","null"]},"secret":{"type":"string"},"title":{"type":"string"},"url":{"type":"string"},"username":{"type":"string"}},"required":["title","secret"],"type":"object"}]},"CreatePhaseRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive."},{"properties":{"budget_currency":{"type":["string","null"]},"budget_minor":{"description":"Wave 39 lane 4 (report C/D): the phase's billing budget and/or the\nfixed fee a COMPLETED non-milestone phase releases. Milestone\nphases keep using `milestone_amount_*`.","format":"int64","type":["integer","null"]},"description":{"type":["string","null"]},"end_date":{"format":"date","type":["string","null"]},"fee_currency":{"type":["string","null"]},"fee_minor":{"format":"int64","type":["integer","null"]},"is_milestone":{"type":"boolean"},"milestone_amount_minor":{"format":"int64","type":["integer","null"]},"milestone_currency":{"type":["string","null"]},"name":{"type":"string"},"sort_order":{"format":"int32","type":"integer"},"start_date":{"description":"The planned window (Wave 34 claims close-out, claim 6). Both ends\noptional: a phase created without dates lands in the ordered list,\nNOT on the Gantt, and the UI says which it is. Supplying only one\nend is rejected at the database CHECK, because a phase with a start\nand no length has no bar to draw.","format":"date","type":["string","null"]}},"required":["name"],"type":"object"}]},"CreatePickRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"note":{"type":["string","null"]},"qty_milli":{"format":"int64","type":["integer","null"]},"stock_item_id":{"format":"uuid","type":"string"},"ticket_id":{"format":"uuid","type":"string"}},"required":["ticket_id","stock_item_id"],"type":"object"}]},"CreatePortalUserRequest":{"properties":{"display_name":{"type":"string"},"email":{"type":"string"},"password":{"description":"Initial credential, hashed with argon2id before storage. The admin\ndelivers it to the contact out-of-band (v1 has no invite email).","type":"string"},"role":{"$ref":"#/components/schemas/PortalRole"}},"required":["email","display_name","password","role"],"type":"object"},"CreateProjectRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive."},{"properties":{"billing_contract_id":{"description":"The contract this project bills through (Wave 39 lane 4, report\nP2). Its rate card/blocks/caps price the project's T&M entries and\nits runs carry the project's released milestones. Validated to\nbelong to the same client.","format":"uuid","type":["string","null"]},"billing_method":{"$ref":"#/components/schemas/BillingMethod"},"budget_minor":{"format":"int64","type":["integer","null"]},"client_id":{"format":"uuid","type":["string","null"]},"currency":{"type":["string","null"]},"description":{"type":["string","null"]},"due_date":{"format":"date","type":["string","null"]},"name":{"type":"string"},"start_date":{"format":"date","type":["string","null"]}},"required":["name"],"type":"object"}]},"CreateProposalRequest":{"description":"POST /api/agent-proposals body.","properties":{"agent_ref":{"description":"Agent provenance (§5). Optional: the machine identity defaults to\nthe caller's (`api-key:<id>` / `user:<id>`).","type":["string","null"]},"before_value":{"description":"The value the agent saw when it drafted the change (the IF8\nbefore-snapshot). `null` for creates. Application re-reads the\nlive entity, so this is decision context, not a blind write.","type":["object","null"]},"capability":{"description":"Write capability this change belongs to (must be granted).","type":["string","null"]},"classification":{"$ref":"#/components/schemas/ProposalClassification"},"entity":{"type":"string"},"entity_id":{"format":"uuid","type":["string","null"]},"for_user":{"description":"The user the agent acted on behalf of.","format":"uuid","type":["string","null"]},"op":{"type":"string"},"proposed_value":{"type":"object"},"rationale":{"type":["string","null"]},"rule_set_hash":{"description":"Hash of the evaluated rule set at proposal time.","type":["string","null"]},"session_id":{"type":["string","null"]},"skill_slug":{"type":["string","null"]},"skill_version":{"format":"int32","type":["integer","null"]},"tool":{"type":["string","null"]}},"required":["entity","op","proposed_value"],"type":"object"},"CreateProposalResponse":{"description":"POST /api/agent-proposals response: the stored proposal.","properties":{"proposal":{"$ref":"#/components/schemas/AgentProposalView"}},"required":["proposal"],"type":"object"},"CreateProviderRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"enabled":{"type":"boolean"},"host_patterns":{"description":"Exact hosts this provider accepts (e.g. `[\"loom.com\"]`).","items":{"type":"string"},"type":"array"},"name":{"type":"string"},"oembed_endpoint":{"description":"oEmbed endpoint template; exactly one `{url}` placeholder, https.","type":"string"},"slug":{"description":"Stable per-org handle; derived from the name when omitted.","type":["string","null"]},"url_regex":{"description":"Regex over the full resource URL (Rust regex syntax).","type":"string"}},"required":["name","host_patterns","url_regex","oembed_endpoint"],"type":"object"}]},"CreatePushSessionRequest":{"description":"POST /api/migration/push/sessions body.","properties":{"conflict_policy":{"$ref":"#/components/schemas/ConflictPolicy"},"entity_kind":{"$ref":"#/components/schemas/EntityKind"},"label":{"description":"Operator-facing run label.","type":["string","null"]},"source_note":{"description":"Free-form note about the third-party source system.","type":["string","null"]}},"required":["entity_kind","conflict_policy"],"type":"object"},"CreateQuoteRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"client_id":{"format":"uuid","type":"string"},"currency":{"type":"string"},"issued_on":{"description":"Issuance date (defaults to today).","format":"date","type":["string","null"]},"lines":{"items":{"$ref":"#/components/schemas/QuoteLineInput"},"type":"array"},"notes":{"type":["string","null"]},"tax_rate_bps":{"description":"Flat tax rate over the TAXABLE subtotal, basis points (800 = 8%).","format":"int32","type":"integer"},"terms":{"type":["string","null"]},"valid_until":{"format":"date","type":"string"}},"required":["client_id","currency","valid_until","lines"],"type":"object"}],"description":"POST /api/quotes body."},"CreateReplySnippetRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"board_ids":{"items":{"format":"uuid","type":"string"},"type":"array"},"body":{"type":"string"},"name":{"type":"string"}},"required":["name","body"],"type":"object"}]},"CreateReportDefinitionRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"description":{"type":["string","null"]},"name":{"description":"Org-unique display name (1–200 chars after trimming).","type":"string"},"scope":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/DefinitionScope","description":"Requested visibility; `org` requires admin and non-admins are\nforced onto `self` regardless."}]},"spec":{"$ref":"#/components/schemas/ReportSpec","description":"The registry-validated report spec."}},"required":["name","spec"],"type":"object"}],"description":"POST /api/report-definitions body."},"CreateReportScheduleRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"cadence":{"$ref":"#/components/schemas/ScheduleCadence"},"day_of_month":{"description":"Monthly only: 1..=28.","format":"int32","type":["integer","null"]},"day_of_week":{"description":"Weekly only: 0 = Monday .. 6 = Sunday.","format":"int32","type":["integer","null"]},"enabled":{"type":["boolean","null"]},"name":{"description":"Org-unique display name (1–200 chars after trimming).","type":"string"},"recipients":{"description":"Recipient staff users of this org (1–50).","items":{"format":"uuid","type":"string"},"type":"array"},"report_definition_id":{"description":"The stored R3 definition this schedule delivers.","format":"uuid","type":"string"},"time_of_day":{"description":"`HH:MM` or `HH:MM:SS`, read in `timezone`.","type":"string"},"timezone":{"description":"IANA zone the wall clock reads in.","type":"string"},"window_days":{"description":"Trailing-day window per run (1..=366); default 30.","format":"int32","type":["integer","null"]}},"required":["name","report_definition_id","recipients","cadence","time_of_day","timezone"],"type":"object"}],"description":"POST /api/report-schedules body."},"CreateRuleRequest":{"properties":{"actions":{"description":"Action documents (see AutomationAction schema)."},"board_id":{"format":"uuid","type":["string","null"]},"conditions":{"description":"Condition documents (see AutomationCondition schema)."},"name":{"type":"string"},"subject_kind":{"description":"What the rule's subject is: `ticket` (default, historical) or\n`board` — a rule that fires on ticket-less envelopes (Wave 43\nlane 3).","type":["string","null"]},"trigger_event":{"description":"Domain event type, e.g. \"ticket.created\".","type":"string"}},"required":["name","trigger_event","conditions","actions"],"type":"object"},"CreateScheduleRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"$ref":"#/components/schemas/CadenceSpec"},{"properties":{"assignee_id":{"format":"uuid","type":["string","null"]},"board_id":{"format":"uuid","type":"string"},"body":{"type":["string","null"]},"client_id":{"format":"uuid","type":["string","null"]},"name":{"type":"string"},"priority":{"description":"\"p1\" | \"p2\" | \"p3\" | \"p4\" (defaults p3)","type":"string"},"site_id":{"format":"uuid","type":["string","null"]},"title":{"type":"string"}},"required":["name","board_id","title"],"type":"object"}]},"CreateSchedulingLinkRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"expires_at":{"description":"Absolute expiry (lifecycle signal); absent = stays valid until\nused or revoked.","format":"date-time","type":["string","null"]},"label":{"type":["string","null"]},"meeting_type_id":{"format":"uuid","type":"string"},"ticket_id":{"description":"Bookings made through this link land back on this ticket.","format":"uuid","type":["string","null"]}},"required":["meeting_type_id"],"type":"object"}]},"CreateSearchPinRequest":{"properties":{"entity_id":{"format":"uuid","type":"string"},"entity_kind":{"type":"string"},"query":{"description":"The query that produced the result (2..=256 chars — the same bounds\nGET /api/search enforces), snapshotted on the pin.","type":"string"}},"required":["query","entity_kind","entity_id"],"type":"object"},"CreateSecretFolderRequest":{"properties":{"client_id":{"description":"NULL = org scope.","format":"uuid","type":["string","null"]},"kind":{"$ref":"#/components/schemas/SecretFolderKind"},"label":{"type":"string"},"parent_id":{"format":"uuid","type":["string","null"]},"restricted":{"type":"boolean"},"slug":{"type":"string"}},"required":["slug","label","kind"],"type":"object"},"CreateSecretRecordRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"client_id":{"description":"NULL = org-scope record.","format":"uuid","type":["string","null"]},"expires_at":{"format":"date-time","type":["string","null"]},"fields":{"description":"Material fields — sealed with coordinate AAD before persistence.","items":{"$ref":"#/components/schemas/SecretFieldValue"},"type":"array"},"file_bytes":{"format":"int64","type":["integer","null"]},"file_sha256":{"type":["string","null"]},"folder_id":{"format":"uuid","type":"string"},"kind":{"$ref":"#/components/schemas/SecretRecordKind"},"object_key":{"description":"Document-kind ciphertext-file pointer + hash (object storage).","type":["string","null"]},"rotate_at":{"description":"Rotation schedule (Wave 19 lane 3): the worker sweep reminds once\npast this instant (at-most-once per schedule).","format":"date-time","type":["string","null"]},"title":{"type":"string"}},"required":["folder_id","kind","title"],"type":"object"}]},"CreateSecretShareLinkRequest":{"properties":{"expires_at":{"description":"RFC 3339 instant; the server typed-refuses dates in the past or\nabsurdly far out (> 90 days — a share link is a bounded window,\nnot a chronic credential).","format":"date-time","type":"string"},"label":{"description":"Optional provenance shown to link managers and the audit trail\n(\"kiosk roll-out window\", …). Secrets NEVER belong here — the\naudit stores detail, never material.","type":"string"}},"required":["expires_at"],"type":"object"},"CreateServiceAccountRequest":{"properties":{"cert_fingerprint":{"description":"Lowercase hex SHA-256 of the client certificate to pin.","type":"string"},"cert_subject":{"description":"Optional display-only subject DN.","type":["string","null"]},"expires_at":{"description":"Optional expiry; must be in the future when provided.","format":"date-time","type":["string","null"]},"name":{"type":"string"},"scopes":{"description":"Permission-grant keys (validated against the policy matrix).","items":{"type":"string"},"type":"array"}},"required":["name","cert_fingerprint","scopes"],"type":"object"},"CreateSignOffRequest":{"description":"POST /api/tickets/{id}/sign-offs","properties":{"captured_at":{"description":"Device-clock reading when the stroke finished. Recorded verbatim.","format":"date-time","type":"string"},"consent_id":{"description":"Optional explicit consent id — pins the exact wording row when the\npad fetched it before a concurrent revision landed.","format":"uuid","type":["string","null"]},"consent_version":{"description":"Consent wording version the signer saw. The server rejects an\nunknown version so a stale pad cannot capture against wording the\nsigner never read.","format":"int32","type":"integer"},"idempotency_key":{"description":"Client-minted replay anchor (offline queue). Enforced unique per org.","type":"string"},"image_base64":{"description":"Base64 PNG of the trimmed, ink-only mark.","type":"string"},"signer_kind":{"description":"Reserved for the portal path; rejected today.","type":["string","null"]},"signer_name":{"description":"Who signed, as they typed it on the pad.","type":"string"},"supersedes_sign_off_id":{"description":"Set only when this capture corrects an earlier sign-off.","format":"uuid","type":["string","null"]}},"required":["image_base64","signer_name","captured_at","consent_version","idempotency_key"],"type":"object"},"CreateSiteRequest":{"properties":{"address":{"type":["string","null"]},"city":{"type":["string","null"]},"country":{"type":["string","null"]},"latitude":{"format":"double","type":["number","null"]},"longitude":{"format":"double","type":["number","null"]},"name":{"type":"string"},"postal_code":{"type":["string","null"]},"region":{"type":["string","null"]},"timezone":{"type":["string","null"]}},"required":["name"],"type":"object"},"CreateSlaCreditRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1). (Creates carry no version.)"},{"properties":{"amount_minor":{"format":"int64","type":"integer"},"applies_on":{"format":"date","type":"string"},"currency":{"type":"string"},"reason":{"description":"What was missed — printed on the credit line verbatim (\"resolution\ntarget breached by 2d 4h\"). Required: a silent credit is exactly\nthe failure mode the ledger refuses.","type":"string"},"ticket_id":{"format":"uuid","type":["string","null"]}},"required":["reason","amount_minor","currency","applies_on"],"type":"object"}]},"CreateStageRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"name":{"type":"string"},"position":{"format":"int32","type":["integer","null"]},"stage_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/StageType","description":"`open|won|lost` (default `open`)."}]}},"required":["name"],"type":"object"}],"description":"POST /api/pipeline/stages body."},"CreateStatusRequest":{"properties":{"category":{"$ref":"#/components/schemas/TicketStatusCategory","description":"The semantic category this status binds to — behavior (SLA pauses,\nbilling, automations) reads the category, never the label (§5)."},"is_entry":{"type":"boolean"},"name":{"type":"string"},"sort_order":{"format":"int32","type":"integer"},"wip_limit":{"description":"Optional per-column WIP cap (0089). 1..=1000 — the DB CHECK is the\nbackstop; this route validates before the write.","format":"int32","type":["integer","null"]}},"required":["name","category","sort_order","is_entry"],"type":"object"},"CreateStockItemRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"catalog_item_id":{"description":"Soft catalog link when a catalog line exists; absent = free-form\npart (name/part_number carry the identity).","format":"uuid","type":["string","null"]},"currency":{"type":"string"},"is_tool":{"description":"Wave 32 lane 3: flag the item as a tool (serialized-only, eligible\nfor tool checkout + kit membership).","type":"boolean"},"kind":{"type":"string"},"last_unit_cost_minor":{"description":"Optional opening cost reference at create (display only; locked\ncosts land with receive legs).","format":"int64","type":["integer","null"]},"name":{"type":"string"},"part_number":{"type":["string","null"]},"sku":{"type":["string","null"]},"unit":{"type":"string"}},"required":["name","currency"],"type":"object"}],"description":"POST /api/stock-items."},"CreateStockLocationRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"assigned_user_id":{"description":"Whose van/truck (optional).","format":"uuid","type":["string","null"]},"code":{"type":["string","null"]},"kind":{"type":"string"},"name":{"type":"string"},"parent_id":{"format":"uuid","type":["string","null"]}},"required":["name","kind"],"type":"object"}],"description":"POST /api/stock-locations."},"CreateTagRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"color":{"type":["string","null"]},"name":{"type":"string"}},"required":["name"],"type":"object"}]},"CreateTaskRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive."},{"properties":{"assignee_id":{"format":"uuid","type":["string","null"]},"depends_on":{"description":"Finish-to-start dependencies to create with the task; every id must\nbe a task of the SAME project. Cycles are impossible at creation\n(nothing can depend on the not-yet-existing task) but membership is\nvalidated.","items":{"format":"uuid","type":"string"},"type":"array"},"description":{"type":["string","null"]},"due_at":{"format":"date-time","type":["string","null"]},"is_milestone":{"type":"boolean"},"milestone_amount_minor":{"format":"int64","type":["integer","null"]},"milestone_currency":{"type":["string","null"]},"phase_id":{"format":"uuid","type":["string","null"]},"sort_order":{"format":"int32","type":"integer"},"title":{"type":"string"}},"required":["title"],"type":"object"}]},"CreateTaxonomyNodeRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key. Fresh\nentities have no version to match."},{"properties":{"description":{"type":["string","null"]},"name":{"type":"string"},"parent_id":{"description":"Subcategory parent (categories only): must be a ROOT category of\nthis org — the tree is exactly two levels deep.","format":"uuid","type":["string","null"]},"sort_order":{"format":"int32","type":"integer"}},"required":["name"],"type":"object"}]},"CreateTemplateRequest":{"properties":{"description":{"type":["string","null"]},"name":{"type":"string"},"spec":{"description":"Versioned template spec (core-projects `template`, spec_version 1).\nValidated eagerly so broken blueprints never persist."}},"required":["name","spec"],"type":"object"},"CreateTickRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"date":{"format":"date","type":"string"},"line_key":{"type":"string"},"note":{"type":["string","null"]},"ticket_id":{"format":"uuid","type":["string","null"]}},"required":["date","line_key"],"type":"object"}]},"CreateTicketLinkRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"link_type":{"$ref":"#/components/schemas/TicketLinkType"},"to_ticket_id":{"format":"uuid","type":"string"}},"required":["to_ticket_id","link_type"],"type":"object"}]},"CreateTicketRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive."},{"properties":{"assignee_id":{"format":"uuid","type":["string","null"]},"board_id":{"format":"uuid","type":"string"},"body":{"type":["string","null"]},"client_id":{"format":"uuid","type":["string","null"]},"contact_id":{"format":"uuid","type":["string","null"]},"custom_fields":{"description":"Initial custom-field values; each is validated against its\ndefinition. REQUIRED definitions must all be present (the private build tracker 2.17).","items":{"$ref":"#/components/schemas/TicketFieldInput"},"type":"array"},"due_at":{"format":"date-time","type":["string","null"]},"priority":{"$ref":"#/components/schemas/Priority"},"site_id":{"format":"uuid","type":["string","null"]},"title":{"type":"string"}},"required":["board_id","title","priority"],"type":"object"}]},"CreateTimeEntryRequest":{"properties":{"billable":{"type":"boolean"},"ended_at":{"format":"date-time","type":"string"},"notes":{"type":["string","null"]},"phase_id":{"description":"Project-scoped variant (exactly one of ticket_id / phase_id).","format":"uuid","type":["string","null"]},"started_at":{"format":"date-time","type":"string"},"ticket_id":{"description":"The ticket this entry bills through. `None` ONLY for project-scoped\nentries (Wave 39 lane 4, report P5): supply `phase_id` instead and\nthe entry bills through the phase project's linked contract.","format":"uuid","type":["string","null"]},"unbillable_reason":{"description":"Typed unbillable reason — required when `billable` is false (V3).","type":["string","null"]},"work_type":{"description":"Category slug from the org's time taxonomy (Wave 39 V5).","type":["string","null"]}},"required":["started_at","ended_at","billable"],"type":"object"},"CreateTravelDraftRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"appointment_id":{"description":"Wave 32 lane 7: the planned appointment this trip reconciles\nagainst (optional — ad-hoc travel against a ticket stays legal).","format":"uuid","type":["string","null"]},"billable":{"type":"boolean"},"distance":{"format":"double","type":"number"},"distance_unit":{"description":"`km` | `mi`.","type":"string"},"ended_at":{"format":"date-time","type":"string"},"notes":{"type":["string","null"]},"started_at":{"format":"date-time","type":"string"},"ticket_id":{"format":"uuid","type":"string"},"user_id":{"description":"Whose work this is; defaults to the caller. Only owner/admin may\nfile on someone else's behalf.","format":"uuid","type":["string","null"]},"vehicle":{"type":"string"}},"required":["ticket_id","started_at","ended_at","distance","distance_unit","vehicle"],"type":"object"}],"description":"POST /api/dispatch/travel-drafts body."},"CreateTravelExpenseRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"amount_minor":{"description":"Overrides the computed reimbursement amount when present.","format":"int64","type":["integer","null"]},"description":{"description":"Overrides the description when present.","type":["string","null"]}},"type":"object"}]},"CreateVendorRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"account_number":{"type":["string","null"]},"code":{"type":["string","null"]},"contacts":{"description":"Initial contact list (replace-on-write semantics).","items":{"$ref":"#/components/schemas/VendorContactInput"},"type":"array"},"default_currency":{"type":["string","null"]},"email":{"type":["string","null"]},"name":{"type":"string"},"notes":{"type":["string","null"]},"phone":{"type":["string","null"]},"website":{"type":["string","null"]}},"required":["name"],"type":"object"}]},"CreateViewRequest":{"properties":{"is_default":{"type":"boolean"},"name":{"type":"string"},"project_id":{"description":"`None` = org-wide view (spans every project).","format":"uuid","type":["string","null"]},"spec":{"description":"Versioned, additive-only view spec (filter/group/sort). Consumers\ntolerate unknown fields."}},"required":["name"],"type":"object"},"CreateWebhookRequest":{"properties":{"event_types":{"description":"Exact event types to subscribe; empty subscribes to everything.","items":{"type":"string"},"type":"array"},"url":{"description":"HTTPS endpoint receiving `POST`s. (http:// allowed for local dev.)","type":"string"}},"required":["url"],"type":"object"},"CreateWorkflowDocumentRequest":{"properties":{"definition":{"description":"The workflow document (see WorkflowDocument schema); parsed and\nvalidated before storage."},"name":{"type":"string"},"subject_kind":{"description":"`\"ticket\"` or `\"board\"`; omitted means `\"ticket\"` (the simple tier's\ndefault). A board-subject document runs against events with no ticket.","type":["string","null"]},"trigger_event":{"description":"Domain event type the document is triggered by, e.g. \"ticket.created\".","type":"string"}},"required":["name","trigger_event","definition"],"type":"object"},"CreatedLink":{"description":"The create response. `token` is the RAW shareable token — shown ONCE,\nnever stored server-side (only its SHA-256 hash).","properties":{"booking_path":{"description":"Relative booking path clients render against the public origin.","type":"string"},"link":{"$ref":"#/components/schemas/SchedulingLink"},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"token":{"type":"string"}},"required":["link","token","booking_path","mutation"],"type":"object"},"CredentialBindingInput":{"description":"One binding an operator asks for, in the replace-set body of\n`PATCH /api/plugins/installed/{id}`.","properties":{"header_name":{"description":"The header the value is injected as, e.g. `x-api-key`. Lowercased\nbefore it is stored.","type":"string"},"profile":{"description":"A key of the pinned manifest's `connector_profile.outbound_scopes`.","type":"string"},"secret_record_id":{"description":"The vault record whose `secret` field is injected. The record is named\nby id here because that is the only handle a screen has; the id is\nresolved through the caller's org, so an id from another tenant is a\n`404` rather than a binding.","format":"uuid","type":"string"}},"required":["profile","secret_record_id","header_name"],"type":"object"},"CredentialBindingView":{"description":"What the consent screen shows for one binding: the profile, the header, and\nthe vault record's NAME. Never a field, a value, ciphertext or a key.","properties":{"bound_at":{"format":"date-time","type":"string"},"bound_by":{"format":"uuid","type":["string","null"]},"header_name":{"type":"string"},"profile":{"type":"string"},"record_title":{"description":"The vault record's title. This is the whole of what the screen may say\nabout the record's contents.","type":"string"},"secret_record_id":{"format":"uuid","type":"string"}},"required":["profile","header_name","secret_record_id","record_title","bound_at"],"type":"object"},"CredentialView":{"properties":{"configured":{"type":"boolean"},"updated_at":{"type":["string","null"]}},"required":["configured"],"type":"object"},"CredentialsView":{"properties":{"postmark":{"$ref":"#/components/schemas/CredentialView"},"ses":{"$ref":"#/components/schemas/CredentialView"},"smtp":{"$ref":"#/components/schemas/CredentialView"}},"required":["postmark","smtp","ses"],"type":"object"},"CreditNote":{"description":"The credit document. `issued_against_invoice_id` is the issuing\ncontext (may be `None` for a free-standing client credit);\napplications may target any issued invoice of the same client and\ncurrency.","properties":{"applied_minor":{"description":"Sum moved onto invoices, minor units.","format":"int64","type":"integer"},"client_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"currency":{"description":"ISO 4217 code; every amount on the credit uses it.","type":"string"},"id":{"format":"uuid","type":"string"},"issued_against_invoice_id":{"format":"uuid","type":["string","null"]},"number":{"description":"Human credit-note number, unique per org (e.g. \"CRN-000012\").","type":"string"},"org_id":{"format":"uuid","type":"string"},"reason":{"type":["string","null"]},"refunded_minor":{"description":"Sum refunded back out, minor units.","format":"int64","type":"integer"},"status":{"$ref":"#/components/schemas/CreditNoteStatus"},"total_minor":{"description":"Credit face value, minor units (> 0).","format":"int64","type":"integer"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","number","client_id","status","currency","total_minor","applied_minor","refunded_minor","created_at","updated_at"],"type":"object"},"CreditNoteAllocationResponse":{"allOf":[{"$ref":"#/components/schemas/CreditNote"},{"properties":{"invoice":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Invoice"}]},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"payment":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/InvoicePayment"}]}},"required":["mutation"],"type":"object"}],"description":"Apply/refund response: the credit plus the affected invoice (and the\nreceipt for applies) so the client reconciles both sides in one shot."},"CreditNoteApplication":{"description":"One allocation of credit-note value onto an invoice. The money move\nitself is the paired invoice payment receipt (provider `credit_note`);\nthis row is the credit-side ledger.","properties":{"amount_minor":{"format":"int64","type":"integer"},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"credit_note_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"invoice_id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"payment_id":{"format":"uuid","type":["string","null"]}},"required":["id","org_id","credit_note_id","invoice_id","amount_minor","created_at"],"type":"object"},"CreditNoteDetail":{"allOf":[{"$ref":"#/components/schemas/CreditNote"},{"properties":{"applications":{"items":{"$ref":"#/components/schemas/CreditNoteApplication"},"type":"array"},"remaining_minor":{"description":"Still-available credit, minor units.","format":"int64","type":"integer"}},"required":["remaining_minor","applications"],"type":"object"}],"description":"The credit document plus its allocation ledger (GET /{id})."},"CreditNoteListQuery":{"description":"GET /api/credit-notes query.","properties":{"client_id":{"format":"uuid","type":["string","null"]},"status":{"description":"Exact status: `issued` | `void`.","type":["string","null"]}},"type":"object"},"CreditNoteMutationResponse":{"allOf":[{"$ref":"#/components/schemas/CreditNote"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2) for credit-note mutations."},"CreditNoteStatus":{"description":"Document lifecycle of a credit note. `applied` / `refunded` /\n`partially applied` are DERIVED from the counters, never stored —\nallocation moves continuously; only a void is a state.","enum":["issued","void"],"type":"string"},"CsatSection":{"description":"CSAT inputs slot (Wave 34 lane 2): `available` is the honest feature\ngate — true only once the org has real survey data, never fabricated\nfrom a zero-window. Scores are 1-5; \"low\" means score ≤ 2.","properties":{"available":{"type":"boolean"},"avg_score":{"description":"Mean of recorded scores in the window; `None` with no responses.","format":"double","type":["number","null"]},"low_scores":{"description":"Responses at score ≤ 2 in the window — the automation trigger zone.","format":"int64","type":"integer"},"responses":{"format":"int64","type":"integer"}},"required":["available","responses","low_scores"],"type":"object"},"CurateFieldsRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first mutation parameters (IF1)."},{"properties":{"fields":{"description":"Per-field human values; a `null` value un-curates the field."}},"required":["fields"],"type":"object"}]},"CustomDomainInstructions":{"description":"Manual DNS setup instructions (the seam's none-mode), ready to render.","properties":{"domain":{"type":"string"},"edge_record":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/DnsRecordInstruction"}]},"verification_note":{"type":"string"},"verification_txt":{"$ref":"#/components/schemas/DnsRecordInstruction"}},"required":["domain","verification_txt","verification_note"],"type":"object"},"CustomDomainView":{"description":"GET response: current state + verification status + whether a DNS-provider\nplugin is installed (A8 (F)).","properties":{"domain":{"description":"The stored domain, or `null` when none is set.","type":["string","null"]},"instructions":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CustomDomainInstructions","description":"Manual paste instructions for the current domain (`null` when none)."}]},"org_id":{"format":"uuid","type":"string"},"provider_plugin_installed":{"description":"Whether an enabled plugin install in this org holds the\n`org_custom_domain.manage` capability — a DNS-provider plugin that can\nautomate the records. Always informational: the manual path needs none.","type":"boolean"},"status":{"description":"`none` (no domain) | `pending` (set, unverified) | `verified`.","example":"pending","type":"string"},"verification_token":{"description":"The TXT challenge for the current domain (`null` when none is set).","type":["string","null"]},"verified_at":{"description":"When Core last verified the TXT token, or `null`.","format":"date-time","type":["string","null"]}},"required":["org_id","status","provider_plugin_installed"],"type":"object"},"CustomizationManifest":{"description":"The customization manifest envelope, `manifest_version` 1.","properties":{"generated_at":{"description":"Dump instant (excluded from determinism comparisons).","format":"date-time","type":"string"},"manifest_version":{"description":"Envelope schema version. Bump ONLY for breaking section reshapes.","format":"int32","minimum":0,"type":"integer"},"org_id":{"description":"The org this manifest documents.","format":"uuid","type":"string"},"sections":{"$ref":"#/components/schemas/ManifestSections","description":"The customization sections. Always fully serialized."}},"required":["manifest_version","org_id","generated_at","sections"],"type":"object"},"DayCapacity":{"description":"One technician's capacity for one day of the horizon.","properties":{"available_minutes":{"description":"Availability coverage minus exclusions for the UTC day.","format":"int64","type":"integer"},"booked_minutes":{"description":"Live booking minutes clipped to the day.","format":"int64","type":"integer"},"date":{"format":"date","type":"string"},"free_minutes":{"description":"What a NEW booking can still consume: availability minus\nexclusions minus existing bookings.","format":"int64","type":"integer"},"utilization_percent":{"description":"0–100+, where >100 is over-capacity.","format":"int64","type":"integer"}},"required":["date","available_minutes","booked_minutes","free_minutes","utilization_percent"],"type":"object"},"DayPackAvailabilityView":{"properties":{"available_units":{"format":"int64","type":"integer"},"holders":{"items":{"$ref":"#/components/schemas/DayPackHolderView"},"type":"array"},"next_due_at":{"format":"date-time","type":["string","null"]},"required_units":{"description":"Tools count whole units; consumables carry milli-units.","format":"int64","type":"integer"},"status":{"description":"`available` | `partial` | `unavailable` | `not_configured`.","type":"string"}},"required":["status","required_units","available_units"],"type":"object"},"DayPackCounts":{"properties":{"collected":{"format":"int64","type":"integer"},"lines":{"format":"int64","type":"integer"},"stops":{"format":"int64","type":"integer"},"unavailable":{"description":"Lines whose availability is `unavailable` or `not_configured`.","format":"int64","type":"integer"}},"required":["stops","lines","collected","unavailable"],"type":"object"},"DayPackHolderView":{"properties":{"client_name":{"type":["string","null"]},"display_name":{"type":["string","null"]},"due_at":{"format":"date-time","type":["string","null"]},"expected_return_at":{"format":"date","type":["string","null"]},"kind":{"description":"`checkout` | `loan` | `out`.","type":"string"},"overdue":{"type":"boolean"},"user_id":{"format":"uuid","type":["string","null"]}},"required":["kind","overdue"],"type":"object"},"DayPackKitView":{"properties":{"appointment_id":{"format":"uuid","type":["string","null"]},"available_units":{"format":"int64","type":"integer"},"code":{"type":["string","null"]},"kit_id":{"format":"uuid","type":"string"},"line_key":{"type":"string"},"member_count":{"format":"int64","type":"integer"},"missing":{"items":{"type":"string"},"type":"array"},"name":{"type":"string"},"ready":{"type":"boolean"},"required_units":{"format":"int64","type":"integer"},"tick":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/DayPackTickView"}]},"ticket_id":{"format":"uuid","type":["string","null"]}},"required":["line_key","kit_id","name","member_count","ready","required_units","available_units"],"type":"object"},"DayPackLineView":{"properties":{"appointment_id":{"format":"uuid","type":["string","null"]},"availability":{"$ref":"#/components/schemas/DayPackAvailabilityView"},"is_tool":{"type":"boolean"},"item_name":{"type":"string"},"line_key":{"description":"Stable identity for ticks: `pick:<id>` / `rule:<id>:<ticket>` /\n`checklist:<id>`.","type":"string"},"note":{"type":["string","null"]},"provenance":{"description":"`pick` | `rule` | `checklist`.","type":"string"},"qty_milli":{"format":"int64","type":"integer"},"secondary":{"type":["string","null"]},"source":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/DayPackSourceView"}]},"stock_item_id":{"format":"uuid","type":"string"},"tick":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/DayPackTickView"}]},"ticket_id":{"format":"uuid","type":"string"},"unit":{"type":"string"}},"required":["line_key","provenance","ticket_id","stock_item_id","item_name","unit","is_tool","qty_milli","availability"],"type":"object"},"DayPackQuery":{"properties":{"date":{"description":"The day to assemble; defaults to today (UTC). Callers with a local\ntimezone pass their own date so the panel agrees with the wall clock.","format":"date","type":["string","null"]}},"type":"object"},"DayPackReminderView":{"properties":{"text":{"type":"string"},"ticket_id":{"format":"uuid","type":"string"}},"required":["ticket_id","text"],"type":"object"},"DayPackSourceView":{"properties":{"available_units":{"format":"int64","type":"integer"},"is_tech_vehicle":{"description":"True when this is the tech's own van/truck: gear already aboard.","type":"boolean"},"kind":{"type":"string"},"location_id":{"format":"uuid","type":"string"},"name":{"type":"string"}},"required":["location_id","name","kind","available_units","is_tech_vehicle"],"type":"object"},"DayPackStopView":{"properties":{"appointment_id":{"format":"uuid","type":"string"},"client_name":{"type":["string","null"]},"ends_at":{"format":"date-time","type":"string"},"kind":{"type":"string"},"site_label":{"type":["string","null"]},"starts_at":{"format":"date-time","type":"string"},"ticket_id":{"format":"uuid","type":["string","null"]},"ticket_number":{"type":["string","null"]},"title":{"type":"string"},"travel_from_kind":{"type":["string","null"]},"travel_minutes":{"format":"int64","type":"integer"},"travel_quality":{"type":["string","null"]}},"required":["appointment_id","title","starts_at","ends_at","kind","travel_minutes"],"type":"object"},"DayPackTickView":{"properties":{"line_key":{"type":"string"},"note":{"type":["string","null"]},"ticked_at":{"format":"date-time","type":"string"}},"required":["line_key","ticked_at"],"type":"object"},"DayPackTomorrowView":{"properties":{"date":{"format":"date","type":"string"},"kits":{"items":{"$ref":"#/components/schemas/DayPackKitView"},"type":"array"},"lines":{"items":{"$ref":"#/components/schemas/DayPackLineView"},"type":"array"},"reason":{"description":"`direct_to_site` | `start_point_unknown` when the list shows;\n`office_start` / `no_stops` never reach this view.","type":"string"},"reminders":{"items":{"$ref":"#/components/schemas/DayPackReminderView"},"type":"array"},"start_point_kind":{"type":["string","null"]},"start_point_label":{"type":["string","null"]},"stops":{"items":{"$ref":"#/components/schemas/DayPackStopView"},"type":"array"}},"required":["date","reason","stops","lines","kits"],"type":"object"},"DayPackTripView":{"properties":{"coverage_warnings":{"description":"Typed sentences: a ticked quantity that cannot cover the remaining\nstops of this trip.","items":{"type":"string"},"type":"array"},"index":{"format":"int32","type":"integer"},"kits":{"items":{"$ref":"#/components/schemas/DayPackKitView"},"type":"array"},"lines":{"items":{"$ref":"#/components/schemas/DayPackLineView"},"type":"array"},"opens_at":{"format":"date-time","type":"string"},"opens_at_office":{"description":"The trip opens where the tech can re-collect: the office when the day\nstarts there or a logged office return precedes it.","type":"boolean"},"reminders":{"items":{"$ref":"#/components/schemas/DayPackReminderView"},"type":"array"},"stops":{"items":{"$ref":"#/components/schemas/DayPackStopView"},"type":"array"}},"required":["index","opens_at","opens_at_office","stops","lines","kits"],"type":"object"},"DayPackView":{"properties":{"counts":{"$ref":"#/components/schemas/DayPackCounts"},"date":{"format":"date","type":"string"},"display_name":{"type":"string"},"has_stops":{"type":"boolean"},"start_point":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/StartPointView"}]},"timezone":{"type":"string"},"tomorrow":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/DayPackTomorrowView"}]},"trips":{"items":{"$ref":"#/components/schemas/DayPackTripView"},"type":"array"},"user_id":{"format":"uuid","type":"string"}},"required":["date","user_id","display_name","timezone","has_stops","trips","counts"],"type":"object"},"DayWeekQuery":{"properties":{"date":{"description":"Local date in the TARGET user's timezone. Day view: the day itself.\nWeek view: any date inside the week (Monday-start). Defaults to\ntoday in that timezone.","type":["string","null"]},"user_id":{"description":"Whose calendar. Defaults to the caller; another user requires\nowner/admin.","format":"uuid","type":["string","null"]}},"type":"object"},"DeadWorkPage":{"description":"One page of the list.","properties":{"items":{"description":"Newest abandonment first.","items":{"$ref":"#/components/schemas/DeadWorkView"},"type":"array"},"next_offset":{"description":"Offset for the next page; absent when this page is the last.","format":"int64","type":["integer","null"]}},"required":["items"],"type":"object"},"DeadWorkView":{"description":"One dead-work row as the operator surface reads it.","properties":{"attempts":{"format":"int32","type":"integer"},"cancelled_at":{"description":"Set when an operator has already cancelled it and the caller asked for\ncancelled rows.","format":"date-time","type":["string","null"]},"dead_since":{"description":"When it was abandoned, not when it was enqueued.","format":"date-time","type":"string"},"id":{"description":"The row id in that ledger; the argument the verbs take.","format":"uuid","type":"string"},"last_error":{"type":["string","null"]},"replayable":{"description":"Whether `POST .../redrive` can requeue it. False for a dead stream\nevent, whose payload needs the relay's stream writer.","type":"boolean"},"source":{"description":"`job` | `email` | `webhook` | `event` — the ledger it came from.","type":"string"},"subject":{"description":"What a human calls it: a job kind, an email subject and recipients, a\nwebhook URL and event type, a consumer group and event type.","type":"string"}},"required":["source","id","subject","attempts","dead_since","replayable"],"type":"object"},"DealDetail":{"allOf":[{"$ref":"#/components/schemas/PipelineDeal"},{"properties":{"transitions":{"items":{"$ref":"#/components/schemas/PipelineDealTransition"},"type":"array"}},"required":["transitions"],"type":"object"}],"description":"The deal aggregate: document + transition ledger."},"DealListQuery":{"description":"GET /api/pipeline/deals query.","properties":{"client_id":{"format":"uuid","type":["string","null"]},"owner_user_id":{"format":"uuid","type":["string","null"]},"quote_id":{"format":"uuid","type":["string","null"]},"stage_id":{"format":"uuid","type":["string","null"]},"status":{"description":"Exact status: `open|won|lost`.","type":["string","null"]}},"type":"object"},"DealMutationResponse":{"allOf":[{"$ref":"#/components/schemas/PipelineDeal"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}]},"DealStatus":{"description":"A deal's posture, mirroring its stage's kind. Kept as a distinct enum\nso consumers filter deals without resolving the stage row.","enum":["open","won","lost"],"type":"string"},"DecideAppointmentRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + If-Match\n`version` on the request."},{"properties":{"decision_note":{"type":["string","null"]}},"type":"object"}],"description":"A staff decision body. `decision_note` is the (optional) reason shown\nto the client on decline and a confirmation note on approve."},"DecideHeldRequest":{"properties":{"decision":{"description":"`accept` threads a client-visible comment on the ticket; `refuse`\nleaves the message off every ticket.","type":"string"},"reason":{"description":"Recorded with the decision in place of the hold reason.","type":["string","null"]},"ticket_id":{"description":"Required for `accept` unless routing already resolved a ticket.","format":"uuid","type":["string","null"]}},"required":["decision"],"type":"object"},"DecideHeldResponse":{"properties":{"comment_id":{"format":"uuid","type":["string","null"]},"decision":{"description":"`accepted` | `refused`.","type":"string"},"message_id":{"format":"uuid","type":"string"},"ticket_id":{"format":"uuid","type":["string","null"]}},"required":["message_id","decision"],"type":"object"},"DecideProposalRequest":{"properties":{"note":{"type":["string","null"]}},"type":"object"},"DecideResponse":{"description":"Instant-first mutation response (IF2) for a decision. `appointment`\nis present ONLY on approve: the calendar booking this decision made.","properties":{"appointment":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Appointment"}]},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"request":{"$ref":"#/components/schemas/AppointmentRequest"}},"required":["request","mutation"],"type":"object"},"DecisionConfigMutationResponse":{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"surface":{"$ref":"#/components/schemas/DecisionSurfaceOverview"}},"required":["surface","mutation"],"type":"object"},"DecisionFeatureMutationResponse":{"properties":{"features_enabled":{"type":"boolean"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["features_enabled","mutation"],"type":"object"},"DecisionOverviewResponse":{"properties":{"features_enabled":{"type":"boolean"},"surfaces":{"items":{"$ref":"#/components/schemas/DecisionSurfaceOverview"},"type":"array"}},"required":["features_enabled","surfaces"],"type":"object"},"DecisionPreviewRequest":{"properties":{"entity_id":{"format":"uuid","type":"string"},"entity_type":{"description":"`ticket` | `alert` — the REAL org entity to compose from.","type":"string"},"fire":{"description":"When true, one shadow decision run rides the REAL edge\n(admission, shadow answer store, ledger, caps, typed quiet).","type":["boolean","null"]}},"required":["entity_type","entity_id"],"type":"object"},"DecisionRunAnswer":{"properties":{"above_threshold":{"description":"True when this answer cleared the question's own `act` threshold.","type":"boolean"},"confidence":{"format":"double","type":["number","null"]},"kind":{"type":"string"},"label":{"description":"The label the model would have used, in words. Never an index.","type":"string"},"question_id":{"type":"string"}},"required":["question_id","kind","label","above_threshold"],"type":"object"},"DecisionRunSummary":{"description":"The four figures the run log leads with.","properties":{"above_threshold":{"description":"Runs with at least one answer above its `act` threshold.","format":"int64","type":"integer"},"agreed":{"description":"Runs whose proposal matches the status the ticket actually reached.","format":"int64","type":"integer"},"comparable":{"description":"Runs that could be compared at all (a ticket that still exists).","format":"int64","type":"integer"},"has_evidence":{"description":"False when the product has never recorded a run for this surface, so\nthe page can say \"no evidence yet\" rather than showing zeros.","type":"boolean"},"lowest_threshold":{"description":"The loosest `act` threshold in force. A surface that mixes question\nkinds has one line PER QUESTION (ADR 0010 §5), so the pair is the\nhonest headline: a surface whose questions share one line reports both\nas the same number.","format":"double","type":"number"},"runs":{"description":"Runs recorded in the window.","format":"int64","type":"integer"},"threshold":{"description":"The strictest `act` threshold in force, so the log and the drawer\nagree on the number that gates the most.","format":"double","type":"number"},"threshold_questions":{"description":"How many questions carry a line, so the figure is never read as \"the\"\nline when there are several.","minimum":0,"type":"integer"}},"required":["runs","above_threshold","agreed","comparable","threshold","lowest_threshold","threshold_questions","has_evidence"],"type":"object"},"DecisionRunView":{"description":"One answered run, in the words the run log draws.","properties":{"above_threshold":{"description":"True when any answer cleared the surface's `act` threshold.","type":"boolean"},"agreed":{"description":"True when a picked label matches the status the ticket actually\nreached. False when the run disagreed, None when the ticket moved on\nbefore its status could be compared.","type":["boolean","null"]},"answers":{"description":"What it would have said: the picked label per question, with the\nconfidence that decided it.","items":{"$ref":"#/components/schemas/DecisionRunAnswer"},"type":"array"},"created_at":{"format":"date-time","type":"string"},"current_status":{"description":"The status the ticket sits in now — the human's answer, and the only\nthing agreement can be measured against.","type":["string","null"]},"id":{"format":"uuid","type":"string"},"mode":{"type":"string"},"model":{"type":"string"},"ticket_id":{"description":"The ticket the run was about, when it was about one.","format":"uuid","type":["string","null"]},"ticket_number":{"type":["string","null"]},"ticket_title":{"type":["string","null"]},"top_confidence":{"description":"The highest confidence in the run, for the log's meter.","format":"double","type":["number","null"]}},"required":["id","mode","created_at","model","answers","above_threshold"],"type":"object"},"DecisionRunsResponse":{"properties":{"days":{"format":"int32","type":"integer"},"runs":{"items":{"$ref":"#/components/schemas/DecisionRunView"},"type":"array"},"summary":{"$ref":"#/components/schemas/DecisionRunSummary"},"surface_key":{"type":"string"}},"required":["surface_key","days","summary","runs"],"type":"object"},"DecisionSuggestionsResponse":{"properties":{"suggestions":{"items":{"$ref":"#/components/schemas/SuggestionSurface"},"type":"array"},"ticket_id":{"format":"uuid","type":"string"}},"required":["ticket_id","suggestions"],"type":"object"},"DecisionSurfaceOverview":{"properties":{"configured":{"description":"False while the org never configured the surface — the ADR 0011\n§3 posture: silently quiet, shown with its what-activates note.","type":"boolean"},"customized":{"type":"boolean"},"decision_model_curated":{"type":"boolean"},"description":{"type":"string"},"enabled":{"type":"boolean"},"internal":{"description":"Internal decision surfaces (search_rerank / llm_verify /\ntool_routing / loop_termination) are never grant targets; the UI\nrenders their posture note-only.","type":"boolean"},"label":{"type":"string"},"latest_starter_version":{"description":"None while no curated set exists for the surface version.","format":"int32","type":["integer","null"]},"mode":{"type":"string"},"model_id":{"type":["string","null"]},"model_provider":{"type":["string","null"]},"starter_version":{"format":"int32","type":"integer"},"surface_key":{"type":"string"}},"required":["surface_key","label","internal","description","starter_version","customized","configured","mode","enabled","decision_model_curated"],"type":"object"},"DeclarationView":{"description":"Wire shape of one declaration. Kinds/options are flattened so generated\nclients can render editors directly from this payload.","properties":{"default":{"$ref":"#/components/schemas/SettingValue"},"description":{"type":"string"},"key":{"type":"string"},"kind":{"$ref":"#/components/schemas/SettingKind"},"label":{"type":"string"},"options":{"description":"Closed option set for enumerations; empty otherwise.","items":{"type":"string"},"type":"array"},"scope":{"description":"\"org\" | \"user\"","type":"string"},"section":{"description":"Settings-hub section this declaration renders under.","type":"string"},"visibility":{"description":"\"ui\" renders an editable row; \"hidden\" is state readable via values.","type":"string"}},"required":["key","kind","default","scope","section","visibility","label","description","options"],"type":"object"},"DedupeEntityKind":{"description":"The entity family a dedupe record binds. Clients ride reconciliation\nonly (mapping decisions, not folds).","enum":["contact","client"],"type":"string"},"DedupeRunMode":{"description":"What an automated pass DOES with its above-threshold suggestions.","enum":["report_only","auto_merge"],"type":"string"},"DedupeRunOutcome":{"properties":{"candidate_id":{"format":"uuid","type":"string"},"confidence":{"description":"The recorded answer's probability/noul number, when one came back.","format":"double","type":["number","null"]},"disposition":{"description":"\"auto_merged\" | \"suggested\" | \"below_threshold\" | \"quiet\"","type":"string"},"label":{"description":"Human label pair (\"A\" ← \"B\") for the report body.","type":"string"},"queue":{"description":"\"config_items\" | \"contacts\" | \"clients\"","type":"string"}},"required":["queue","candidate_id","label","disposition"],"type":"object"},"DedupeRunReport":{"description":"One pass's outcome — the run report (persisted on the workflow row and\ndelivered to the designated users as a `dedupe_report` notification).","properties":{"above_threshold":{"description":"Suggestions whose answer sat at/above the workflow threshold.","minimum":0,"type":"integer"},"asked":{"minimum":0,"type":"integer"},"auto_merged":{"minimum":0,"type":"integer"},"finished_at":{"type":"string"},"outcomes":{"description":"Per-candidate outcomes for the report/notification body.","items":{"$ref":"#/components/schemas/DedupeRunOutcome"},"type":"array"},"queues_scanned":{"items":{"type":"string"},"type":"array"},"scanned":{"minimum":0,"type":"integer"},"started_at":{"type":"string"},"truncated":{"description":"The asks the pass would have fired next run (the resource-efficiency\ncap applies per run; a truncated pass is VISIBLE, never silent).","type":"boolean"}},"required":["queues_scanned","scanned","asked","above_threshold","auto_merged","truncated","started_at","finished_at","outcomes"],"type":"object"},"DedupeRunRequest":{"properties":{"dry_run":{"description":"Preview posture: fire the asks (suggestion-grade, recorded) but\nstamp nothing, merge nothing, notify no one.","type":"boolean"},"entity_kinds":{"items":{"type":"string"},"type":["array","null"]}},"type":"object"},"DedupeRunResponse":{"properties":{"dry_run":{"type":"boolean"},"report":{"$ref":"#/components/schemas/DedupeRunReport"}},"required":["report","dry_run"],"type":"object"},"DedupeWorkflow":{"description":"One org's Jev dedupe workflow (the `dedupe_workflows` row).","properties":{"confidence_threshold":{"description":"The workflow's own confidence dial: auto_merge folds only at/above\nthis; everything below stays queued for the workbench.","format":"double","type":"number"},"created_at":{"format":"date-time","type":"string"},"enabled":{"type":"boolean"},"entity_kinds":{"items":{"type":"string"},"type":"array"},"interval_hours":{"description":"Scheduled cadence; None = manual runs only (the workbench trigger).","format":"int32","type":["integer","null"]},"last_result":{},"last_run_at":{"format":"date-time","type":["string","null"]},"mode":{"$ref":"#/components/schemas/DedupeRunMode"},"next_run_at":{"format":"date-time","type":["string","null"]},"notify_user_ids":{"items":{"format":"uuid","type":"string"},"type":"array"},"org_id":{"format":"uuid","type":"string"},"updated_at":{"format":"date-time","type":"string"},"updated_by":{"format":"uuid","type":["string","null"]}},"required":["org_id","enabled","confidence_threshold","mode","notify_user_ids","entity_kinds","created_at","updated_at"],"type":"object"},"DedupeWorkflowRequest":{"properties":{"confidence_threshold":{"format":"double","type":"number"},"enabled":{"type":"boolean"},"entity_kinds":{"items":{"type":"string"},"type":"array"},"interval_hours":{"description":"Scheduled cadence in hours (None = manual runs only).","format":"int32","type":["integer","null"]},"mode":{"description":"report_only | auto_merge","type":"string"},"notify_user_ids":{"items":{"format":"uuid","type":"string"},"type":"array"}},"required":["enabled","confidence_threshold","mode","notify_user_ids","entity_kinds"],"type":"object"},"DedupeWorkflowView":{"properties":{"decision_configured":{"description":"The gate's honest state: whether the org's decision model is\nconfigured right now (the workflow can be stored even when the\nmodel later goes down — the RUN then degrades quiet, per ADR 0010).","type":"boolean"},"workflow":{"$ref":"#/components/schemas/DedupeWorkflow"}},"required":["workflow","decision_configured"],"type":"object"},"DeeplinkEntry":{"description":"One resolved one-click link on a PO, in open order.","properties":{"carrier":{"type":["string","null"]},"kind":{"$ref":"#/components/schemas/ExternalLinkKind"},"label":{"type":["string","null"]},"line_description":{"type":["string","null"]},"line_id":{"format":"uuid","type":["string","null"]},"line_position":{"format":"int32","type":["integer","null"]},"link_id":{"format":"uuid","type":"string"},"subject":{"description":"vendor | purchase_order | purchase_order_line","type":"string"},"url":{"type":"string"}},"required":["link_id","kind","url","subject"],"type":"object"},"DeeplinkVendor":{"properties":{"account_number":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"website":{"type":["string","null"]}},"required":["id","name"],"type":"object"},"DefinitionScope":{"description":"Visibility scope of a stored definition: `org` (published org-wide,\nadmins only) or `self` (private to the creator).","enum":["org","self"],"type":"string"},"DeleteOrderLineRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"}]},"DeleteStatusResult":{"description":"What a delete with reassignment actually did (Wave 41 lane 12).\n\nReturned instead of a bare 204 because the confirm has to say what\nhappened, and the admin is the one who has to believe it.","properties":{"moved_tickets":{"description":"Tickets moved to the replacement status before the delete.","format":"int64","type":"integer"},"paused_automations":{"description":"Automation rules that named this status and were PAUSED. Never\nrepointed: an automation's conditions are the org's own logic.","format":"int64","type":"integer"},"sla_pauses_changed":{"description":"SLA pause states opened or closed by the move.","format":"int64","type":"integer"}},"required":["moved_tickets","paused_automations","sla_pauses_changed"],"type":"object"},"DeleteTickQuery":{"properties":{"date":{"format":"date","type":"string"},"line_key":{"type":"string"}},"required":["date","line_key"],"type":"object"},"DeliveryAttemptView":{"properties":{"attempt":{"format":"int32","type":"integer"},"created_at":{"format":"date-time","type":"string"},"delivered_at":{"format":"date-time","type":["string","null"]},"event_id":{"format":"uuid","type":"string"},"event_type":{"type":"string"},"id":{"format":"uuid","type":"string"},"last_error":{"type":["string","null"]},"next_retry_at":{"format":"date-time","type":["string","null"]},"response_status":{"description":"HTTP status when a response arrived; null for transport failures.","format":"int32","type":["integer","null"]}},"required":["id","event_id","event_type","attempt","created_at"],"type":"object"},"DeliveryCounts":{"properties":{"dead":{"format":"int64","type":"integer"},"queued":{"format":"int64","type":"integer"},"sending":{"format":"int64","type":"integer"},"sent":{"format":"int64","type":"integer"}},"required":["queued","sending","sent","dead"],"type":"object"},"DeliveryLedgerResponse":{"properties":{"counts":{"$ref":"#/components/schemas/DeliveryCounts","description":"Complete status set — zero counts included, so the summary strip\nnever hides a state by omission."},"next_cursor":{"description":"Opaque cursor for the next page; `None` = exhausted.","type":["string","null"]},"rows":{"items":{"$ref":"#/components/schemas/DeliveryLedgerRow"},"type":"array"},"transport_configured":{"description":"False when no transport is configured: every row then sits queued.","type":"boolean"}},"required":["rows","counts","transport_configured"],"type":"object"},"DeliveryLedgerRow":{"properties":{"attempts":{"format":"int32","type":"integer"},"created_at":{"type":"string"},"from_address":{"type":"string"},"greeting_source":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/EmailSourceView","description":"Which rung of the greeting/signature chain supplied each block for\nTHIS message (Wave 42), as the wire name plus the wording an admin\nreads. `None` is a row written before the lane recorded it, and it\nrenders as \"none\" rather than a guess."}]},"last_error":{"type":["string","null"]},"max_attempts":{"format":"int32","type":"integer"},"message_id":{"format":"uuid","type":"string"},"rfc_message_id":{"type":"string"},"signature_source":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/EmailSourceView"}]},"status":{"type":"string"},"subject":{"type":["string","null"]},"ticket_id":{"format":"uuid","type":["string","null"]},"to_addresses":{"items":{"type":"string"},"type":"array"},"updated_at":{"type":"string"}},"required":["message_id","rfc_message_id","to_addresses","from_address","status","attempts","max_attempts","created_at","updated_at"],"type":"object"},"DeliveryListQuery":{"description":"GET /api/report-schedules/{id}/deliveries query.","properties":{"limit":{"description":"Page size, clamped to [1, 200].","format":"int64","type":["integer","null"]},"offset":{"description":"Row offset for paging.","format":"int64","type":["integer","null"]}},"type":"object"},"DeliveryPage":{"properties":{"deliveries":{"items":{"$ref":"#/components/schemas/DeliveryAttemptView"},"type":"array"},"next_cursor":{"description":"Opaque keyset cursor for the next page; absent when exhausted.","type":["string","null"]}},"required":["deliveries"],"type":"object"},"DeliveryQuery":{"properties":{"cursor":{"type":["string","null"]},"limit":{"format":"int64","type":"integer"}},"type":"object"},"DependencyListResponse":{"description":"A task's direct finish-to-start dependencies (full rows, so the client\nrenders \"blocked by\" without extra fetches).","properties":{"depends_on":{"items":{"$ref":"#/components/schemas/ProjectTask"},"type":"array"},"project_id":{"format":"uuid","type":"string"},"task_id":{"format":"uuid","type":"string"}},"required":["task_id","project_id","depends_on"],"type":"object"},"DestinationView":{"description":"One webhook destination and the counters that decide its pause.","properties":{"consecutive_failures":{"description":"Retryable delivery failures since the last delivery it accepted (or\nlast refused on its merits).","format":"int32","type":"integer"},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"paused_at":{"description":"Set while the destination is paused: new events are not matched for it.","format":"date-time","type":["string","null"]},"paused_reason":{"type":["string","null"]},"url":{"type":"string"}},"required":["id","url","enabled","consecutive_failures"],"type":"object"},"DetectionFile":{"properties":{"content_type":{"type":"string"},"filename":{"type":"string"},"sha256":{"type":"string"},"size_bytes":{"format":"int64","type":"integer"}},"required":["filename","content_type","size_bytes","sha256"],"type":"object"},"DetectionView":{"properties":{"attachments":{"description":"Files that rode the message; the v1 scanner returns one verdict per\nmessage (body plus attachment bytes), so these are what was scanned\nwith it, not per-file verdicts.","items":{"$ref":"#/components/schemas/DetectionFile"},"type":"array"},"from_address":{"type":"string"},"ingest_source":{"description":"Ledger transport label — the ingest source (`webhook/clamd`, …).","type":"string"},"message_id":{"format":"uuid","type":"string"},"occurred_at":{"format":"date-time","type":"string"},"subject":{"type":["string","null"]},"verdict":{"description":"Recorded scan verdict (`infected`); fail-closed quarantines ride the\nsame status, so the pane words it as \"quarantined\", not \"virus\".","type":"string"}},"required":["message_id","from_address","verdict","ingest_source","occurred_at","attachments"],"type":"object"},"DeviceAsset":{"properties":{"base_purchase_cost_minor":{"format":"int64","type":["integer","null"]},"client_id":{"description":"NULL for internal-org assets; the DB CHECK (migration 0103) ties\n`scope` and `client_id` together structurally.","format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"curated_fields":{"description":"CI5: human-set tier; sticky forever, never auto-overwritten."},"depreciation_method":{"description":"none | straight_line | declining_balance.","type":"string"},"depreciation_rate_bps":{"format":"int32","type":["integer","null"]},"field_sources":{"description":"CI5: per observed field `{source_system, source_link_id, observed_at}`."},"fields":{"description":"Custom-field values validated against the layout's schema\n(docs-platform v1 shape; app-validated). The EFFECTIVE view:\ncurated wins over observed (CI5, migration 0061)."},"id":{"format":"uuid","type":"string"},"identity_keys":{"description":"CI3 deterministic identity keys (source GUID, serial, MACs, ...)."},"kind":{"description":"Free-text connector compatibility column; typed classification\nlives in `layout_id` + `fields` (asset taxonomy, migration 0054).","type":"string"},"layout_id":{"description":"The typed layout this asset instantiates; NULL = untyped.","format":"uuid","type":["string","null"]},"merged_into_id":{"description":"CI4: non-NULL means this row was absorbed by the target (tombstone).","format":"uuid","type":["string","null"]},"name":{"type":"string"},"observed_fields":{"description":"CI5: feed-written tier; churns silently with its source."},"org_id":{"format":"uuid","type":"string"},"ownership":{"description":"owned | leased | rented.","type":"string"},"purchase_cost_minor":{"format":"int64","type":["integer","null"]},"purchase_currency":{"type":["string","null"]},"purchase_date":{"format":"date","type":["string","null"]},"purchase_fx_rate_date":{"format":"date","type":["string","null"]},"purchase_fx_rate_used":{"description":"FX conversion snapshot (ADR 0002 lock moment: inventory receipt) —\nlocked once when the purchase cost is written in a non-base\ncurrency; immutable afterwards (DB trigger enforced).","type":["string","null"]},"purchase_fx_source":{"type":["string","null"]},"rental_cost_minor":{"format":"int64","type":["integer","null"]},"rental_currency":{"type":["string","null"]},"rental_cycle":{"description":"monthly | quarterly | annual.","type":["string","null"]},"rental_end":{"format":"date","type":["string","null"]},"rental_start":{"format":"date","type":["string","null"]},"rental_vendor":{"type":["string","null"]},"salvage_value_minor":{"format":"int64","type":["integer","null"]},"scope":{"description":"client | internal_org (migration 0103): the MSP's OWN equipment\nrides the SAME DeviceAsset lifecycle under an org-scoped row —\nclient_id NULL, never a fake client record.","type":"string"},"site_id":{"format":"uuid","type":["string","null"]},"status":{"type":"string"},"tag":{"description":"Human asset tag, e.g. \"EXCH-01\".","type":"string"},"updated_at":{"format":"date-time","type":"string"},"useful_life_months":{"format":"int32","type":["integer","null"]},"warranty":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/WarrantyWindow","description":"The warranty horizon, derived from this row's own columns through\n`asset_warranty_from_fields` at read time. It travels WITH the asset so\nno surface has to re-derive it from `purchase_date` plus\n`useful_life_months` — that duplication is how a ledger and a scan\nsheet end up disagreeing about which side of the window an asset sits\non. NULL when the asset carries nothing to derive a horizon from."}]}},"required":["id","org_id","tag","name","kind","status","created_at","updated_at"],"type":"object"},"DiffEntry":{"description":"One structural change between the current document and the draft. The\ncaller renders these as the review diff; nothing is applied here.","properties":{"after":{"type":["object","null"]},"before":{"type":["object","null"]},"op":{"description":"`add`, `remove` or `change`.","type":"string"},"path":{"description":"A stable location, e.g. `nodes/triage`, `edges/a→b`, `version`.","type":"string"}},"required":["op","path"],"type":"object"},"DirectoryGroupDetailResponse":{"description":"One group with its current members and role mappings.","properties":{"group":{"$ref":"#/components/schemas/DirectoryGroupView"},"members":{"items":{"$ref":"#/components/schemas/DirectoryGroupMemberView"},"type":"array"},"roles":{"items":{"$ref":"#/components/schemas/DirectoryGroupRoleView"},"type":"array"}},"required":["group","members","roles"],"type":"object"},"DirectoryGroupMemberView":{"description":"One member of a group.","properties":{"added_at":{"type":"string"},"added_by":{"description":"Who added them: a staff user id, `null` for a write that came from a\nsync.","format":"uuid","type":["string","null"]},"display_name":{"type":"string"},"email":{"type":"string"},"is_active":{"description":"A deactivated user keeps the membership row; the principal is refused at\nauth instead, so nothing needs unwinding when someone leaves.","type":"boolean"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","display_name","email","is_active","added_at"],"type":"object"},"DirectoryGroupMutationResponse":{"allOf":[{"$ref":"#/components/schemas/DirectoryGroupView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): the group fields at the top level\nplus the additive `mutation` metadata."},"DirectoryGroupRoleView":{"description":"One role a group carries.","properties":{"builtin":{"description":"Shipped preset tag (owner, admin, …), `null` for a custom role.","type":["string","null"]},"name":{"type":"string"},"role_id":{"format":"uuid","type":"string"}},"required":["role_id","name"],"type":"object"},"DirectoryGroupView":{"description":"One directory group, with the counts a listing shows.","properties":{"created_at":{"type":"string"},"description":{"description":"Free-text purpose. `null` when unset.","type":["string","null"]},"external_id":{"description":"The identity provider's identifier for this group, `null` for a group\ncreated in Marula that no directory knows about. Unique per org.","type":["string","null"]},"id":{"format":"uuid","type":"string"},"member_count":{"description":"Active and inactive users in the group.","format":"int64","type":"integer"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"role_count":{"description":"Roles the group carries.","format":"int64","type":"integer"},"updated_at":{"type":"string"}},"required":["id","org_id","name","created_at","updated_at","member_count","role_count"],"type":"object"},"DirectoryGroupsListResponse":{"properties":{"groups":{"description":"Every group in the org, by name.","items":{"$ref":"#/components/schemas/DirectoryGroupView"},"type":"array"}},"required":["groups"],"type":"object"},"DispatchAppointment":{"description":"One booking block on the dispatch board: the appointment plus the\nticket number and client name the block face renders.","properties":{"client_id":{"format":"uuid","type":["string","null"]},"client_name":{"type":["string","null"]},"dispatch_status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/DispatchStatusShade","description":"Latest field-service status push against this booking (dispatch v2,\nadditive): `None` when nothing was pushed yet."}]},"ends_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"kind":{"$ref":"#/components/schemas/AppointmentKind"},"starts_at":{"format":"date-time","type":"string"},"status":{"$ref":"#/components/schemas/AppointmentStatus"},"ticket_id":{"format":"uuid","type":["string","null"]},"ticket_number":{"type":["string","null"]},"title":{"type":"string"},"travel_distance_meters":{"format":"int32","type":["integer","null"]},"travel_from":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TravelOrigin","description":"Travel provenance (Wave 32 lane 7): where the estimate ran from,\nwhich provider produced it, and at what quality. All absent for\nmanual or unestimated bookings."}]},"travel_minutes":{"format":"int32","type":"integer"},"travel_provider":{"type":["string","null"]},"travel_quality":{"type":["string","null"]},"user_id":{"format":"uuid","type":"string"}},"required":["id","user_id","title","starts_at","ends_at","kind","status","travel_minutes"],"type":"object"},"DispatchDayQuery":{"description":"One dispatch-path query: `date` is a LOCAL date interpreted as the\nUTC day window (documented UTC scope).","properties":{"date":{"description":"The day to assemble; defaults to today (UTC).","format":"date","type":["string","null"]}},"type":"object"},"DispatchDayView":{"description":"The assembled day-view payload: one request, one render.","properties":{"date":{"format":"date","type":"string"},"from":{"format":"date-time","type":"string"},"technicians":{"items":{"$ref":"#/components/schemas/TechnicianView"},"type":"array"},"to":{"format":"date-time","type":"string"},"unassigned":{"description":"Unassigned, non-closed tickets in dispatch order (priority rank,\nthen effective SLA/due deadline).","items":{"$ref":"#/components/schemas/MyWorkItem"},"type":"array"}},"required":["date","from","to","technicians","unassigned"],"type":"object"},"DispatchQueueQuery":{"properties":{"limit":{"description":"Page size, clamped to [1, 500].","format":"int64","type":["integer","null"]}},"type":"object"},"DispatchQueueView":{"description":"GET /api/dispatch/queue envelope.","properties":{"queue":{"items":{"$ref":"#/components/schemas/QueuedTicket"},"type":"array"}},"required":["queue"],"type":"object"},"DispatchStatusShade":{"description":"The latest connector/staff status push for one booking, shaded onto\nthe board block face.","properties":{"note":{"type":["string","null"]},"occurred_at":{"format":"date-time","type":"string"},"source":{"description":"`connector` | `staff`.","type":"string"},"status":{"type":"string"}},"required":["status","source","occurred_at"],"type":"object"},"DispositionStatus":{"description":"The human verification disposition of one time entry (Wave 39 V1).\n`rejected` and `non_billable` demand the typed [`UnbillableReason`];\nthe workspace badge set matches this vocabulary exactly.","enum":["approved","rejected","non_billable","needs_review"],"type":"string"},"DnsRecordInstruction":{"description":"One DNS record an operator pastes by hand. Mirrors the solver's\n`ManualDnsRecord` as a utoipa schema (the seam crate carries no OpenAPI\ndependency).","properties":{"kind":{"description":"The record TYPE: `TXT`, `A`, `AAAA`, or `CNAME`.","example":"TXT","type":"string"},"name":{"description":"The record NAME (e.g. `_marula-verify.support.msp.example.com`).","type":"string"},"notes":{"type":"string"},"ttl":{"format":"int32","minimum":0,"type":"integer"},"value":{"description":"The record VALUE to paste.","type":"string"}},"required":["name","kind","value","ttl","notes"],"type":"object"},"Document":{"description":"A document's living handle. No content lives here — content is only\never a [`DocumentVersion`]; `current_version` names the newest one.","properties":{"client_id":{"description":"NULL = org-global document; set = client-scoped (docs-platform v1,\nmigration 0053).","format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"current_version":{"description":"The newest version number in the document's history.","format":"int32","type":"integer"},"fields":{"description":"Custom-field values validated against the layout's schema."},"id":{"format":"uuid","type":"string"},"layout_id":{"description":"The typed layout this document instantiates; NULL = untyped.","format":"uuid","type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"updated_by":{"format":"uuid","type":["string","null"]}},"required":["id","org_id","title","current_version","created_at","updated_at"],"type":"object"},"DocumentAttachmentDeleted":{"properties":{"deleted":{"type":"boolean"},"object_removed":{"description":"Whether the backing object was reclaimed too (refcount hit 0).","type":"boolean"}},"required":["deleted","object_removed"],"type":"object"},"DocumentAttachmentList":{"properties":{"attachments":{"items":{"$ref":"#/components/schemas/DocumentAttachmentView"},"type":"array"}},"required":["attachments"],"type":"object"},"DocumentAttachmentView":{"properties":{"content_type":{"type":"string"},"created_at":{"type":"string"},"document_id":{"format":"uuid","type":"string"},"filename":{"type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"sha256":{"description":"sha256 hex — integrity verifiable after download.","type":"string"},"size_bytes":{"format":"int64","type":"integer"},"uploaded_by":{"format":"uuid","type":["string","null"]}},"required":["id","org_id","document_id","filename","content_type","size_bytes","sha256","created_at"],"type":"object"},"DocumentLayout":{"description":"An org-managed document/asset type. System layouts (the seeded\nstandard MSP set) are editable but not deletable.","properties":{"created_at":{"format":"date-time","type":"string"},"description":{"type":"string"},"fields":{"items":{"$ref":"#/components/schemas/LayoutField"},"type":"array"},"icon":{"type":"string"},"id":{"format":"uuid","type":"string"},"is_system":{"type":"boolean"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"semantic_class":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CiSemanticClass","description":"CI6: the behavior-bearing semantic class this type is bound to\n(ADR 0003, migration 0061). Renaming the layout never changes\nbehavior; identity keys and field schema read this class."}]},"slug":{"description":"Stable per-org handle (unique).","type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","slug","name","icon","fields","is_system","created_at","updated_at"],"type":"object"},"DocumentLink":{"description":"A cross-entity relationship row (`document_links`, 0053).","properties":{"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"from_id":{"format":"uuid","type":"string"},"from_kind":{"$ref":"#/components/schemas/LinkKind"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"to_id":{"format":"uuid","type":"string"},"to_kind":{"$ref":"#/components/schemas/LinkKind"}},"required":["id","org_id","from_kind","from_id","to_kind","to_id","created_at"],"type":"object"},"DocumentListQuery":{"properties":{"client_id":{"description":"Scope narrow: a client uuid, or the literal `global` for org-wide\ndocuments (docs-platform v1). Absent = all scopes.","type":["string","null"]},"layout_id":{"format":"uuid","type":["string","null"]},"q":{"description":"Title substring narrowing; ranked full-text search rides /api/search.","type":["string","null"]}},"type":"object"},"DocumentMutationResponse":{"allOf":[{"$ref":"#/components/schemas/Document"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): document fields at the top\nlevel plus the additive `mutation` metadata."},"DocumentVersion":{"description":"One immutable snapshot in a document's append-only history. Every\nversion stores its complete title + body, so any pair is diffable\nwithout reconstructing intermediate state.","properties":{"author_id":{"format":"uuid","type":["string","null"]},"body":{"type":"string"},"change_summary":{"description":"Optional human explanation of what changed in this version.","type":"string"},"created_at":{"format":"date-time","type":"string"},"document_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"restored_from_version":{"description":"Set when this version was created by a RESTORE: the historical\nversion whose content was copied. Restore-creates-new-version is\nthe only way old content comes back — history is never rewritten.","format":"int32","type":["integer","null"]},"title":{"type":"string"},"version":{"description":"1-based, gap-free per document.","format":"int32","type":"integer"}},"required":["id","org_id","document_id","version","title","created_at"],"type":"object"},"DocumentView":{"allOf":[{"$ref":"#/components/schemas/Document"},{"properties":{"body":{"type":"string"},"change_summary":{"type":"string"},"content_updated_at":{"format":"date-time","type":"string"},"restored_from_version":{"format":"int32","type":["integer","null"]}},"required":["body","change_summary","content_updated_at"],"type":"object"}],"description":"GET /api/documents/{id} response: the living handle plus the CURRENT\nversion's content (read-your-write on the body the last write stored)."},"DownloadMode":{"description":"Which way this deployment can hand a download out.","enum":["presigned","stream"],"type":"string"},"DownloadTicketQuery":{"description":"The ticket request's query: `?presign=true` asks for a URL, absent streams.","properties":{"presign":{"description":"`true` asks for a download ticket. Anything else (or no query at all)\nkeeps the byte contract untouched.","type":"boolean"}},"type":"object"},"DryRunRequest":{"allOf":[{"$ref":"#/components/schemas/RowsPayload"},{"properties":{"conflict_policy":{"$ref":"#/components/schemas/ConflictPolicy"},"entity_kind":{"$ref":"#/components/schemas/EntityKind"},"label":{"description":"Free-form run label recorded with the dry-run history entry.","type":["string","null"]},"template_id":{"description":"Optional saved field-mapping template: source headers are renamed to\nthe template's canonical columns before validation (D2 remainder).","format":"uuid","type":["string","null"]}},"required":["entity_kind","conflict_policy"],"type":"object"}],"description":"POST /api/imports/dry-run request: validate a full payload and report\nwhat each row WOULD do before any data write. The probe is recorded as a\nfinished dry-run batch for operator history."},"DsnRow":{"properties":{"detail":{"type":["string","null"]},"service":{"type":"string"},"state":{"description":"`configured` | `unset` | `unknown` — never the DSN itself.","type":"string"}},"required":["service","state"],"type":"object"},"DunningState":{"description":"Collections posture of an issued invoice. Forward-only: states are\ndata moved by explicit API writes (events + write trail), never a\nbackground side effect.","enum":["none","reminder_sent","overdue","final_notice","collections"],"type":"string"},"DunningTransitionRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key +\nIf-Match `version` on the invoice."},{"properties":{"note":{"type":["string","null"]},"to_state":{"$ref":"#/components/schemas/DunningState","description":"The target state; must be strictly forward on the ladder\n(skipping rungs is allowed)."}},"required":["to_state"],"type":"object"}]},"EdgeCheck":{"properties":{"error":{"description":"Set when the address lookup failed.","type":["string","null"]},"resolves":{"description":"Whether the domain resolves to at least one address. Plausibility\nonly: an edge-proxied record resolves to the CDN's IPs.","type":"boolean"}},"required":["resolves"],"type":"object"},"EdgeExchangeResponse":{"oneOf":[{"$ref":"#/components/schemas/TokenPair"},{"description":"Org MFA policy satisfied the same way as password login: any\nenrolled factor must complete via POST /api/auth/mfa/verify.","properties":{"methods":{"items":{"type":"string"},"type":"array"},"mfa_required":{"type":"boolean"},"mfa_token":{"type":"string"}},"required":["mfa_required","mfa_token","methods"],"type":"object"}]},"EdgeLinkInfo":{"properties":{"email":{"type":"string"},"last_login_at":{"format":"date-time","type":["string","null"]},"link_id":{"format":"uuid","type":"string"},"subject":{"type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["link_id","user_id","subject","email"],"type":"object"},"EdgeSourceInfo":{"properties":{"audience":{"type":"string"},"display_name":{"type":"string"},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"issuer":{"type":"string"},"jwks_url":{"type":"string"},"name":{"description":"URL path segment used by the exchange + link routes.","type":"string"}},"required":["id","name","display_name","issuer","audience","jwks_url","enabled"],"type":"object"},"EffectsRequest":{"properties":{"actions":{"description":"AutomationAction documents, parsed and validated via core-domain."},"board_id":{"description":"The board a ticket-less subject is scoped to.","format":"uuid","type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"rule_id":{"description":"The rule whose effects these are. Required in practice: the rule's\nrecorded author is the principal the batch runs as (Wave 58 lane\n`p58-effects-seam`), so a call that names no rule is refused. Optional\nin the wire shape so \"no rule\" is expressible and refusable rather than\na sentinel the seam guesses at.","format":"uuid","type":["string","null"]},"ticket_id":{"description":"The ticket the subject resolved to. `None` on a board-subject match\n(Wave 43 lane 3) — there is no ticket to act on.","format":"uuid","type":["string","null"]}},"required":["org_id","actions"],"type":"object"},"EmailDeliveryView":{"description":"Delivery projection from the `email_outbound` ledger. Present for\noutbound rows only — inbound mail has no send lifecycle.","properties":{"attempts":{"format":"int32","type":"integer"},"last_error":{"type":["string","null"]},"max_attempts":{"format":"int32","type":"integer"},"status":{"description":"queued | sending | sent | dead","type":"string"}},"required":["status","attempts","max_attempts"],"type":"object"},"EmailDirection":{"description":"Whether a message entered (inbound) or left (outbound) the system.","enum":["inbound","outbound"],"type":"string"},"EmailEnvelopeOverride":{"description":"The greeting and signature a composer wrote for THIS message (Wave 42).\n\nPurely additive and purely per-message: it changes what goes out with\nthis one note and is never stored as a preference. The composer\nprefills both fields from the sender's own default, so this carries the\nEDIT, and the composer is the only thing that should set it — the\nserver never merges it into a preference behind the sender's back.","properties":{"greeting":{"type":["string","null"]},"signature":{"type":["string","null"]}},"type":"object"},"EmailEnvelopeView":{"description":"The caller's resolved email envelope: the greeting and signature that\nwould go out, which rung supplied each, the org's own signature for the\nfallback story, the policy in force, and the insertable tokens.\n\nThis is the preview AND the composer's prefill in one call, resolved\nthrough the same chain and the same dialect the send path uses. A UI\nthat previewed with its own regex would be a second implementation of\nthe rules, and the one thing a preview must never be is more generous\nthan the send.","properties":{"greeting":{"type":["string","null"]},"greeting_source":{"type":"string"},"org_signature":{"description":"The org's own signature (rendered), so a member can see what their\nmessages fall back to without an admin.","type":["string","null"]},"policy":{"type":"string"},"signature":{"type":["string","null"]},"signature_source":{"type":"string"},"tokens":{"items":{"$ref":"#/components/schemas/EmailTokenView"},"type":"array"},"user_greeting":{"description":"The caller's own stored defaults, unrendered — the token form the\neditor edits.","type":"string"},"user_signature":{"type":"string"}},"required":["policy","greeting_source","signature_source","user_greeting","user_signature","tokens"],"type":"object"},"EmailMessage":{"description":"One persisted email message. `rfc_message_id` is unique per org and is\nthe idempotency key for inbound processing; `email_refs` carries the\nRFC 5322 References chain (`references` itself is reserved SQL).","properties":{"body_text":{"type":["string","null"]},"comment_id":{"format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"direction":{"$ref":"#/components/schemas/EmailDirection"},"email_refs":{"items":{"type":"string"},"type":"array"},"from_address":{"type":"string"},"from_name":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"in_reply_to":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"raw_ref":{"type":["string","null"]},"rfc_message_id":{"type":"string"},"scan_status":{"$ref":"#/components/schemas/ScanStatus"},"subject":{"type":["string","null"]},"ticket_id":{"format":"uuid","type":["string","null"]},"to_addresses":{"items":{"type":"string"},"type":"array"},"transport":{"description":"Which transport adapter handled the message (postmark/smtp/…).","type":"string"}},"required":["id","org_id","direction","rfc_message_id","email_refs","from_address","to_addresses","scan_status","transport","created_at"],"type":"object"},"EmailSourceView":{"description":"One rung of the resolution chain, as the ledger reports it.","properties":{"label":{"description":"Ledger wording: \"the sender\", \"the board\", \"the organization\".","type":"string"},"source":{"description":"The stable wire name (`user_default`, `board_identity`, …).","type":"string"}},"required":["source","label"],"type":"object"},"EmailTokenView":{"description":"One placeholder the signature editor can insert, with the value it would\nexpand to for the caller RIGHT NOW.","properties":{"key":{"description":"The placeholder key, e.g. `user.name`.","type":"string"},"label":{"description":"Human label for the picker row.","type":"string"},"value":{"description":"What it expands to for this caller, or `None` when their record has\nno value for it — a token that would render empty says so here\ninstead of looking like a field the editor forgot.","type":["string","null"]}},"required":["key","label"],"type":"object"},"EmailVerifiedResponse":{"properties":{"email_verified":{"type":"boolean"},"id":{"format":"uuid","type":"string"}},"required":["id","email_verified"],"type":"object"},"EmailVerifyRequest":{"properties":{"email":{"type":"string"}},"required":["email"],"type":"object"},"EmbedMutationResponse":{"allOf":[{"$ref":"#/components/schemas/KbEmbed"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}]},"EmbedResolution":{"description":"The allowlist decision plus (optionally) the provider's own oEmbed\nembed HTML, fetched server-side. `html` is present only when\n`fetch=true` and the provider endpoint answers 2xx.","properties":{"html":{"description":"Sanctioned embed HTML straight from the provider's oEmbed endpoint.\nNever client-supplied; only present when `fetch` was requested.","type":["string","null"]},"oembed_url":{"description":"The percent-encoded oEmbed URL this resolution would fetch.","type":"string"},"provider_id":{"format":"uuid","type":"string"},"provider_slug":{"type":"string"}},"required":["provider_id","provider_slug","oembed_url"],"type":"object"},"EntityBatchOutcome":{"properties":{"detail":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"merge":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/EntityMerge"}]},"outcome":{"description":"\"applied\" | \"skipped\" | \"failed\"","type":"string"}},"required":["id","outcome"],"type":"object"},"EntityBatchRequest":{"properties":{"action":{"description":"accept | reject | defer","type":"string"},"ids":{"items":{"format":"uuid","type":"string"},"type":"array"}},"required":["ids","action"],"type":"object"},"EntityBatchResponse":{"properties":{"accepted":{"minimum":0,"type":"integer"},"deferred":{"minimum":0,"type":"integer"},"failed":{"minimum":0,"type":"integer"},"outcomes":{"items":{"$ref":"#/components/schemas/EntityBatchOutcome"},"type":"array"},"rejected":{"minimum":0,"type":"integer"},"skipped":{"minimum":0,"type":"integer"}},"required":["accepted","rejected","deferred","skipped","failed","outcomes"],"type":"object"},"EntityCandidateView":{"allOf":[{"$ref":"#/components/schemas/EntityMergeCandidate"},{"properties":{"candidate_label":{"type":["string","null"]},"primary_label":{"description":"Human labels for the card: contact candidates carry both contact\nnames; client candidates carry the client name + vendor label.","type":"string"}},"required":["primary_label"],"type":"object"}]},"EntityKind":{"description":"Which entity family a batch targets. One batch = exactly one kind; the\nparent-before-child ordering across batches keeps foreign references\nresolvable without cross-batch transactions. D2 remainder extends the\noriginal client/site/contact family with boards, tickets, and time\nentries: [`Self::import_order`] is the documented run-script order.","enum":["client","site","contact","board","ticket","time_entry","kb_category","kb_article"],"type":"string"},"EntityMerge":{"description":"An audited, undoable contact merge (the `ci_merges` split substrate).","properties":{"candidate_id":{"format":"uuid","type":["string","null"]},"confidence":{"format":"double","type":["number","null"]},"entity_kind":{"$ref":"#/components/schemas/DedupeEntityKind"},"evidence":{},"id":{"format":"uuid","type":"string"},"merged_at":{"format":"date-time","type":"string"},"merged_by":{"format":"uuid","type":["string","null"]},"merged_entity_id":{"format":"uuid","type":"string"},"moved":{},"org_id":{"format":"uuid","type":"string"},"primary_entity_id":{"format":"uuid","type":"string"},"reason":{"type":"string"},"status":{"type":"string"},"undone_at":{"format":"date-time","type":["string","null"]},"undone_by":{"format":"uuid","type":["string","null"]}},"required":["id","org_id","entity_kind","primary_entity_id","merged_entity_id","reason","status","merged_at"],"type":"object"},"EntityMergeCandidate":{"description":"A reviewable client/contact dedupe candidate (ADR 0003 visible\nevidence). For contact candidates both ids are set; for client\nreconciliation the candidate side is the VENDOR ORG\n(`vendor_org_id`/`vendor_label`) and `candidate_entity_id` is NULL.","properties":{"candidate_entity_id":{"format":"uuid","type":["string","null"]},"confidence":{"format":"double","type":"number"},"created_at":{"format":"date-time","type":"string"},"decided_at":{"format":"date-time","type":["string","null"]},"decided_by":{"format":"uuid","type":["string","null"]},"entity_kind":{"$ref":"#/components/schemas/DedupeEntityKind"},"evidence":{},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"primary_entity_id":{"format":"uuid","type":"string"},"reason":{"type":"string"},"source_system":{"type":["string","null"]},"status":{"$ref":"#/components/schemas/EntityMergeStatus"},"updated_at":{"format":"date-time","type":"string"},"vendor_label":{"type":["string","null"]},"vendor_org_id":{"type":["string","null"]}},"required":["id","org_id","entity_kind","primary_entity_id","confidence","reason","status","created_at","updated_at"],"type":"object"},"EntityMergeStatus":{"description":"Candidate lifecycle — mirrors `CiMergeStatus` (same queue mechanics).","enum":["pending","auto_merged","accepted","rejected","deferred"],"type":"string"},"EntitySourceLink":{"description":"A vendor record link for a contact (the CI2 link key generalized).","properties":{"created_at":{"format":"date-time","type":"string"},"entity_id":{"format":"uuid","type":"string"},"entity_kind":{"$ref":"#/components/schemas/DedupeEntityKind"},"external_id":{"type":"string"},"id":{"format":"uuid","type":"string"},"last_seen_at":{"format":"date-time","type":"string"},"org_id":{"format":"uuid","type":"string"},"payload_ref":{"type":["string","null"]},"source_guid":{"type":["string","null"]},"source_system":{"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","entity_kind","entity_id","source_system","external_id","last_seen_at","created_at","updated_at"],"type":"object"},"EntityWorkbenchDecision":{"properties":{"answers":{},"mode":{"type":"string"},"model":{"type":"string"},"provider":{"type":"string"},"recorded_at":{"format":"date-time","type":"string"},"surface":{"type":"string"}},"required":["surface","mode","provider","model","recorded_at","answers"],"type":"object"},"EntityWorkbenchFieldRow":{"properties":{"candidate_curated":{},"candidate_observed":{},"candidate_source":{},"key":{"type":"string"},"primary_curated":{},"primary_observed":{},"primary_source":{}},"required":["key"],"type":"object"},"EntityWorkbenchSide":{"properties":{"contact":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Contact"}]},"curated_fields":{},"field_sources":{},"observed_fields":{},"source_links":{"items":{"$ref":"#/components/schemas/EntitySourceLink"},"type":"array"}},"required":["source_links","observed_fields","curated_fields","field_sources"],"type":"object"},"EntityWorkbenchView":{"properties":{"auto_merge_threshold":{"description":"The org's auto-merge threshold, surfaced inline (why was this\nqueued instead of auto-merged).","format":"double","type":"number"},"candidate":{"$ref":"#/components/schemas/EntityCandidateView"},"candidate_side":{"$ref":"#/components/schemas/EntityWorkbenchSide"},"decision":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/EntityWorkbenchDecision","description":"Suggestion-grade `decision_record_dedupe` judgment; `None` (silent\nin the UI) whenever the decision model is unconfigured — ADR 0010."}]},"fields":{"items":{"$ref":"#/components/schemas/EntityWorkbenchFieldRow"},"type":"array"},"primary":{"$ref":"#/components/schemas/EntityWorkbenchSide"}},"required":["candidate","primary","candidate_side","fields","auto_merge_threshold"],"type":"object"},"EntryActionRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"IF1-IF3 mutation parameters (idempotency key / version)."},{"properties":{"action":{"description":"`approve` | `reject` | `flip_billable` | `adjust_minutes` |\n`recategorize` | `add_note`.","type":"string"},"minutes":{"description":"`adjust_minutes` only: the NEW effective minutes for the entry\n(0..=current effective). The signed delta is derived here.","format":"int64","type":["integer","null"]},"note":{"description":"`add_note` only: the note text.","type":["string","null"]},"reason":{"description":"Required by `reject`, `flip_billable`, and `adjust_minutes`:\nthe human explanation that rides the audit row (V9). Omitting\nit is a 422, not a silent write.","type":["string","null"]},"reason_code":{"description":"`reject` only: the typed rejection code.","type":["string","null"]},"unbillable_reason":{"description":"`flip_billable` only: required when flipping to non-billable.","type":["string","null"]},"work_type":{"description":"`recategorize` only: a category slug from the org taxonomy.","type":["string","null"]}},"required":["action"],"type":"object"}],"description":"The body of an entry action. One endpoint, one envelope, every\nin-context edit — so the reason requirement, the audit row, the\npre-run guard, and the invoiced lock cannot be forgotten on one\nbranch."},"EntryActionResponse":{"description":"The applied action result.","properties":{"action":{"description":"The action actually applied.","type":"string"},"adjustment":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TimeEntryAdjustment","description":"The adjustment row this write produced, when it produced one."}]},"audit_action":{"description":"The audit action name written to `audit_events` (V9).","type":"string"},"disposition":{"description":"The entry's current disposition after the write.","type":["string","null"]},"effective_minutes":{"description":"Effective minutes after the write.","format":"int64","type":"integer"},"entry_id":{"format":"uuid","type":"string"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["entry_id","action","effective_minutes","audit_action","mutation"],"type":"object"},"EntrySuggestion":{"description":"Suggestion payload built from one `decision_billing_audit` row.","properties":{"billable_probability":{"description":"The audit's `billable` probability, when recorded.","format":"double","type":["number","null"]},"chip":{"description":"The suggested classification chip: how to interpret the audit.\n`billable_false` = \"looks non-billable\", `billable_true` =\n\"looks billable\". Derived from the `billable` answer's\nthreshold state — never auto-applied.","type":"string"},"label":{"description":"Human-readable chip text the UI renders (typed, no model prose).","type":"string"},"mode":{"description":"`shadow` rows drive nothing and are labeled as such.","type":"string"},"recorded_at":{"format":"date-time","type":"string"},"state":{"description":"`act` | `pending` | `ignore` — threshold gate at read time.","type":["string","null"]},"verdicts":{"description":"The server's mechanical verdicts for this entry (Wave 41 lane 8), each\nwith the one action that clears it. Empty means clean — and empty for\nan entry that is not visible in scope either, because RLS proves\nvisibility by absence, never by an error.","items":{"$ref":"#/components/schemas/TimeVerdict"},"type":"array"}},"required":["chip","label","mode","recorded_at"],"type":"object"},"EntrySuggestionsResponse":{"properties":{"entry_id":{"format":"uuid","type":"string"},"suggestions":{"description":"Empty when the surface is unconfigured or no answers exist —\nthe honest visibly-absent state.","items":{"$ref":"#/components/schemas/EntrySuggestion"},"type":"array"}},"required":["entry_id","suggestions"],"type":"object"},"EscalationStep":{"description":"One escalation ladder step as stored (`sla_escalation_steps`).","properties":{"at_pct":{"format":"int64","type":"integer"},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"notify_user_ids":{"description":"Extra recipients beyond the ticket assignee.","items":{"format":"uuid","type":"string"},"type":"array"},"org_id":{"format":"uuid","type":"string"},"policy_id":{"format":"uuid","type":"string"},"step_index":{"format":"int32","type":"integer"},"trigger_kind":{"description":"'risk' | 'breach' | 'escalate' — bound to existing notification kinds.","type":"string"}},"required":["id","org_id","policy_id","step_index","trigger_kind","at_pct","notify_user_ids","enabled"],"type":"object"},"EscalatorSpec":{"description":"Scheduled escalator spec (E): uplift bps per billing-annual\nanniversary.","properties":{"first_uplift_on":{"description":"First anniversary the uplift applies from (`None` = the anchor's\nfirst full period).","format":"date","type":["string","null"]},"uplift_bps":{"format":"int32","type":"integer"}},"required":["uplift_bps"],"type":"object"},"EventsListResponse":{"properties":{"connection_id":{"format":"uuid","type":"string"},"events":{"items":{"$ref":"#/components/schemas/CalendarSyncEvent"},"type":"array"}},"required":["connection_id","events"],"type":"object"},"ExpenseMutationResponse":{"properties":{"expense":{"$ref":"#/components/schemas/TicketExpense"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["expense","mutation"],"type":"object"},"ExportArchiveUpdate":{"properties":{"archived":{"type":"boolean"}},"required":["archived"],"type":"object"},"ExportBundle":{"description":"The org-data export envelope, `bundle_version` 1.","properties":{"bundle_version":{"description":"Envelope schema version. Bump ONLY for breaking section reshapes.","format":"int32","minimum":0,"type":"integer"},"generated_at":{"description":"Dump instant (excluded from determinism comparisons).","format":"date-time","type":"string"},"org_id":{"description":"The org this bundle documents.","format":"uuid","type":"string"},"sections":{"$ref":"#/components/schemas/ExportSections","description":"The export sections. Always fully serialized."}},"required":["bundle_version","org_id","generated_at","sections"],"type":"object"},"ExportClient":{"description":"One client company row.","properties":{"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"notes":{"type":["string","null"]},"restricted_to":{"description":"Optional visibility restriction: when non-empty, only these users\n(plus admins) can see the client. Opaque user-id references.","items":{"format":"uuid","type":"string"},"type":"array"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","name","restricted_to","created_at","updated_at"],"type":"object"},"ExportComment":{"description":"One ticket comment.","properties":{"author_id":{"format":"uuid","type":["string","null"]},"body":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"kind":{"$ref":"#/components/schemas/NoteKind","description":"Structured note taxonomy: internal | client_visible | in_house_update."}},"required":["id","author_id","kind","body","created_at"],"type":"object"},"ExportContact":{"description":"One contact row; `client_id`/`site_id` reference this bundle.","properties":{"client_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"email":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_primary":{"type":"boolean"},"name":{"type":"string"},"phone":{"type":["string","null"]},"site_id":{"format":"uuid","type":["string","null"]},"title":{"type":["string","null"]}},"required":["id","client_id","site_id","name","is_primary","created_at"],"type":"object"},"ExportCreatedResponse":{"properties":{"bundle_version":{"description":"Envelope version of the produced bundle (mirrors the body).","format":"int32","minimum":0,"type":"integer"},"download_url":{"description":"Self-contained relative URL carrying the signature + expiry; the\nclient prefixes its API origin. Works without a bearer token.","type":"string"},"expires_at":{"format":"date-time","type":"string"},"export_id":{"format":"uuid","type":"string"},"sha256":{"description":"sha256 of the stored bytes (download integrity check).","type":"string"},"size_bytes":{"description":"Exact bundle byte size as stored.","format":"int64","type":"integer"}},"required":["export_id","download_url","expires_at","size_bytes","sha256","bundle_version"],"type":"object"},"ExportFieldValue":{"description":"One typed custom-field value attached to any entity row.","properties":{"entity":{"description":"Field-definition family the value belongs to (ticket | client |\ncontact | asset) — resolved from the definition at load time.","type":"string"},"entity_id":{"format":"uuid","type":"string"},"field_id":{"format":"uuid","type":"string"},"value":{"description":"The typed value exactly as stored (JSONB passthrough).","type":"object"}},"required":["entity","entity_id","field_id","value"],"type":"object"},"ExportHistoryList":{"properties":{"exports":{"items":{"$ref":"#/components/schemas/ExportHistoryView"},"type":"array"}},"required":["exports"],"type":"object"},"ExportHistoryView":{"description":"One ledger row as /settings/export renders it. The signed link is\nNEVER stored — rows carry content facts; links are minted fresh.","properties":{"archived":{"type":"boolean"},"bundle_version":{"format":"int32","type":"integer"},"created_at":{"type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"export_id":{"format":"uuid","type":"string"},"sha256":{"type":"string"},"size_bytes":{"format":"int64","type":"integer"}},"required":["export_id","bundle_version","size_bytes","sha256","archived","created_at"],"type":"object"},"ExportLinkRefreshed":{"properties":{"download_url":{"type":"string"},"expires_at":{"format":"date-time","type":"string"}},"required":["download_url","expires_at"],"type":"object"},"ExportOrg":{"description":"Org profile identity.","properties":{"created_at":{"format":"date-time","type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"ticket_prefix":{"description":"Human ticket-number prefix (e.g. \"MER\").","type":"string"}},"required":["name","slug","ticket_prefix","created_at"],"type":"object"},"ExportSections":{"description":"Section container — all fields always present; empty org state is an\nempty array, never an absent key.","properties":{"clients":{"items":{"$ref":"#/components/schemas/ExportClient"},"type":"array"},"contacts":{"items":{"$ref":"#/components/schemas/ExportContact"},"type":"array"},"custom_field_values":{"description":"Every custom-field VALUE in the org, flat across entity families —\nthe data plane the U5 manifest deliberately excludes. `entity` names\nthe family the field definition attaches to (ticket | client |\ncontact | asset); `entity_id` is the owning row.","items":{"$ref":"#/components/schemas/ExportFieldValue"},"type":"array"},"customization":{"$ref":"#/components/schemas/ManifestSections","description":"The U5 customization-manifest sections, embedded verbatim — D1 is\nthe manifest's generalization, not its replacement."},"org":{"$ref":"#/components/schemas/ExportOrg","description":"Org profile identity (volatile counters excluded — a restore\nreseeds the ticket-number sequence from the imported tickets)."},"sites":{"items":{"$ref":"#/components/schemas/ExportSite"},"type":"array"},"tickets":{"description":"Tickets with their comments nested. Parents (client/site/contact)\nare referenced by id into the sections above.","items":{"$ref":"#/components/schemas/ExportTicket"},"type":"array"},"time_entries":{"items":{"$ref":"#/components/schemas/ExportTimeEntry"},"type":"array"}},"required":["customization","org","clients","sites","contacts","tickets","time_entries","custom_field_values"],"type":"object"},"ExportSite":{"description":"One site row; `client_id` references `clients` in this bundle.","properties":{"address":{"type":["string","null"]},"city":{"type":["string","null"]},"client_id":{"format":"uuid","type":"string"},"country":{"type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"latitude":{"format":"double","type":["number","null"]},"longitude":{"format":"double","type":["number","null"]},"name":{"type":"string"},"postal_code":{"type":["string","null"]},"region":{"type":["string","null"]},"timezone":{"type":["string","null"]}},"required":["id","client_id","name","created_at"],"type":"object"},"ExportTicket":{"description":"One ticket with its comment thread nested. Authorship is an opaque\nuser-id reference; the bundle never carries user records.","properties":{"assignee_id":{"format":"uuid","type":["string","null"]},"board_id":{"format":"uuid","type":"string"},"body":{"type":["string","null"]},"category":{"$ref":"#/components/schemas/TicketStatusCategory","description":"Semantic category of the captured status (behavior binds to this,\nnever to the status label)."},"client_id":{"format":"uuid","type":["string","null"]},"comments":{"items":{"$ref":"#/components/schemas/ExportComment"},"type":"array"},"contact_id":{"format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"due_at":{"format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"number":{"description":"Human number (e.g. \"MAR-12\") — the bundle's stable ticket key.","type":"string"},"priority":{"$ref":"#/components/schemas/Priority"},"requester_id":{"format":"uuid","type":["string","null"]},"resolved_at":{"format":"date-time","type":["string","null"]},"site_id":{"format":"uuid","type":["string","null"]},"status_id":{"format":"uuid","type":"string"},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","number","board_id","status_id","title","priority","category","client_id","site_id","contact_id","assignee_id","requester_id","created_at","updated_at","comments"],"type":"object"},"ExportTimeEntry":{"description":"One time entry; `ticket_id` and `user_id` reference this bundle's\ntickets and the org's live users respectively.","properties":{"billable":{"type":"boolean"},"created_at":{"format":"date-time","type":"string"},"ended_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"minutes":{"format":"int64","type":"integer"},"notes":{"type":["string","null"]},"phase_id":{"format":"uuid","type":["string","null"]},"started_at":{"format":"date-time","type":"string"},"ticket_id":{"description":"`None` on project-scoped entries (Wave 39 lane 4): `phase_id`\nscopes the entry instead.","format":"uuid","type":["string","null"]},"unbillable_reason":{"type":["string","null"]},"user_id":{"format":"uuid","type":"string"},"work_type":{"type":["string","null"]}},"required":["id","user_id","started_at","ended_at","minutes","billable","created_at"],"type":"object"},"ExternalDocUrlKind":{"description":"The closed external-document vocabulary. The classifier in `core-kb`\nmaps a URL to exactly one of these; `other` is the honest fallback for\nany link that is neither SharePoint, OneDrive, nor Google Drive.","enum":["sharepoint","onedrive","google_drive","other"],"type":"string"},"ExternalDocumentListQuery":{"properties":{"article_id":{"description":"Narrow to one article's attachments.","format":"uuid","type":["string","null"]}},"type":"object"},"ExternalDocumentMutationResponse":{"allOf":[{"$ref":"#/components/schemas/KbExternalDocument"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2) for external documents."},"ExternalLink":{"description":"A typed external URL bound to a subject (vendor / PO / PO line /\ndevice asset). No polymorphic FK — the write path checks that the\nsubject row exists in the org.","properties":{"carrier":{"description":"Carrier that owns a tracking page (kind = tracking).","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"kind":{"$ref":"#/components/schemas/ExternalLinkKind"},"label":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"subject_id":{"format":"uuid","type":"string"},"subject_kind":{"$ref":"#/components/schemas/ExternalLinkSubject"},"updated_at":{"format":"date-time","type":"string"},"url":{"type":"string"}},"required":["id","org_id","subject_kind","subject_id","kind","url","created_at","updated_at"],"type":"object"},"ExternalLinkKind":{"enum":["order_portal","tracking","invoice","other"],"type":"string"},"ExternalLinkSubject":{"enum":["vendor","purchase_order","purchase_order_line","device_asset"],"type":"string"},"FavoriteRequest":{"properties":{"board_id":{"format":"uuid","type":"string"},"favorite":{"type":"boolean"}},"required":["board_id","favorite"],"type":"object"},"FeeSpec":{"description":"Fixed-fee schedule entry spec.","properties":{"amount_minor":{"format":"int64","type":"integer"},"due_on":{"format":"date","type":"string"},"label":{"type":"string"},"notes":{"type":["string","null"]}},"required":["label","amount_minor","due_on"],"type":"object"},"FeedUpsertRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first mutation parameters (IF1)."},{"properties":{"client_id":{"format":"uuid","type":"string"},"external_id":{"type":"string"},"identity_keys":{"description":"CI3 deterministic identity signals (source GUID, serial, MACs, ...)."},"layout_id":{"format":"uuid","type":["string","null"]},"name":{"type":"string"},"observed_fields":{"description":"CI5 observed tier: feed-written, churns with its source."},"payload_ref":{"type":["string","null"]},"source_guid":{"type":["string","null"]},"source_system":{"type":"string"},"tag":{"description":"Display defaults for a freshly created canonical item.","type":"string"}},"required":["source_system","external_id","client_id","tag","name"],"type":"object"}],"description":"The feeder upsert input — lane 8's seam (NinjaOne / IT Glue / Auvik)."},"FeedUpsertResponse":{"allOf":[{"$ref":"#/components/schemas/DeviceAsset"},{"properties":{"auto_merged":{"description":"Auto-merged at/above the org confidence threshold.","type":"boolean"},"contradictions":{"description":"Observed values that contradict a curated value (never applied).","items":{"type":"string"},"type":"array"},"created_item":{"description":"A fresh canonical row was created by THIS sync (no twin risk).","type":"boolean"},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"queued_candidates":{"description":"Reviewable candidates queued for a human (visible evidence).","items":{"$ref":"#/components/schemas/CiMergeCandidate"},"type":"array"},"resolved_existing":{"description":"An existing item was deterministically resolved through identity\nkeys; no second row exists for this source record.","type":"boolean"},"source_link":{"$ref":"#/components/schemas/ConfigSourceLink"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): item fields flattened at the top\nlevel plus the additive feed-sync evidence and `mutation` metadata."},"FieldDefView":{"description":"Wire view of one field definition; choice kinds carry their closed\noption set so generated clients can render editors directly.","properties":{"board_id":{"description":"Owning board (`None` = org-wide; v1 always creates board-scoped).","format":"uuid","type":["string","null"]},"entity":{"type":"string"},"id":{"format":"uuid","type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"options":{"description":"Closed option set for select/multiselect; empty otherwise.","items":{"type":"string"},"type":"array"},"org_id":{"format":"uuid","type":"string"},"required":{"type":"boolean"},"sort_order":{"format":"int32","type":"integer"}},"required":["id","org_id","entity","name","kind","options","required","sort_order"],"type":"object"},"FieldKindWire":{"description":"The definition kinds (mirror of migration 0009's CHECK set). Kept as a\nstring-newtype wire enum so unknown kinds fail validation, not deserialization.","enum":["text","number","date","select","multiselect","bool","url","user"],"type":"string"},"FieldResolution":{"description":"Per-field resolution: which side's value wins when the tickets\ndisagree. `\"survivor\"` (the default) keeps the survivor's value.","enum":["survivor","source"],"type":"string"},"FilterOp":{"description":"Filter operators supported by the v1 compiled reports. Operand typing is\nenforced through [`ValueType`] matches in the builder, so e.g. a bool\noperand can never reach a timestamp column. The serde encoding is the\n`snake_case` slug (`lt_eq`, `is_null`, …) stored specs carry.","enum":["eq","not_eq","lt","lt_eq","gt","gt_eq","is_null","is_not_null"],"type":"string"},"FilterValue":{"description":"A typed filter operand. The variant must match the catalog\n[`ValueType`] of the filtered column (`Int` covers both Int and BigInt\ncolumns) — enforced by [`ReportSpec::validate`], so a mismatched\noperand can never reach a bind. The serde encoding is the externally\ntagged slug (`{\"text\": \"p1\"}`, `{\"bool\": true}`).","oneOf":[{"properties":{"uuid":{"format":"uuid","type":"string"}},"required":["uuid"],"type":"object"},{"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"},{"description":"RFC 3339 instant (chrono serde encoding).","properties":{"timestamp":{"description":"RFC 3339 instant (chrono serde encoding).","format":"date-time","type":"string"}},"required":["timestamp"],"type":"object"},{"description":"`YYYY-MM-DD`.","properties":{"date":{"description":"`YYYY-MM-DD`.","format":"date","type":"string"}},"required":["date"],"type":"object"},{"properties":{"int":{"format":"int64","type":"integer"}},"required":["int"],"type":"object"},{"properties":{"bool":{"type":"boolean"}},"required":["bool"],"type":"object"}]},"FixEntryRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"description":{"description":"The description to write. The server will only take text the approver\nsaw: the factual suggestion it offered for this entry.","type":["string","null"]},"minutes":{"description":"Whole minutes to take off the END of the entry's window, not a new\ntotal and not a delta on the duration. An overlap is a fact about when\nthe work happened, so the fix shortens the window and the duration\nfollows it. A trim only ever shortens: lengthening what a client is\nbilled is not an approver's call from a queue.","format":"int32","type":["integer","null"]},"note":{"description":"Free-text context recorded on the decision trail.","type":["string","null"]}},"type":"object"}],"description":"POST /api/time-entries/:id/fix — an approver applies one verdict's fix\nto an entry that is still in the queue. At least one of `description` /\n`minutes` is required; the entry stays `submitted` afterwards, because\nclearing the objection is not approving it."},"FixEntryResponse":{"allOf":[{"$ref":"#/components/schemas/TimeEntry"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): the entry as it now reads, plus the\nundo substrate. The client reconciles from the snapshot and never has to\nguess what the server did to the minutes."},"ForkReplySnippetRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"name":{"type":"string"}},"required":["name"],"type":"object"}]},"ForkRoleRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key. Absent\nin legacy clients — purely additive. (Creates carry no version.)"},{"properties":{"name":{"description":"Name of the custom copy. Must not collide with an existing role\nname (typed 409 `role_name_taken`).","type":"string"}},"required":["name"],"type":"object"}]},"ForkSecretFolderRequest":{"properties":{"client_id":{"format":"uuid","type":"string"}},"required":["client_id"],"type":"object"},"ForkTemplateRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key."},{"properties":{"name":{"type":"string"},"slug":{"type":"string"}},"required":["slug","name"],"type":"object"}]},"FxRateEntry":{"description":"One `fx_rates` row: append-only; corrections add rows, never rewrite.","properties":{"as_of_date":{"type":"string"},"base_currency":{"description":"1 base major unit buys `rate` quote major units.","type":"string"},"captured_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"quote_currency":{"type":"string"},"rate":{"description":"Stored-precision decimal string (\"17.850000000000\").","type":"string"},"source":{"description":"'manual' for the built-in provider; managed feeds stamp their own.","type":"string"}},"required":["id","org_id","base_currency","quote_currency","as_of_date","rate","source","captured_at","created_by"],"type":"object"},"FxReconciliationRow":{"description":"One `ops.v1_fx_reconciliation` row — the FX deliverable to the books\n(FX7): original amount, rate, source/date, base equivalent, sync\ntarget. The core records conversions that happened; realized/unrealized\ngain-loss stays in the accounting system.","properties":{"base_amount_minor":{"format":"int64","type":["integer","null"]},"fact_at":{"format":"date-time","type":["string","null"]},"fact_id":{"format":"uuid","type":"string"},"fx_rate_date":{"type":"string"},"fx_rate_used":{"type":["string","null"]},"fx_source":{"type":["string","null"]},"invoice_id":{"format":"uuid","type":"string"},"kind":{"description":"'expense' | 'cost_line' | 'invoice_issue' | 'labor_pricing' |\n'inventory_receipt' | 'payment'.","type":"string"},"org_id":{"format":"uuid","type":"string"},"original_amount_minor":{"format":"int64","type":["integer","null"]},"original_currency":{"type":["string","null"]},"sync_target":{"description":"The accounting export target ('none' until a deployment configures\none — qbo-export-sync / xero-export-sync are per-deployment).","type":"string"},"ticket_id":{"format":"uuid","type":"string"}},"required":["kind","fact_id","org_id","ticket_id","invoice_id","fx_rate_date","sync_target"],"type":"object"},"GapDayView":{"properties":{"activity":{"type":"boolean"},"day":{"description":"Local calendar day (YYYY-MM-DD).","type":"string"},"expected_minutes":{"format":"int64","type":"integer"},"has_gap":{"type":"boolean"},"logged_minutes":{"format":"int64","type":"integer"},"nudge_sent_at":{"description":"When the EOD run nudged for this day, if it did.","format":"date-time","type":["string","null"]},"unaccounted_minutes":{"format":"int64","type":"integer"}},"required":["day","activity","logged_minutes","expected_minutes","has_gap","unaccounted_minutes"],"type":"object"},"GapReportQuery":{"properties":{"from":{"description":"Inclusive local-day start (YYYY-MM-DD). Defaults to yesterday.","format":"date","type":["string","null"]},"to":{"description":"Inclusive local-day end (YYYY-MM-DD). Defaults to `from`.","format":"date","type":["string","null"]},"user_id":{"description":"Manager report only: whose days. Requires owner/admin.","format":"uuid","type":["string","null"]}},"type":"object"},"GapReportResponse":{"properties":{"days":{"items":{"$ref":"#/components/schemas/GapDayView"},"type":"array"},"expected_minutes":{"format":"int64","type":"integer"},"timezone":{"type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","timezone","expected_minutes","days"],"type":"object"},"GarageView":{"properties":{"admin_api_docs_url":{"type":"string"},"bootstrap_commands":{"description":"The documented bootstrap sequence, verbatim.","items":{"type":"string"},"type":"array"},"container_paths":{"description":"Mount points inside the Garage container.","items":{"type":"string"},"type":"array"},"s3_bucket":{"type":["string","null"]},"s3_endpoint":{"description":"S3 endpoint the API is configured against, when S3 mode is on.","type":["string","null"]},"volumes":{"description":"Named volumes in the shipped compose file (the compose project\nprefix applies at runtime).","items":{"type":"string"},"type":"array"}},"required":["volumes","container_paths","bootstrap_commands","admin_api_docs_url"],"type":"object"},"GroupMutation":{"properties":{"group":{"$ref":"#/components/schemas/SchedulingGroup"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["group","mutation"],"type":"object"},"HealthResponse":{"properties":{"status":{"type":"string"}},"required":["status"],"type":"object"},"HeartbeatRequest":{"description":"POST /api/presence/heartbeat body. One heartbeat per viewer per entity\ninstance every ~30s; entries expire after 90s without one (TTL is\nserver-side, see `realtime::presence`).","properties":{"editing":{"description":"True while the caller is actively editing (vs. just viewing).","type":"boolean"},"entity":{"description":"Entity family (e.g. `\"ticket\"`).","type":"string"},"entity_id":{"description":"The entity instance id.","type":"string"}},"required":["entity","entity_id"],"type":"object"},"HeldMessageView":{"description":"One held message as the triage queue renders it.","properties":{"body_text":{"description":"The message body as it would appear in the ticket's comment.","type":["string","null"]},"candidate_ticket_id":{"description":"The ticket routing resolved. Accepting without naming a ticket uses it.","format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"from_address":{"type":"string"},"from_name":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"proposal":{"description":"The engine's proposal, present under `shadow` and `autonomous`."},"reason":{"description":"Why it is held: `sender_not_participant` today.","type":["string","null"]},"rfc_message_id":{"type":"string"},"subject":{"type":["string","null"]}},"required":["id","rfc_message_id","from_address","created_at"],"type":"object"},"HeldMessagesView":{"properties":{"holds":{"items":{"$ref":"#/components/schemas/HeldMessageView"},"type":"array"},"level":{"description":"The org's level, so the queue can say why these messages are waiting.","type":"string"}},"required":["level","holds"],"type":"object"},"InboundSettingsResponse":{"properties":{"catchall_address":{"type":["string","null"]},"catchall_board_id":{"format":"uuid","type":["string","null"]},"enabled":{"type":"boolean"}},"required":["enabled"],"type":"object"},"IncidentView":{"description":"The storm group behind an incident ticket.","properties":{"alert_count":{"description":"How many alerts have collapsed into this ticket so far.","format":"int32","type":"integer"},"first_alert_at":{"type":"string"},"last_alert_at":{"type":"string"},"ticket_id":{"format":"uuid","type":"string"},"ticket_number":{"type":"string"}},"required":["ticket_id","ticket_number","alert_count","first_alert_at","last_alert_at"],"type":"object"},"IngestAlertRequest":{"properties":{"attributes":{"description":"Provider-specific structured context (must be a JSON object)."},"correlation_key":{"description":"Storm grouping key; defaults to `dedup_key` when absent so\nuncorrelated alerts each anchor their own incident.","type":["string","null"]},"dedup_key":{"description":"Provider's per-event identity. REQUIRED: this is the replay gate.","type":"string"},"description":{"type":["string","null"]},"occurred_at":{"description":"When the condition started (defaults to receipt time).","format":"date-time","type":["string","null"]},"severity":{"$ref":"#/components/schemas/AlertSeverity"},"title":{"type":"string"}},"required":["dedup_key","title","severity"],"type":"object"},"IngestResponse":{"description":"POST /api/alerts/ingest — outcome of one signed delivery.","properties":{"alert":{"$ref":"#/components/schemas/AlertView"},"incident":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/IncidentView","description":"Present for `incident_created` and `collapsed`; `None` when the\nanchor ticket vanished before a replay answered."}]},"status":{"description":"`incident_created` (this alert anchored a NEW incident ticket),\n`collapsed` (folded into the still-open storm incident), or\n`duplicate` (replayed dedup_key — the stored alert is returned,\nnothing was written).","type":"string"}},"required":["status","alert"],"type":"object"},"InstallPluginRequest":{"properties":{"granted_capabilities":{"description":"Capability keys granted at install — a subset of the pinned\nmanifest's declared set. Unknown keys are rejected (closed\nvocabulary).","items":{"type":"string"},"type":"array"},"slug":{"type":"string"},"version":{"description":"Pin the adopted version; defaults to the entry's latest.","type":["string","null"]}},"required":["slug"],"type":"object"},"InstallSettingsView":{"properties":{"config":{},"settings_schema":{"description":"The pinned manifest's declared JSON-Schema (the render source)."},"slug":{"type":"string"},"version":{"type":"string"}},"required":["slug","version","config"],"type":"object"},"InstallView":{"properties":{"artifact_kind":{"description":"The governance profile from the PINNED manifest (ADR 0009 §6):\n`wasm` | `http_connector` — the roster-tab discriminator.","type":"string"},"credential_bindings":{"description":"The credential bindings this install holds (Wave 53 lane 12): which\nprofile wears which vault record, as which header. The record is named\nby title only — [`CredentialBindingView`] has no member that could\ncarry a field or a value.","items":{"$ref":"#/components/schemas/CredentialBindingView"},"type":"array"},"effective_permissions":{"description":"The effective permission keys the grants resolve onto (the\nEXISTING matrix vocabulary — `tickets:read`, `financial:read`,\n…). Informational; enforcement re-derives from the same matrix.","items":{"type":"string"},"type":"array"},"egress_profiles":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"description":"The egress profiles the PINNED manifest declares, as\n`profile -> allowlisted https scopes`. Shown beside the bindings\nbecause a profile name on its own is not consent: the operator has to\nsee which hosts that name reaches. Empty for an install whose manifest\ndeclares none.","propertyNames":{"type":"string"},"type":"object"},"granted_capabilities":{"items":{"type":"string"},"type":"array"},"id":{"format":"uuid","type":"string"},"installed_at":{"format":"date-time","type":"string"},"module_digest":{"description":"The digest pin: sha256 of the module this install adopted, or empty for\na declarative-only install. An operator can see exactly which bytes the\ninstall names, and the executor is handed the same value.","type":"string"},"name":{"type":"string"},"previous_version":{"description":"The version a rollback would return to, when one is parked.","type":["string","null"]},"revoked_reason":{"description":"Why the install was revoked, when it is.","type":["string","null"]},"slug":{"type":"string"},"status":{"$ref":"#/components/schemas/PluginInstallStatus"},"version":{"type":"string"}},"required":["id","slug","name","version","status","granted_capabilities","artifact_kind","effective_permissions","module_digest","egress_profiles","credential_bindings","installed_at"],"type":"object"},"InstantiateTemplateRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion (of the newly created project). Absent in legacy clients —\npurely additive."},{"properties":{"assignee_map":{"additionalProperties":{"format":"uuid","type":"string"},"description":"Resolves template `assignee_key`s to user ids at instantiation\ntime; unmapped keys simply leave those tasks unassigned.","propertyNames":{"type":"string"},"type":"object"},"client_id":{"format":"uuid","type":["string","null"]},"due_date":{"format":"date","type":["string","null"]},"name":{"type":"string"},"start_date":{"format":"date","type":["string","null"]}},"required":["name"],"type":"object"}]},"IntegrationLinkStatus":{"description":"Per-connector link state as data (linked/unlinked + last touch).","properties":{"connector_type":{"type":"string"},"linked_at":{"format":"date-time","type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["connector_type","linked_at","updated_at"],"type":"object"},"InviteInfo":{"description":"The one-time setup token an admin delivers to the invitee (shown once;\nonly its sha-256 is stored).","properties":{"expires_at":{"type":"string"},"setup_token":{"type":"string"}},"required":["setup_token","expires_at"],"type":"object"},"InviteUserRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key."},{"properties":{"display_name":{"type":"string"},"email":{"type":"string"},"role":{"description":"Builtin org role preset: owner, admin, dispatcher, senior_tech, technician, bookkeeper, auditor, read_only.","type":"string"},"timezone":{"type":["string","null"]}},"required":["email","display_name","role"],"type":"object"}]},"Invoice":{"description":"The billing document. Derives from exactly one contract; `currency`\nand `total_minor` come from the contract's evaluation (line sum).","properties":{"base_total_minor":{"format":"int64","type":["integer","null"]},"client_id":{"format":"uuid","type":"string"},"contract_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"currency":{"description":"ISO 4217 code copied from the contract; every amount uses it.","type":"string"},"due_on":{"format":"date","type":"string"},"dunning_note":{"type":["string","null"]},"dunning_state":{"$ref":"#/components/schemas/DunningState"},"dunning_updated_at":{"format":"date-time","type":["string","null"]},"fx_rate_date":{"type":["string","null"]},"fx_rate_used":{"description":"FX conversion snapshot (ADR 0002 lock moment: invoice issue) —\nlocked once at the issue leg when currency ≠ org base; immutable\nafterwards. `base_total_minor` is the issue-time base equivalent.","type":["string","null"]},"fx_source":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"issued_at":{"format":"date","type":"string"},"notes":{"type":["string","null"]},"number":{"description":"Human invoice number, unique per org (e.g. \"INV-000042\").","type":"string"},"org_id":{"format":"uuid","type":"string"},"period_from":{"description":"The billed period, when the contract type has one (recurring /\nretainer / T&M window slice); `None` for fixed-fee schedules.","format":"date","type":["string","null"]},"period_to":{"format":"date","type":["string","null"]},"po_number":{"description":"Client purchase-order reference (Wave 39 F): copied from the\nproducing contract at run time so the document carries what the\nclient's AP system matches against. TAX STAYS OUT by design —\nthe 0036 boundary stands (tax is an accounting-export concern).","type":["string","null"]},"run_id":{"description":"The run that created this invoice (`None` for future manual invoices).","format":"uuid","type":["string","null"]},"status":{"$ref":"#/components/schemas/InvoiceStatus"},"total_minor":{"description":"Sum of line amounts, minor units.","format":"int64","type":"integer"},"total_paid_minor":{"description":"Recorded payment total, minor units.","format":"int64","type":"integer"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","client_id","contract_id","number","status","currency","issued_at","due_on","total_minor","total_paid_minor","dunning_state","created_at","updated_at"],"type":"object"},"InvoiceCandidateView":{"properties":{"amount_minor":{"format":"int64","type":"integer"},"client_id":{"format":"uuid","type":["string","null"]},"currency":{"type":"string"},"description":{"type":"string"},"line_id":{"format":"uuid","type":"string"},"source":{"description":"\"cost\" | \"expense\"","type":"string"},"ticket_id":{"format":"uuid","type":"string"},"ticket_number":{"description":"The ticket's human number and title. Present so the ledger can name it.","type":"string"},"ticket_title":{"type":"string"}},"required":["ticket_id","ticket_number","ticket_title","source","line_id","description","amount_minor","currency"],"type":"object"},"InvoiceDetail":{"allOf":[{"$ref":"#/components/schemas/Invoice"},{"properties":{"lines":{"items":{"$ref":"#/components/schemas/InvoiceLine"},"type":"array"},"payments":{"items":{"$ref":"#/components/schemas/InvoicePayment"},"type":"array"}},"required":["lines","payments"],"type":"object"}],"description":"The invoice aggregate: document, lines, and payment receipts."},"InvoiceLine":{"description":"One persisted billing line; mirrors the engine's `BillingCandidateLine`\n(kind reuse via [`crate::entities::BillingLineKind`]) minus the\ndenormalized contract/client names.","properties":{"amount_minor":{"format":"int64","type":"integer"},"base_amount_minor":{"format":"int64","type":["integer","null"]},"contract_id":{"format":"uuid","type":"string"},"description":{"type":"string"},"fx_rate_date":{"type":["string","null"]},"fx_rate_used":{"description":"FX conversion snapshot (ADR 0002 lock moment: time-entry costing —\nthe run pricing labor into this line) when currency ≠ org base.","type":["string","null"]},"fx_source":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"invoice_id":{"format":"uuid","type":"string"},"kind":{"$ref":"#/components/schemas/BillingLineKind"},"org_id":{"format":"uuid","type":"string"},"period_end":{"format":"date","type":"string"},"period_start":{"format":"date","type":"string"},"position":{"format":"int32","type":"integer"},"quantity_minutes":{"description":"Labor minutes on the line (T&M / overage); 0 for amount lines.","format":"int64","type":"integer"},"unit_amount_minor":{"format":"int64","type":"integer"}},"required":["id","org_id","invoice_id","contract_id","kind","description","period_start","period_end","quantity_minutes","unit_amount_minor","amount_minor","position"],"type":"object"},"InvoiceListQuery":{"properties":{"client_id":{"format":"uuid","type":["string","null"]},"contract_id":{"format":"uuid","type":["string","null"]},"dunning_state":{"description":"Exact dunning state:\n`none|reminder_sent|overdue|final_notice|collections`.","type":["string","null"]},"status":{"description":"Exact status: `draft|issued|paid|void`.","type":["string","null"]}},"type":"object"},"InvoiceMutationResponse":{"allOf":[{"$ref":"#/components/schemas/Invoice"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2) for invoice mutations."},"InvoicePayment":{"description":"A payment receipt recorded through the `PaymentProvider` seam. The\nprovider stays a string (`none` in v1 — the built-in provider approves\nlocally without external PSP calls).","properties":{"amount_minor":{"format":"int64","type":"integer"},"base_amount_minor":{"format":"int64","type":["integer","null"]},"created_by":{"format":"uuid","type":["string","null"]},"currency":{"type":"string"},"fx_rate_date":{"type":["string","null"]},"fx_rate_used":{"description":"FX conversion snapshot (ADR 0002 lock moment: payment settlement) —\nlocked once at receipt when currency ≠ org base; immutable after.","type":["string","null"]},"fx_source":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"invoice_id":{"format":"uuid","type":"string"},"notes":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"provider":{"type":"string"},"provider_ref":{"type":["string","null"]},"received_at":{"format":"date-time","type":"string"}},"required":["id","org_id","invoice_id","amount_minor","currency","provider","received_at"],"type":"object"},"InvoiceRun":{"description":"The invoice-run batch record: one evaluation sweep over a window and\nthe roll-up of the invoices it created.","properties":{"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"currency":{"description":"Currency of the summed total; `None` when the run created no\ninvoices (mixed currencies are rejected before any write).","type":["string","null"]},"due_in_days":{"format":"int32","type":"integer"},"id":{"format":"uuid","type":"string"},"invoice_count":{"format":"int32","type":"integer"},"number":{"description":"Human run number, unique per org (e.g. \"RUN-000007\").","type":"string"},"org_id":{"format":"uuid","type":"string"},"period_from":{"format":"date","type":"string"},"period_to":{"format":"date","type":"string"},"total_minor":{"format":"int64","type":"integer"}},"required":["id","org_id","number","period_from","period_to","invoice_count","total_minor","due_in_days","created_at"],"type":"object"},"InvoiceRunResponse":{"description":"Instant-first mutation response (IF2) for an invoice run.","properties":{"invoices":{"items":{"$ref":"#/components/schemas/IssuedInvoice"},"type":"array"},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"run":{"$ref":"#/components/schemas/InvoiceRun"}},"required":["run","invoices","mutation"],"type":"object"},"InvoiceRunSignoff":{"description":"The reviewer sign-off recorded per invoice run (Wave 39 V8): the\nclose checklist a human attested before the run fired. One row per\nrun; the run-create seam refuses an unsigned run.","properties":{"entries_dispositioned":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"preview_seen":{"type":"boolean"},"reviewer_id":{"format":"uuid","type":["string","null"]},"reviewer_note":{"type":["string","null"]},"run_id":{"format":"uuid","type":"string"},"signed_at":{"format":"date-time","type":"string"},"zero_unclassified":{"type":"boolean"}},"required":["id","org_id","run_id","entries_dispositioned","zero_unclassified","preview_seen","signed_at"],"type":"object"},"InvoiceStatus":{"description":"Document lifecycle of an invoice.","enum":["draft","issued","paid","void"],"type":"string"},"IssueCreditNoteRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"client_id":{"format":"uuid","type":"string"},"currency":{"description":"ISO 4217 code — required for free-standing credits; when\n`invoice_id` is set it must match the invoice's currency.","type":["string","null"]},"invoice_id":{"description":"The invoice the credit is raised against (issuing context). When\nset, the credit's currency is the invoice's.","format":"uuid","type":["string","null"]},"reason":{"type":["string","null"]},"total_minor":{"description":"Credit face value, minor units (> 0).","format":"int64","type":"integer"}},"required":["client_id","total_minor"],"type":"object"}],"description":"POST /api/credit-notes body."},"IssueLoanRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"agreement_ref":{"description":"Free-form contract/agreement reference when no contract row exists.","type":["string","null"]},"client_id":{"format":"uuid","type":"string"},"contract_id":{"description":"Agreement anchor where a contract row exists.","format":"uuid","type":["string","null"]},"expected_return_at":{"type":"string"},"is_lease":{"description":"When true the lease terms below are required and stamp the issued\nasset's 0061 rental fields.","type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/LoanItemRequest"},"type":"array"},"lease_cost_minor":{"format":"int64","type":["integer","null"]},"lease_currency":{"type":["string","null"]},"lease_cycle":{"description":"monthly | quarterly | annual.","type":["string","null"]},"notes":{"type":["string","null"]},"out_at":{"description":"Defaults to now.","type":["string","null"]}},"required":["client_id","expected_return_at","items"],"type":"object"}],"description":"POST /api/client-loans."},"IssuedInvoice":{"description":"One issued invoice: the document plus its lines.","properties":{"invoice":{"$ref":"#/components/schemas/Invoice"},"lines":{"items":{"$ref":"#/components/schemas/InvoiceLine"},"type":"array"}},"required":["invoice","lines"],"type":"object"},"JourneyLegView":{"properties":{"distance_meters":{"format":"int64","type":["integer","null"]},"error":{"type":["string","null"]},"from_kind":{"type":"string"},"from_label":{"type":["string","null"]},"minutes":{"format":"int64","type":["integer","null"]},"provider":{"type":["string","null"]},"quality":{"type":["string","null"]},"to_appointment_id":{"format":"uuid","type":"string"}},"required":["from_kind","to_appointment_id"],"type":"object"},"JourneyStartMutationResponse":{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"start_point":{"$ref":"#/components/schemas/StartPointView"},"start_point_id":{"format":"uuid","type":"string"}},"required":["start_point","start_point_id","mutation"],"type":"object"},"JourneyStartQuery":{"properties":{"date":{"description":"The day; defaults to today (UTC).","format":"date","type":["string","null"]},"user_id":{"description":"Whose journey; defaults to the caller (another user requires\nowner/admin).","format":"uuid","type":["string","null"]}},"type":"object"},"JourneyStartView":{"properties":{"date":{"format":"date","type":"string"},"explicit":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/StartPointView","description":"The dispatcher's explicit row, when one exists."}]},"resolved":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/StartPointView","description":"The start point travel estimates will run from (explicit row when\nset, else the auto-suggestion)."}]},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","date"],"type":"object"},"JourneyStopView":{"properties":{"appointment_id":{"format":"uuid","type":"string"},"ends_at":{"format":"date-time","type":"string"},"label":{"type":["string","null"]},"leg":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/JourneyLegView"}]},"place":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TravelPlaceView"}]},"starts_at":{"format":"date-time","type":"string"},"travel_distance_meters":{"format":"int32","type":["integer","null"]},"travel_from":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TravelOrigin"}]},"travel_minutes":{"description":"Booked travel minutes into this stop (the appointment field).","format":"int64","type":"integer"},"travel_provider":{"type":["string","null"]},"travel_quality":{"type":["string","null"]}},"required":["appointment_id","starts_at","ends_at","travel_minutes"],"type":"object"},"KbArticle":{"description":"A KB article.","properties":{"body":{"type":"string"},"category_id":{"description":"Survives category deletion (the FK is ON DELETE SET NULL).","format":"uuid","type":["string","null"]},"client_visible":{"description":"The article half of the client-facing gate (portal v2 honors it).","type":"boolean"},"content_jsonb":{"description":"Canonical rich content (ADR 0007): the versioned envelope\n`{\"schema_ver\": 1, \"doc\": <tiptap>}` — `None` for legacy rows the\n0077 backfill has not seen (reads fall back to `body`)."},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"slug":{"description":"Stable per-org handle (unique).","type":"string"},"status":{"$ref":"#/components/schemas/KbArticleStatus"},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"updated_by":{"format":"uuid","type":["string","null"]}},"required":["id","org_id","title","slug","status","client_visible","created_at","updated_at"],"type":"object"},"KbArticleStatus":{"description":"Article lifecycle. Closed vocabulary so filtering and the client-facing\nrule stay deterministic: `draft` (staff authoring), `published`\n(readable, searchable), `archived` (retired, out of the active tree —\ncontent edits are refused while archived; unarchive to edit).","enum":["draft","published","archived"],"type":"string"},"KbCategory":{"description":"A KB category: grouping + ordering for the article tree.","properties":{"client_visible":{"description":"The category half of the client-facing gate: an article is visible\nto portal clients only when this AND the article flag are set (and\nthe article is published).","type":"boolean"},"created_at":{"format":"date-time","type":"string"},"description":{"type":"string"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"position":{"format":"int32","type":"integer"},"slug":{"description":"Stable per-org handle (unique); portals and imports reference this,\nnever the row id.","type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","name","slug","position","client_visible","created_at","updated_at"],"type":"object"},"KbEmbed":{"description":"One embed instance attached to a KB article. Its sanction source is\nthe registry row resolved at write time (`provider_slug` snapshots the\nhandle; `provider_id` clears when the provider row is deleted, which\nrenders the embed as blocked until re-resolved).","properties":{"article_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"provider_id":{"format":"uuid","type":["string","null"]},"provider_slug":{"type":["string","null"]},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"url":{"type":"string"}},"required":["id","org_id","article_id","url","created_at","updated_at"],"type":"object"},"KbEmbedProvider":{"description":"An oEmbed allowlist entry. `org_id` is `None` for the platform-shipped\nsystem rows (`is_system`, immutable from the API); org rows are the\nadmin-managed registry extensions.","properties":{"created_at":{"format":"date-time","type":"string"},"enabled":{"type":"boolean"},"host_patterns":{"description":"Exact host names this provider accepts.","items":{"type":"string"},"type":"array"},"id":{"format":"uuid","type":"string"},"is_system":{"type":"boolean"},"name":{"type":"string"},"oembed_endpoint":{"description":"oEmbed endpoint template; `{url}` is substituted once, and the\nsubstituted value is always percent-encoded before the fetch.","type":"string"},"org_id":{"format":"uuid","type":["string","null"]},"slug":{"description":"Stable per-org handle (unique; system rows use the same space).","type":"string"},"updated_at":{"format":"date-time","type":"string"},"url_regex":{"description":"Regex over the resource URL (Rust regex syntax); validated at\nwrite time by the caller.","type":"string"}},"required":["id","name","slug","host_patterns","url_regex","oembed_endpoint","enabled","is_system","created_at","updated_at"],"type":"object"},"KbExternalDocument":{"description":"A linked external document (KB2): a SharePoint/OneDrive/Drive URL with\ntitle, permission note, and the last link-check result. `article_id`\nattaches it to an article — client-facing exposure rides that\narticle's client-visible gate; standalone rows are staff-only.","properties":{"article_id":{"format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"last_verified_at":{"type":["string","null"]},"last_verified_status":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"permission_note":{"type":"string"},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"url":{"type":"string"},"url_kind":{"$ref":"#/components/schemas/ExternalDocUrlKind"}},"required":["id","org_id","title","url","url_kind","created_at","updated_at"],"type":"object"},"KitAvailabilityMemberView":{"properties":{"available_units":{"format":"int64","type":"integer"},"item_name":{"type":"string"},"ready":{"type":"boolean"},"required_units":{"format":"int64","type":"integer"},"stock_item_id":{"format":"uuid","type":"string"}},"required":["stock_item_id","item_name","required_units","available_units","ready"],"type":"object"},"KitAvailabilityQuery":{"description":"GET /api/tool-kits/{id}/availability?location_id=","properties":{"location_id":{"format":"uuid","type":["string","null"]}},"type":"object"},"KitAvailabilityResponse":{"properties":{"kit_id":{"format":"uuid","type":"string"},"location_id":{"format":"uuid","type":["string","null"]},"members":{"items":{"$ref":"#/components/schemas/KitAvailabilityMemberView"},"type":"array"},"ready":{"description":"True when every member has enough in-stock units at the location.","type":"boolean"}},"required":["kit_id","ready","members"],"type":"object"},"KitDetailResponse":{"properties":{"kit":{"$ref":"#/components/schemas/ToolKitView"},"members":{"items":{"$ref":"#/components/schemas/KitMemberView"},"type":"array"}},"required":["kit","members"],"type":"object"},"KitListQuery":{"properties":{"active":{"description":"`true` = active kits only; absent = all.","type":["boolean","null"]},"limit":{"format":"int64","type":["integer","null"]}},"type":"object"},"KitMemberView":{"properties":{"id":{"format":"uuid","type":"string"},"is_serialized":{"type":"boolean"},"is_tool":{"type":"boolean"},"item_name":{"type":"string"},"item_sku":{"type":["string","null"]},"qty_milli":{"format":"int64","type":"integer"},"stock_item_id":{"format":"uuid","type":"string"}},"required":["id","stock_item_id","item_name","qty_milli","is_tool","is_serialized"],"type":"object"},"KitMutationWithMembersResponse":{"properties":{"kit":{"$ref":"#/components/schemas/ToolKitView"},"members":{"items":{"$ref":"#/components/schemas/KitMemberView"},"type":"array"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["kit","members","mutation"],"type":"object"},"LabelBarcode":{"properties":{"data":{"type":"string"},"height_mm":{"format":"double","type":"number"},"token":{"type":"string"},"width_mm":{"description":"Band width: the FULL content width (the QR sits above it, never over it).","format":"double","type":"number"},"y_mm":{"format":"double","type":"number"}},"required":["token","data","y_mm","height_mm","width_mm"],"type":"object"},"LabelEntityView":{"description":"Which entity a rendered label names.","properties":{"deep_link":{"description":"The canonical deep link — the QR payload, and what `POST\n/api/scan/resolve` hands back for the printed label.","type":"string"},"display_name":{"type":"string"},"entity_id":{"format":"uuid","type":"string"},"purpose":{"type":"string"}},"required":["purpose","entity_id","display_name","deep_link"],"type":"object"},"LabelLine":{"properties":{"font_mm":{"description":"The effective font size for this line: `0.72 × height_mm` when the\ntext fits, shrunk (floor `0.45 × height_mm`) until it does. The ZPL\n`^A0N` height and the browser preview both use THIS number, so the\nprint and the preview can never disagree on how big the text is.","format":"double","type":"number"},"height_mm":{"description":"The line box height (spacing).","format":"double","type":"number"},"text":{"type":"string"},"token":{"type":"string"},"truncated":{"description":"True when even the smallest font could not fit the text and the tail\nwas cut — surfaced in the UI, never silent.","type":"boolean"},"y_mm":{"format":"double","type":"number"}},"required":["token","text","y_mm","height_mm","font_mm","truncated"],"type":"object"},"LabelQr":{"properties":{"data":{"type":"string"},"size_mm":{"format":"double","type":"number"},"y_mm":{"format":"double","type":"number"}},"required":["data","y_mm","size_mm"],"type":"object"},"LabelRenderResponse":{"description":"The rendered label: geometry in millimetres, the QR and Code128 as\ninline SVG markup (render them as images — never as script), and\noptionally the exact ZPL the desktop shell sends to a Zebra printer.","properties":{"code128_svg":{"type":["string","null"]},"entity":{"$ref":"#/components/schemas/LabelEntityView"},"label":{"$ref":"#/components/schemas/ResolvedLabel"},"qr_svg":{"type":["string","null"]},"sample":{"description":"True when deterministic sample values were rendered (designer preview).","type":"boolean"},"template":{"$ref":"#/components/schemas/LabelTemplateView"},"zpl":{"type":["string","null"]}},"required":["label","entity","sample","template"],"type":"object"},"LabelTemplateMutationResponse":{"allOf":[{"$ref":"#/components/schemas/LabelTemplateView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}]},"LabelTemplateView":{"description":"The template as the API exposes it; `width_mm`/`height_mm` are the\nresolved physical size (preset or custom), so clients never re-derive it.","properties":{"active":{"type":"boolean"},"barcode_token":{"type":["string","null"]},"created_at":{"type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"dpi":{"format":"int32","type":"integer"},"fields":{"items":{"type":"string"},"type":"array"},"height_mm":{"format":"double","type":"number"},"id":{"format":"uuid","type":"string"},"include_qr":{"type":"boolean"},"is_default":{"type":"boolean"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"purpose":{"type":"string"},"size_preset":{"type":"string"},"updated_at":{"type":"string"},"width_mm":{"format":"double","type":"number"}},"required":["id","org_id","name","purpose","size_preset","width_mm","height_mm","dpi","include_qr","fields","is_default","active","created_at","updated_at"],"type":"object"},"LadderStepSpec":{"properties":{"at_pct":{"description":"Fires when consumed budget reaches this percent (1..=500).","format":"int64","type":"integer"},"enabled":{"type":"boolean"},"notify_user_ids":{"items":{"format":"uuid","type":"string"},"type":"array"},"step_index":{"format":"int32","type":"integer"},"trigger_kind":{"description":"'risk' | 'breach' | 'escalate' (notification-center kinds).","type":"string"}},"required":["step_index","trigger_kind","at_pct","notify_user_ids"],"type":"object"},"LayoutField":{"description":"One field in a layout's schema. The `fields` column stores an array of\nthese (as JSON); the API validates document `fields` objects against\nthem.","properties":{"key":{"type":"string"},"label":{"type":"string"},"options":{"description":"Select-type options.","items":{"type":"string"},"type":"array"},"position":{"format":"int32","type":"integer"},"required":{"type":"boolean"},"type":{"description":"text | number | date | bool | select | url | email","type":"string"}},"required":["key","label","type"],"type":"object"},"LayoutMutationResponse":{"allOf":[{"$ref":"#/components/schemas/DocumentLayout"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}]},"LinkBeginResponse":{"properties":{"authorize_url":{"type":"string"}},"required":["authorize_url"],"type":"object"},"LinkCheckResponse":{"allOf":[{"$ref":"#/components/schemas/KbExternalDocument"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"The link-check response: the refreshed row plus the outcome."},"LinkCheckStatus":{"description":"Outcome of a link-check. `unreachable` covers DNS/TLS/HTTP failures —\nthe record exists but could not be verified.","enum":["ok","unreachable"],"type":"string"},"LinkKind":{"description":"Either endpoint of a link. Documents, passwords, assets, and secret\nrecords can link FROM; documents, passwords, clients, tickets, KB\narticles, assets, and secret records can be linked TO (asset kind\nadded in migration 0054; secret kind added in migration 0078 — the\nnative vault joins the cross-entity RecordLink seam, ADR 0006).","enum":["document","password","client","ticket","kb_article","asset","secret"],"type":"string"},"LinkMutationResponse":{"properties":{"link":{"$ref":"#/components/schemas/ExternalLink"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["link","mutation"],"type":"object"},"LinksView":{"properties":{"from":{"items":{"type":"object"},"type":"array"},"rollup":{"$ref":"#/components/schemas/TicketRollup"},"to":{"items":{"type":"object"},"type":"array"}},"required":["from","to","rollup"],"type":"object"},"ListCandidatesQuery":{"properties":{"status":{"description":"pending | auto_merged | accepted | rejected","type":["string","null"]}},"type":"object"},"ListConfigItemsQuery":{"properties":{"client_id":{"format":"uuid","type":["string","null"]},"layout_id":{"format":"uuid","type":["string","null"]},"q":{"type":["string","null"]},"semantic_class":{"description":"CI6 semantic class filter (workstation | server | vm |\nnetwork_appliance | printer | iot).","type":["string","null"]}},"type":"object"},"ListEntityCandidatesQuery":{"properties":{"kind":{"description":"contact | client","type":["string","null"]},"status":{"description":"pending | auto_merged | accepted | rejected | deferred","type":["string","null"]}},"type":"object"},"ListEntriesQuery":{"properties":{"limit":{"format":"int64","type":"integer"},"since":{"format":"date-time","type":["string","null"]},"ticket_id":{"format":"uuid","type":["string","null"]},"user_id":{"format":"uuid","type":["string","null"]}},"type":"object"},"ListOrdersQuery":{"properties":{"project_id":{"format":"uuid","type":["string","null"]},"q":{"type":["string","null"]},"status":{"type":["string","null"]},"vendor_id":{"format":"uuid","type":["string","null"]}},"type":"object"},"ListOrgUsersQuery":{"properties":{"limit":{"description":"Page size (default 50, max 200).","format":"int64","type":["integer","null"]},"offset":{"description":"Offset paging.","format":"int64","type":["integer","null"]}},"type":"object"},"ListRatingsQuery":{"properties":{"artifact_digest":{"type":["string","null"]},"source_url":{"type":["string","null"]}},"type":"object"},"ListTaxonomyQuery":{"properties":{"include_retired":{"description":"Include retired nodes (admin editors + the manifest want them;\npickers default to false).","type":"boolean"}},"type":"object"},"ListTravelDraftsQuery":{"properties":{"limit":{"description":"Page size, clamped to [1, 200].","format":"int64","type":["integer","null"]},"status":{"description":"`draft` | `submitted`.","type":["string","null"]},"user_id":{"format":"uuid","type":["string","null"]}},"type":"object"},"ListVendorsQuery":{"properties":{"include_inactive":{"type":"boolean"},"q":{"type":["string","null"]}},"type":"object"},"LoanItemRequest":{"description":"One device to issue: from org stock (item + optional serial) or an\nexisting fleet asset.","properties":{"device_asset_id":{"format":"uuid","type":["string","null"]},"serial_id":{"format":"uuid","type":["string","null"]},"stock_item_id":{"format":"uuid","type":["string","null"]}},"type":"object"},"LoanListQuery":{"properties":{"client_id":{"format":"uuid","type":["string","null"]},"limit":{"format":"int64","type":["integer","null"]},"overdue_only":{"description":"Only open loans past their expected return.","type":["boolean","null"]},"status":{"description":"open | returned (absent = both).","type":["string","null"]}},"type":"object"},"LoanReturnLineRequest":{"properties":{"line_id":{"format":"uuid","type":"string"},"return_condition":{"description":"good | fair | damaged.","type":["string","null"]},"return_notes":{"type":["string","null"]}},"required":["line_id"],"type":"object"},"LocationListQuery":{"properties":{"active_only":{"type":["boolean","null"]},"kind":{"type":["string","null"]}},"type":"object"},"LoginRequest":{"properties":{"email":{"type":"string"},"password":{"type":"string"}},"required":["email","password"],"type":"object"},"LoginResponse":{"description":"Login outcome: tokens directly, or a short-lived MFA bridge token when\nany second factor is enrolled. Untagged so legacy clients that ignore\n`mfa_required` keep parsing the plain token pair they always got.","oneOf":[{"$ref":"#/components/schemas/TokenPair"},{"properties":{"methods":{"description":"Second factors able to satisfy this login.","items":{"type":"string"},"type":"array"},"mfa_required":{"type":"boolean"},"mfa_token":{"type":"string"}},"required":["mfa_required","mfa_token","methods"],"type":"object"}]},"LoopMessage":{"description":"One conversation message (`system` | `user` | `assistant` | `tool`).","properties":{"content":{"type":"string"},"role":{"type":"string"},"tool_call_id":{"description":"For `tool` messages: the tool call id being answered.","type":["string","null"]},"tool_calls":{"description":"For `assistant` messages that requested tools: the provider-wire\ntool_calls round-trip.","type":["object","null"]}},"required":["role","content"],"type":"object"},"MagicLinkConfirm":{"properties":{"token":{"type":"string"}},"required":["token"],"type":"object"},"MailSettingsResponse":{"properties":{"boards":{"items":{"$ref":"#/components/schemas/BoardRouteView"},"type":"array"},"digest_policy":{"type":"string"},"inbound":{"$ref":"#/components/schemas/InboundSettingsResponse"},"outbound":{"$ref":"#/components/schemas/OutboundSettingsView"},"reply_by_email":{"$ref":"#/components/schemas/ReplyByEmailView"}},"required":["inbound","outbound","boards","reply_by_email","digest_policy"],"type":"object"},"MaintenanceWindow":{"description":"Planned downtime: an absolute UTC interval during which SLA clocks do\nnot consume business-time budget (vendor maintenance, change freezes).","properties":{"ends_at":{"format":"date-time","type":"string"},"name":{"type":"string"},"starts_at":{"format":"date-time","type":"string"}},"required":["name","starts_at","ends_at"],"type":"object"},"ManifestApiKey":{"description":"API-key metadata. `key_hash` (the credential verifier) and the raw key\nmaterial are structurally absent — only the display prefix survives.","properties":{"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"last_used_at":{"format":"date-time","type":["string","null"]},"name":{"type":"string"},"prefix":{"description":"Non-secret display identifier (e.g. `mrl_ab12cd34`).","type":"string"},"revoked_at":{"format":"date-time","type":["string","null"]},"scopes":{"description":"Capability scope strings (\"tickets:read\", …).","items":{"type":"string"},"type":"array"}},"required":["id","name","prefix","scopes","created_by","created_at"],"type":"object"},"ManifestAutomationRule":{"description":"One event-triggered automation rule.","properties":{"actions":{"type":"object"},"board_id":{"description":"Board the rule is scoped to; null = every board in the org.","format":"uuid","type":["string","null"]},"conditions":{"type":"object"},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"subject_kind":{"description":"What the rule's subject is: `ticket` (the historical default) or\n`board` — a rule that fires on ticket-less envelopes (Wave 43\nlane 3). Absent from a pre-lane manifest, so it defaults.","type":"string"},"trigger_event":{"description":"Domain event type from the envelope, e.g. `ticket.created`.","type":"string"}},"required":["id","board_id","name","trigger_event","conditions","actions","enabled"],"type":"object"},"ManifestBoard":{"description":"One board's workflow customization.","properties":{"id":{"format":"uuid","type":"string"},"is_default":{"description":"The org's default intake board.","type":"boolean"},"name":{"type":"string"},"require_checklist_close":{"description":"Pre-close checklist enforcement toggle (workflow discipline flag).","type":"boolean"},"statuses":{"description":"Custom statuses; ALWAYS one per semantic category (defaults-first\nseeding guarantees category coverage, never removed by edits).","items":{"$ref":"#/components/schemas/ManifestBoardStatus"},"type":"array"},"transitions":{"description":"Explicit transition adjacency between this board's statuses. Empty\nmeans the semantic-category floor applies (any→any except identity\nand closed→open), not \"no transitions\".","items":{"$ref":"#/components/schemas/ManifestTransition"},"type":"array"}},"required":["id","name","is_default","require_checklist_close","statuses","transitions"],"type":"object"},"ManifestBoardStatus":{"description":"One custom status, bound to its semantic category. Renames and colors\nnever change behavior — consumers must branch on `category`, not `name`.","properties":{"category":{"$ref":"#/components/schemas/TicketStatusCategory","description":"Semantic category: open | in_progress | waiting | closed. THE\nbinding every SLA/automation/reporting behavior keys on."},"color":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_entry":{"description":"Tickets may enter this status directly at creation.","type":"boolean"},"name":{"type":"string"},"sort_order":{"format":"int32","type":"integer"}},"required":["id","name","category","sort_order","is_entry"],"type":"object"},"ManifestClientWorkflowOverride":{"description":"One per-client workflow override row, verbatim. `state` is\n`set` (value overrides the org default) or `disabled` (explicit deny);\nan absent row means the client inherits and exports nothing.","properties":{"client_id":{"format":"uuid","type":"string"},"client_name":{"description":"Stable semantic sort key (ids only break ties across re-imports).","type":"string"},"key":{"description":"Registry key from `core_domain::workflow_overrides` (closed set).","type":"string"},"state":{"description":"\"set\" | \"disabled\".","type":"string"},"value":{"description":"The typed payload (uuid string for reference kinds, boolean for\ntoggles); `null` when `disabled`.","type":["object","null"]}},"required":["client_id","client_name","key","state"],"type":"object"},"ManifestCustomField":{"description":"One custom field definition. `config` is the kind-specific document\n(select options, validation bounds, …) exactly as stored.","properties":{"archived_at":{"description":"Present when the definition was archived (still a customization the\npreflight must see; the values it shaped may still exist).","format":"date-time","type":["string","null"]},"config":{"type":"object"},"entity":{"description":"Entity the field attaches to: ticket | client | contact | asset.","type":"string"},"id":{"format":"uuid","type":"string"},"kind":{"description":"Field kind: text | number | date | select | multiselect | bool | url | user.","type":"string"},"name":{"type":"string"},"required":{"type":"boolean"},"sort_order":{"format":"int32","type":"integer"}},"required":["id","entity","name","kind","config","required","sort_order"],"type":"object"},"ManifestEscalationStep":{"description":"One escalation ladder step.","properties":{"at_pct":{"description":"Percent of the target's business-time budget consumed at firing.","format":"int32","type":"integer"},"enabled":{"type":"boolean"},"notify_user_ids":{"description":"JSON array of user ids to notify.","type":"object"},"step_index":{"format":"int32","type":"integer"},"trigger_kind":{"description":"risk | breach | escalate.","type":"string"}},"required":["step_index","trigger_kind","at_pct","notify_user_ids","enabled"],"type":"object"},"ManifestReportDefinition":{"description":"One stored report definition (the private build tracker R3). The spec body rides as its\nstored jsonb document (`{filters, group_by, measures}`) exactly like\ncustom-field `config` — the registry-validated view/viz slugs travel as\ntheir own fields so a preflight can flag deprecated constructs without\nre-deriving validation. Nothing here is secret-bearing: definitions\nreference the read-model catalog only, never credentials or data.","properties":{"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":"string"},"description":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"kind":{"description":"Registry-backed view slug (`tickets`, `time_entries`, …).","type":"string"},"name":{"type":"string"},"scope":{"description":"Visibility scope: `org` (published org-wide) or `self` (private to\nthe creator; admins see all). Preflight-relevant because org-scope\ndefinitions are shared surface, self-scope ones are personal.","type":"string"},"spec":{"description":"Spec body document: `{filters, group_by, measures}` as stored.","type":"object"},"updated_at":{"format":"date-time","type":"string"},"viz":{"description":"Visualization hint slug (`bar` | `line` | `pie` | `table`).","type":"string"}},"required":["id","name","kind","spec","viz","scope","created_by","created_at","updated_at"],"type":"object"},"ManifestRole":{"description":"One org-owned role row of the manifest (a custom role or a forked copy).","properties":{"builtin":{"description":"Shipped preset tag, `null` for org-created roles.","type":["string","null"]},"forked_from":{"description":"Fork origin preset tag, `null` unless the row is a forked copy.","type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_system":{"type":"boolean"},"name":{"type":"string"},"permissions":{"description":"The full matrix vocabulary (`{\"matrix\": {\"module\", \"action\"}}`\nentries plus bare special strings) — verbatim for the preflight."}},"required":["id","name","is_system","permissions"],"type":"object"},"ManifestSections":{"description":"Section container. All fields are always present; empty org state is an\nempty array, never an absent key. New sections append here.","properties":{"api_keys":{"description":"API-key metadata minus secret material (no hashes, no raw keys).","items":{"$ref":"#/components/schemas/ManifestApiKey"},"type":"array"},"automation_rules":{"description":"Event-triggered automation rules.","items":{"$ref":"#/components/schemas/ManifestAutomationRule"},"type":"array"},"boards":{"description":"Per-board workflow: the board identity plus its statuses (each bound\nto a semantic category — the defaults-first guarantee) and its\nexplicit transition adjacency. An EMPTY transitions array means the\nsemantic-category floor applies, not \"no transitions\".","items":{"$ref":"#/components/schemas/ManifestBoard"},"type":"array"},"client_workflow_overrides":{"description":"Per-client workflow overrides (Wave 12 lane 5): every client-scoped\noverride of the org workflow defaults, rows verbatim. The override\nregistry (`core_domain::workflow_overrides`) is the closed key set;\nthe preflight must see which constructs each client's rows reference\nso a migration touching those constructs transforms the rows in the\nsame transaction (§4.5).","items":{"$ref":"#/components/schemas/ManifestClientWorkflowOverride"},"type":"array"},"custom_fields":{"description":"Custom field definitions across all entities (tickets, clients,\ncontacts, assets), including archived ones.","items":{"$ref":"#/components/schemas/ManifestCustomField"},"type":"array"},"report_definitions":{"description":"Stored report definitions (the private build tracker R3): registry-validated specs\nover the versioned read-model views. Covered so the upgrade\npreflight (U6) can flag any definition whose `kind`/spec columns\nreference deprecated view or column constructs (policy §5).","items":{"$ref":"#/components/schemas/ManifestReportDefinition"},"type":"array"},"roles":{"description":"Role rows (Wave 13 lane 6, RB1 — ADR 0004: role rows are\ncustomizations-as-data covered by U5/U6 like any other section).\nThis section is the ORG-OWNED rows only: custom roles and forked\ncopies (including forked presets with their origin tag). Shipped\npreset rows are default state, not customization — they stay\ncanonical and restorable, so the preflight sees no drift.","items":{"$ref":"#/components/schemas/ManifestRole"},"type":"array"},"settings":{"description":"Org-scope settings-registry overrides (user-scope rows are per-user\npreferences, not org customization).","items":{"$ref":"#/components/schemas/ManifestSetting"},"type":"array"},"sla":{"$ref":"#/components/schemas/ManifestSla","description":"SLA configuration: calendars and policies with escalation ladders."},"taxonomy":{"$ref":"#/components/schemas/ManifestTaxonomy","description":"Ticket type/category/subcategory trees (Wave 11 lane 3).\nClassification-only (§5): exported so the preflight sees the full\nclassification surface; retired rows INCLUDED (tickets may still\nreference them). Categories are flat — `parent_id` reconstructs\nthe exactly-two-level tree."},"webhooks":{"description":"Webhook subscriptions with event-type filters, never secrets.","items":{"$ref":"#/components/schemas/ManifestWebhook"},"type":"array"}},"required":["custom_fields","boards","automation_rules","webhooks","api_keys","settings","sla","report_definitions","taxonomy","client_workflow_overrides","roles"],"type":"object"},"ManifestSetting":{"description":"One org-scope settings-registry override.","properties":{"key":{"type":"string"},"value":{"type":"object"}},"required":["key","value"],"type":"object"},"ManifestSla":{"description":"SLA configuration sections.","properties":{"calendars":{"items":{"$ref":"#/components/schemas/ManifestSlaCalendar"},"type":"array"},"policies":{"items":{"$ref":"#/components/schemas/ManifestSlaPolicy"},"type":"array"}},"required":["calendars","policies"],"type":"object"},"ManifestSlaCalendar":{"description":"One named SLA calendar row with its flattened business calendar body.","properties":{"calendar":{"description":"Timezone / weekday windows / holidays / maintenance document.","type":"object"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"}},"required":["id","name","calendar"],"type":"object"},"ManifestSlaPolicy":{"description":"One SLA policy with its targets document, embedded fallback calendar,\ncalendar attachment, and escalation ladder.","properties":{"calendar":{"description":"Embedded fallback calendar; governs while `calendar_id` is null.","type":"object"},"calendar_id":{"description":"Attached named calendar; null keeps the embedded one authoritative.","format":"uuid","type":["string","null"]},"escalation_steps":{"description":"Escalation ladder steps, ordered by `step_index`.","items":{"$ref":"#/components/schemas/ManifestEscalationStep"},"type":"array"},"id":{"format":"uuid","type":"string"},"is_default":{"type":"boolean"},"name":{"type":"string"},"targets":{"description":"Targets document keyed by priority string.","type":"object"}},"required":["id","name","is_default","targets","calendar","calendar_id","escalation_steps"],"type":"object"},"ManifestTaxonomy":{"description":"The two classification trees of one org.","properties":{"categories":{"items":{"$ref":"#/components/schemas/ManifestTaxonomyCategory"},"type":"array"},"types":{"items":{"$ref":"#/components/schemas/ManifestTaxonomyType"},"type":"array"}},"required":["types","categories"],"type":"object"},"ManifestTaxonomyCategory":{"description":"One category (root when `parent_id` is null) or subcategory.","properties":{"description":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_retired":{"type":"boolean"},"name":{"type":"string"},"parent_id":{"format":"uuid","type":["string","null"]},"sort_order":{"format":"int32","type":"integer"}},"required":["id","name","is_retired","sort_order"],"type":"object"},"ManifestTaxonomyType":{"description":"One ticket type (flat list).","properties":{"description":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_retired":{"type":"boolean"},"name":{"type":"string"},"sort_order":{"format":"int32","type":"integer"}},"required":["id","name","is_retired","sort_order"],"type":"object"},"ManifestTransition":{"description":"One allowed status change, `from` → `to`, both ids from the SAME board.","properties":{"from_status_id":{"format":"uuid","type":"string"},"to_status_id":{"format":"uuid","type":"string"}},"required":["from_status_id","to_status_id"],"type":"object"},"ManifestWebhook":{"description":"One webhook subscription. The HMAC signing secret is structurally\nabsent: it exists only at creation time and in the sealed column.","properties":{"created_by":{"format":"uuid","type":["string","null"]},"enabled":{"type":"boolean"},"event_types":{"description":"Event-type filter; empty array = wildcard (all event types).","items":{"type":"string"},"type":"array"},"id":{"format":"uuid","type":"string"},"url":{"type":"string"}},"required":["id","url","event_types","enabled","created_by"],"type":"object"},"MarkReadBulkRequest":{"properties":{"ids":{"items":{"format":"uuid","type":"string"},"type":"array"}},"required":["ids"],"type":"object"},"MarkReadResult":{"properties":{"marked":{"description":"Rows flipped from unread to read (idempotent under retry).","format":"int64","type":"integer"}},"required":["marked"],"type":"object"},"MatchCandidate":{"description":"One technician candidate for one ticket: the skills this candidate is\nmissing (an empty list = fully qualified right now).","properties":{"display_name":{"type":"string"},"missing":{"items":{"type":"string"},"type":"array"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","display_name","missing"],"type":"object"},"MatchQuery":{"description":"GET /api/dispatch/match query: how many unassigned tickets to match.","properties":{"limit":{"format":"int64","type":["integer","null"]}},"type":"object"},"MatchView":{"properties":{"matches":{"items":{"$ref":"#/components/schemas/TicketMatch"},"type":"array"}},"required":["matches"],"type":"object"},"MeResponse":{"properties":{"display_name":{"type":"string"},"email":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"permissions":{"items":{"type":"string"},"type":"array"},"timezone":{"type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","org_id","email","display_name","timezone","permissions"],"type":"object"},"MeasureSpec":{"description":"One aggregate measure.","properties":{"agg":{"$ref":"#/components/schemas/Aggregate"},"column":{"description":"`None` only with [`Aggregate::Count`] (`COUNT(*)`).","type":["string","null"]}},"required":["agg"],"type":"object"},"MeetingAssigneeScope":{"description":"Who a meeting type books against: ONE user's calendar, or the\nunioned availability of a free-form admin-defined group.","enum":["user","group"],"type":"string"},"MeetingType":{"description":"A bookable template: duration, buffer before/after, booking window,\norg-editable free-form `parameters`, and the assignee scope. Auto-\nemail is per-template; with transport `none` the send is skipped.","properties":{"assignee_group_id":{"format":"uuid","type":["string","null"]},"assignee_scope":{"$ref":"#/components/schemas/MeetingAssigneeScope"},"assignee_user_id":{"format":"uuid","type":["string","null"]},"auto_email_body":{"type":["string","null"]},"auto_email_enabled":{"type":"boolean"},"auto_email_subject":{"type":["string","null"]},"booking_window_days":{"format":"int32","type":"integer"},"buffer_minutes":{"format":"int32","type":"integer"},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"description":{"type":["string","null"]},"duration_minutes":{"format":"int32","type":"integer"},"id":{"format":"uuid","type":"string"},"is_active":{"type":"boolean"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"parameters":{"description":"Free-form org-edited payload (location hint, agenda, questions…).\nConsumers must tolerate any shape; nothing in behavior keys off it."},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","name","duration_minutes","buffer_minutes","booking_window_days","parameters","assignee_scope","auto_email_enabled","is_active","created_at","updated_at"],"type":"object"},"MeetingTypeBody":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"assignee_group_id":{"format":"uuid","type":["string","null"]},"assignee_scope":{"$ref":"#/components/schemas/MeetingAssigneeScope"},"assignee_user_id":{"format":"uuid","type":["string","null"]},"auto_email_body":{"type":["string","null"]},"auto_email_enabled":{"type":"boolean"},"auto_email_subject":{"type":["string","null"]},"booking_window_days":{"format":"int32","type":"integer"},"buffer_minutes":{"format":"int32","type":"integer"},"description":{"type":["string","null"]},"duration_minutes":{"format":"int32","type":"integer"},"is_active":{"description":"PATCH-only toggle (absent on create = active).","type":["boolean","null"]},"name":{"type":"string"},"parameters":{"description":"Free-form org-edited payload (location hint, agenda, questions…)."}},"required":["name","duration_minutes","assignee_scope"],"type":"object"}]},"MeetingTypeDeleted":{"properties":{"deleted":{"type":"boolean"}},"required":["deleted"],"type":"object"},"MeetingTypeMutation":{"properties":{"meeting_type":{"$ref":"#/components/schemas/MeetingType"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["meeting_type","mutation"],"type":"object"},"MergeConflictEntry":{"properties":{"field":{"description":"\"board\" | \"title\" | \"priority\" | \"client\" | \"assignee\"","type":"string"},"resolution":{"$ref":"#/components/schemas/FieldResolution"},"source_value":{"type":["object","null"]},"survivor_value":{"type":["object","null"]}},"required":["field","resolution","survivor_value","source_value"],"type":"object"},"MergeFoldSummary":{"properties":{"children_relinked":{"format":"int64","type":"integer"},"comments_folded":{"format":"int64","type":"integer"},"cost_lines_folded":{"format":"int64","type":"integer"},"expenses_folded":{"format":"int64","type":"integer"},"time_entries_folded":{"format":"int64","type":"integer"},"time_minutes_folded":{"format":"int64","type":"integer"}},"required":["comments_folded","time_entries_folded","time_minutes_folded","cost_lines_folded","expenses_folded","children_relinked"],"type":"object"},"MergeRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"resolve":{"$ref":"#/components/schemas/MergeResolutions"},"survivor_id":{"format":"uuid","type":"string"}},"required":["survivor_id"],"type":"object"}]},"MergeResolutions":{"properties":{"assignee":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FieldResolution"}]},"board":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FieldResolution"}]},"client":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FieldResolution"}]},"priority":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FieldResolution"}]},"title":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FieldResolution"}]}},"type":"object"},"MergeResponse":{"properties":{"conflicts":{"items":{"$ref":"#/components/schemas/MergeConflictEntry"},"type":"array"},"fold":{"$ref":"#/components/schemas/MergeFoldSummary"},"source_id":{"format":"uuid","type":"string"},"survivor_id":{"format":"uuid","type":"string"}},"required":["survivor_id","source_id","conflicts","fold"],"type":"object"},"MergeWorkbenchView":{"properties":{"auto_merge_threshold":{"description":"The org's auto-merge threshold — surfaced inline so the reviewer\nsees WHY this row was queued instead of auto-merged.","format":"double","type":"number"},"candidate":{"$ref":"#/components/schemas/CandidateView"},"candidate_side":{"$ref":"#/components/schemas/WorkbenchSide"},"decision":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/WorkbenchDecision"}]},"fields":{"description":"The field-by-field picker's rows: the union of both sides'\nobserved/curated keys with per-side provenance.","items":{"$ref":"#/components/schemas/WorkbenchFieldRow"},"type":"array"},"primary":{"$ref":"#/components/schemas/WorkbenchSide"},"related_pending":{"description":"The queued candidates this merge would ride along with (other\npending candidates sharing either side) so batch context is visible.","items":{"$ref":"#/components/schemas/CandidateView"},"type":"array"}},"required":["candidate","primary","candidate_side","fields","related_pending","auto_merge_threshold"],"type":"object"},"MfaResetResponse":{"properties":{"passkeys_removed":{"format":"int64","type":"integer"},"recovery_codes_removed":{"format":"int64","type":"integer"},"reenroll_required":{"description":"Always true. The account owes a factor until one is enrolled, and the\nMFA surface refuses every other factor change until then.","type":"boolean"},"sessions_revoked":{"description":"Live sessions revoked, so a token minted before the reset cannot keep\nacting on the account.","format":"int64","type":"integer"},"totp_removed":{"description":"TOTP enrolments removed (0 or 1 — the schema holds one per user).","format":"int64","type":"integer"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","totp_removed","passkeys_removed","recovery_codes_removed","sessions_revoked","reenroll_required"],"type":"object"},"MfaStepUpRequest":{"properties":{"code":{"description":"Current TOTP code or a recovery code. A recovery code is CONSUMED,\nmirroring login semantics.","type":"string"},"password":{"type":"string"}},"required":["password","code"],"type":"object"},"MfaStepUpResponse":{"properties":{"expires_at":{"format":"date-time","type":"string"},"method":{"description":"Which factor proved possession (`totp` or `recovery_code`). A passkey\nstep-up is the passkey login itself, recorded on the session it\nestablishes.","type":"string"},"ttl_secs":{"description":"Seconds the proof stays usable, counted from `verified_at`.","format":"int64","type":"integer"},"verified_at":{"format":"date-time","type":"string"}},"required":["method","ttl_secs","verified_at","expires_at"],"type":"object"},"MfaTokenRequest":{"properties":{"mfa_token":{"type":"string"}},"required":["mfa_token"],"type":"object"},"MfaVerifyRequest":{"properties":{"code":{"description":"TOTP code or recovery code.","type":"string"},"mfa_token":{"description":"The `mfa_token` from POST /api/auth/login when mfa_required was set.","type":"string"},"trust_device":{"description":"\"Trust this computer\": keep this browser signed in for\n`TRUSTED_DEVICE_TTL_SECS` instead of the default refresh window.\n\n`#[serde(default)]` so every existing client keeps working unchanged —\nan absent field means \"do not trust\", which is the conservative answer\nand the one a caller that never heard of the option should get.","type":"boolean"}},"required":["mfa_token","code"],"type":"object"},"MoveAppointmentRequest":{"properties":{"ends_at":{"format":"date-time","type":"string"},"starts_at":{"format":"date-time","type":"string"}},"required":["starts_at","ends_at"],"type":"object"},"MoveBoardRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + version."},{"properties":{"reset_sla":{"description":"Default false: the existing SLA clock state is PRESERVED verbatim\n(board membership is classification, not a lifecycle event). true =\nre-initialize the clocks under the org's default policy anchored at\nthe move moment.","type":"boolean"},"status_id":{"description":"Pin the target status explicitly (the retry path after a\n`board_move_status_conflict`). Must be a status of the target\nboard; omitted = remap through the semantic categories.","format":"uuid","type":["string","null"]},"target_board_id":{"format":"uuid","type":"string"}},"required":["target_board_id"],"type":"object"}]},"MoveSpec":{"description":"One move in a batch: the booking's new user and slot.","properties":{"appointment_id":{"format":"uuid","type":"string"},"ends_at":{"format":"date-time","type":"string"},"starts_at":{"format":"date-time","type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["appointment_id","user_id","starts_at","ends_at"],"type":"object"},"MoveStageRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"note":{"type":["string","null"]},"to_stage_id":{"format":"uuid","type":"string"}},"required":["to_stage_id"],"type":"object"}],"description":"POST /api/pipeline/deals/{id}/stage body — one audited transition."},"MovementListQuery":{"properties":{"limit":{"format":"int64","type":["integer","null"]}},"type":"object"},"MovementMutationResponse":{"description":"Instant-first mutation response for ledger legs.","properties":{"movement":{"$ref":"#/components/schemas/StockMovementView"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["movement","mutation"],"type":"object"},"MutationMeta":{"description":"Read-your-write metadata every mutation response carries (IF2), merged\nas the additive `mutation` object alongside the entity fields.","properties":{"after":{"description":"Applied entity snapshot.","type":"object"},"before":{"description":"Prior entity snapshot (the undo substrate). `null` on creates. The\nOpenAPI schema stays a plain object; generated clients get the\nruntime-typed view through the SDK's `snapshotOf` accessor.","type":["object","null"]},"idempotency_key":{"description":"The idempotency key that claimed this result, when the caller\nsupplied one.","type":["string","null"]},"seq":{"description":"Monotonic per-entity sequence: strictly increases by one per\napplied write, so clients order reconciliations against the event\nstream.","format":"int64","type":"integer"},"updated_at":{"description":"The applied entity's `updated_at`.","type":"string"},"version":{"description":"Version token for If-Match-style writes; mirrors `seq`. Echo it in\nthe next write's `version` field.","format":"int64","type":"integer"}},"required":["seq","version","updated_at","before","after"],"type":"object"},"MutationParams":{"description":"The autosave mutation parameters every gated create/update body carries\n(flattened into the request struct — purely additive fields).","properties":{"idempotency_key":{"description":"Idempotency key: the same key with the same payload replays the\nSAME stored result (never double-applies); the same key with a\ndifferent payload is a typed 409. Generate one per user edit\ndebounce/retry episode (UUID works).","type":["string","null"]},"version":{"description":"If-Match-style version: the entity `version` the caller last saw\n(the `mutation.version` of the previous response). A mismatch\nrejects the write with a typed 409 `version_conflict` carrying\n`expected`/`provided`; omit for creates and for legacy callers.","format":"int64","type":["integer","null"]}},"type":"object"},"MyWorkItem":{"description":"One cross-board my-work queue row (the private build tracker 2.11): the ticket plus the\nannotations the Today screen renders per the mockup.","properties":{"board_id":{"description":"The source board of this cross-board row.","format":"uuid","type":"string"},"board_name":{"type":"string"},"category":{"$ref":"#/components/schemas/TicketStatusCategory"},"client_name":{"description":"Client short label for the squared tag (`None` = internal ticket).","type":["string","null"]},"due_at":{"description":"Manual deadline set on the ticket itself.","format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"number":{"type":"string"},"priority":{"$ref":"#/components/schemas/Priority"},"sla_resolution_due":{"description":"SLA resolution deadline when state exists; the UI prefers this over\n`due_at` for the right-aligned countdown column.","format":"date-time","type":["string","null"]},"status_name":{"type":"string"},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","number","title","priority","category","status_name","board_id","board_name","updated_at"],"type":"object"},"NodeAudience":{"description":"Who a node is for. Decides palette visibility against the LLM tool\ncatalogue — a power user and the authoring model should not see the\nsame list. A plugin entry that declares nothing stays `human` until it\nsays otherwise (an undeclared node never auto-joins the model's view).","enum":["human","ai","both"],"type":"string"},"NodeCatalogueView":{"description":"The catalogue read: version, digest, and the nodes the caller asked\nfor. The version and digest let a saved document say which release it\nwas validated against, and make two builds claiming the same release\nwith different declarations detectable.","properties":{"digest":{"type":"string"},"node_count":{"minimum":0,"type":"integer"},"nodes":{"items":{"$ref":"#/components/schemas/NodeView"},"type":"array"},"version":{"type":"string"}},"required":["version","digest","node_count","nodes"],"type":"object"},"NodeFamily":{"description":"Which part of the workflow surface a node belongs to. The palette groups\nby family; the properties panel and the LLM tool catalogue both filter\non it.","enum":["trigger","decision","condition","action","control","value"],"type":"string"},"NodeTier":{"description":"The catalogue's three tiers (docs/reference/workflow-node-catalogue.md).\nTier A is always visible and first in search; Tier B is the domain long\ntail reached through the generic operation nodes; Tier C is\nplugin-contributed.","enum":["a","b","c"],"type":"string"},"NodeView":{"description":"One node as the web and the SDK consume it. Schemas ride as JSON so a\nproperties panel renders from them directly instead of hand-writing a\nform per node.","properties":{"agent_description":{"type":"string"},"audience":{"$ref":"#/components/schemas/NodeAudience"},"emits":{"description":"The event types this node can produce, the seam the static loop\nrefusal reads.","items":{"type":"string"},"type":"array"},"executable":{"description":"Whether the executor can actually apply this node today (Wave 43\nlane 20, item C). A palette should gray out a node whose flag is\nfalse: `WorkflowDocument::validate` refuses to save one.","type":"boolean"},"executor":{"description":"The executor path this node rides: a closed domain operation for a\nTier A wrapper, the generic path for Tier B, the plugin surface\nkind for Tier C.","type":"string"},"family":{"$ref":"#/components/schemas/NodeFamily"},"id":{"type":"string"},"idempotent":{"type":"boolean"},"input_schema":{"type":"object"},"label":{"type":"string"},"output_schema":{"type":"object"},"tier":{"$ref":"#/components/schemas/NodeTier"}},"required":["id","label","agent_description","family","tier","idempotent","audience","executor","input_schema","output_schema","emits","executable"],"type":"object"},"NoteDraftBody":{"properties":{"body":{"type":"string"},"idempotency_key":{"description":"The autosave claim key — the composer must echo it back on submit\nso the committed comment clears this exact draft row.","type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["body","idempotency_key","updated_at"],"type":"object"},"NoteDraftResponse":{"properties":{"draft":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/NoteDraftBody","description":"The caller's draft (per user × ticket); null when none is saved."}]}},"type":"object"},"NoteKind":{"description":"Taxonomy of structured ticket notes (§1).","enum":["internal","client_visible","in_house_update"],"type":"string"},"Notification":{"properties":{"body":{"type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"kind":{"$ref":"#/components/schemas/NotificationKind"},"org_id":{"format":"uuid","type":"string"},"read_at":{"format":"date-time","type":["string","null"]},"ticket_id":{"format":"uuid","type":["string","null"]},"title":{"type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["id","org_id","user_id","kind","title","created_at"],"type":"object"},"NotificationKind":{"enum":["mention","assignment","sla_risk","sla_breach","escalation","missing_time","ticket_update","report_delivery","ai_cap_warn","secret_rotation","stock_low","dedupe_report","scan_detection"],"type":"string"},"NotificationPage":{"description":"One page of the inbox plus the cursor to continue with.","properties":{"items":{"items":{"$ref":"#/components/schemas/Notification"},"type":"array"},"next_cursor":{"description":"Pass as `cursor` on the next request; `None` means exhausted.","type":["string","null"]}},"required":["items"],"type":"object"},"OAuthCallbackQuery":{"properties":{"code":{"type":["string","null"]},"error":{"description":"The operator may decline; the vendor then redirects with an error\nand no code. That is a user choice, not a failure, so it gets its own\nmessage instead of \"invalid code\".","type":["string","null"]},"state":{"type":"string"}},"required":["state"],"type":"object"},"OAuthCallbackResult":{"properties":{"connected":{"type":"boolean"},"message":{"description":"Set only on a DECLINED consent, so the UI can say \"you cancelled\"\nrather than showing a generic failure.","type":["string","null"]}},"required":["connected"],"type":"object"},"OAuthStartResponse":{"description":"Where the operator lands after consent.","properties":{"authorize_url":{"description":"The vendor consent URL. The operator is redirected here; the callback\nURL is pre-registered by the operator with their OAuth client.","type":"string"},"expires_in_minutes":{"format":"int64","type":"integer"}},"required":["authorize_url","expires_in_minutes"],"type":"object"},"OidcPresetInfo":{"description":"One entry of the declarative first-party preset catalog.","properties":{"discovery_template":{"type":"string"},"display_name":{"type":"string"},"issuer_template":{"description":"`{param}`-templated issuer the preset pins.","type":"string"},"name":{"type":"string"},"redirect_guidance":{"description":"Where to register the redirect URI on the provider side.","type":"string"},"required_params":{"description":"Parameters the admin MUST provide (e.g. `tenant`, `domain`).","items":{"type":"string"},"type":"array"},"scopes":{"type":"string"}},"required":["name","display_name","issuer_template","discovery_template","scopes","required_params","redirect_guidance"],"type":"object"},"OidcProviderInfo":{"properties":{"client_id":{"type":"string"},"discovery_url":{"type":"string"},"display_name":{"type":"string"},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"issuer":{"type":"string"},"jit_provisioning":{"type":"boolean"},"name":{"description":"URL path segment used by login/link routes.","type":"string"},"preset":{"description":"Declarative preset this provider was created from, when any.","type":["string","null"]}},"required":["id","name","display_name","issuer","discovery_url","client_id","jit_provisioning","enabled"],"type":"object"},"OnCallBlock":{"description":"An absolute UTC on-call rotation segment for one user.","properties":{"category":{"description":"Free-form rotation label ('primary', 'secondary', 'escalation', …).","type":"string"},"created_at":{"format":"date-time","type":"string"},"ends_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"notes":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"starts_at":{"format":"date-time","type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["id","org_id","user_id","starts_at","ends_at","category","created_at"],"type":"object"},"OnHandQuery":{"properties":{"item_id":{"format":"uuid","type":["string","null"]},"limit":{"format":"int64","type":["integer","null"]},"location_id":{"format":"uuid","type":["string","null"]},"low_only":{"description":"true → only rows at/below the reorder point.","type":["boolean","null"]}},"type":"object"},"OnHandRow":{"description":"The stock-on-hand read model row (`qty leaking thresholds`).","properties":{"item_kind":{"type":"string"},"item_name":{"type":"string"},"location_kind":{"type":"string"},"location_name":{"type":"string"},"low":{"description":"The instant-first low flag the UI + automations key on.","type":"boolean"},"max_qty_milli":{"format":"int64","type":["integer","null"]},"min_qty_milli":{"format":"int64","type":["integer","null"]},"qty_milli":{"format":"int64","type":"integer"},"reorder_point_milli":{"format":"int64","type":["integer","null"]},"stock_item_id":{"format":"uuid","type":"string"},"stock_location_id":{"format":"uuid","type":"string"},"unit":{"type":"string"}},"required":["stock_item_id","item_name","item_kind","unit","stock_location_id","location_name","location_kind","qty_milli","low"],"type":"object"},"OnboardingDisableRequest":{"properties":{"disabled":{"type":"boolean"}},"required":["disabled"],"type":"object"},"OnboardingDisableResponse":{"properties":{"disabled":{"type":"boolean"}},"required":["disabled"],"type":"object"},"OnboardingMemberView":{"properties":{"display_name":{"type":"string"},"email":{"type":"string"},"steps":{"items":{"$ref":"#/components/schemas/OnboardingStepView"},"type":"array"},"unresolved":{"description":"Live: the member still has unresolved steps (drives re-trigger UX).","type":"boolean"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","display_name","email","steps","unresolved"],"type":"object"},"OnboardingScopeView":{"properties":{"disabled":{"description":"Org-wide master switch (Settings → Onboarding).","type":"boolean"},"legacy_wizard_completed":{"description":"Legacy fallback: true while NO per-step flags exist yet but the old\nflat key did (U6-safe supersede).","type":"boolean"},"steps":{"items":{"$ref":"#/components/schemas/OnboardingStepView"},"type":"array"}},"required":["disabled","legacy_wizard_completed","steps"],"type":"object"},"OnboardingSkipRequest":{"properties":{"never":{"description":"true = \"never show this again\" (persists the dismissed flag);\nfalse = plain \"skip for now\" (a no-op that intentionally records\nnothing — the step stays pending and resumable).","type":"boolean"}},"type":"object"},"OnboardingStepView":{"properties":{"dismissed":{"description":"\"never show this again\" was recorded for this step.","type":"boolean"},"done":{"type":"boolean"},"resolved":{"type":"boolean"},"satisfied":{"description":"Server-verified live gate state right now.","type":"boolean"},"step":{"type":"string"}},"required":["step","done","dismissed","satisfied","resolved"],"type":"object"},"OnboardingSummary":{"properties":{"members":{"description":"Populated for admin callers only.","items":{"$ref":"#/components/schemas/OnboardingMemberView"},"type":"array"},"org":{"$ref":"#/components/schemas/OnboardingScopeView"}},"required":["org","members"],"type":"object"},"OnboardingTriggerRequest":{"properties":{"user_id":{"description":"The member whose first-run flow is re-triggered (admin calls);\nomitted means re-run the CALLER's own user onboarding.","format":"uuid","type":["string","null"]}},"type":"object"},"OrderLineInput":{"properties":{"catalog_item_id":{"format":"uuid","type":["string","null"]},"description":{"type":"string"},"position":{"format":"int32","type":["integer","null"]},"qty_ordered_milli":{"format":"int64","type":"integer"},"stock_item_id":{"format":"uuid","type":["string","null"]},"unit_cost_minor":{"format":"int64","type":"integer"}},"required":["description","qty_ordered_milli","unit_cost_minor"],"type":"object"},"OrderMutationResponse":{"allOf":[{"$ref":"#/components/schemas/PurchaseOrderDetail"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}]},"OrgBillingPreview":{"description":"Org-wide preview: every billable contract's evaluation for the window\nplus the grand total — the shape a Wave-9 invoice run will consume.","properties":{"evaluations":{"items":{"$ref":"#/components/schemas/BillingEvaluation"},"type":"array"},"from":{"format":"date","type":"string"},"to":{"format":"date","type":"string"},"total_minor":{"description":"Sum over evaluations, minor units (mixed currencies reject).","format":"int64","type":"integer"}},"required":["from","to","evaluations","total_minor"],"type":"object"},"OrgDefaultView":{"properties":{"email":{"type":"boolean"},"in_app":{"type":"boolean"},"kind":{"$ref":"#/components/schemas/NotificationKind"},"quiet_from":{"type":["string","null"]},"quiet_until":{"type":["string","null"]},"timezone":{"type":"string"}},"required":["kind","in_app","email","timezone"],"type":"object"},"OrgDefaultsResponse":{"properties":{"defaults":{"items":{"$ref":"#/components/schemas/OrgDefaultView"},"type":"array"}},"required":["defaults"],"type":"object"},"OrgProfileResponse":{"properties":{"base_currency":{"description":"The org's ONE base (reporting) currency (ADR 0002, FX1).","type":"string"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"ticket_prefix":{"type":"string"}},"required":["id","name","slug","ticket_prefix","base_currency"],"type":"object"},"OrgQuotaView":{"description":"One metric beside its configured limit, if any, and the percentage of\nit in use.","properties":{"limits_configured":{"description":"Whether the org configured ANY limit. `false` means every\n`percent` below is `None` because there is nothing to measure\nagainst — the answer for an unconfigured org, not a default.","type":"boolean"},"metrics":{"description":"Every reportable metric in a stable order, whether or not a limit\nis configured for it.","items":{"$ref":"#/components/schemas/QuotaMetricView"},"type":"array"},"org_id":{"format":"uuid","type":"string"},"refreshed_at":{"description":"When the usage figures were computed. `None` while the org's row has\nnever been refreshed. Note that `UsageMetric::AutomationRuns` and\n`WebhookDeliveries` read their all-time totals from this row, so the\ntotals are as of this instant rather than as of the request.","format":"date-time","type":["string","null"]}},"required":["org_id","limits_configured","metrics"],"type":"object"},"OrgReconcileRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first mutation parameters (IF1)."},{"properties":{"source_system":{"type":"string"},"vendor_label":{"type":["string","null"]},"vendor_org_id":{"type":"string"}},"required":["source_system","vendor_org_id"],"type":"object"}]},"OrgReconcileResponse":{"properties":{"already_mapped":{"description":"Whether the vendor org was ALREADY mapped (idempotent no-op).","type":"boolean"},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"queued_candidates":{"items":{"$ref":"#/components/schemas/EntityCandidateView"},"type":"array"}},"required":["queued_candidates","already_mapped","mutation"],"type":"object"},"OrgUsageView":{"description":"The org's current usage, one field per recorded metric.","properties":{"active_users":{"description":"Staff accounts that can still sign in. A disabled account is not\nusing anything, so it is not counted here.","example":14,"format":"int64","type":"integer"},"audit_events_24h":{"description":"Audit events within `[refreshed_at - 24h, refreshed_at]` — the closest\nhonest per-org proxy for API activity the core records. NOT an\nAPI-call count: reads are not audited and writes are audited\nbest-effort, so this counts the mutations the core chose to record,\nnot the requests that arrived. The window is closed at refresh time,\nas with the other `_24h` fields.","example":300,"format":"int64","type":"integer"},"automation_runs":{"description":"Ledgered automation execution attempts since the org's first run.\nThis is an ALL-TIME total, so it does not carry the staleness rule\nthe `_24h` fields do — but it is still only as current as\n`refreshed_at`, since the row is written once per refresh.","example":120,"format":"int64","type":"integer"},"automation_runs_24h":{"description":"Ledgered automation execution attempts within\n`[refreshed_at - 24h, refreshed_at]`. The window is CLOSED at\nrefresh time, so it ages with the row rather than sliding to the\nmoment of the read; compare against `refreshed_at` before treating\nthis as current.","example":12,"format":"int64","type":"integer"},"managed_devices":{"description":"Active managed client devices. This is an ACTIVE-INVENTORY count:\n`device_assets` has no connectivity or last-seen signal, so it cannot\nmeasure \"connected\" clients. Status is connector-compatibility free\ntext matched case-insensitively, so orgs whose connectors store a\ndifferent status string under-report — this is a floor, not a census.","example":9,"format":"int64","type":"integer"},"object_store_bytes":{"description":"Recorded object-store payload bytes (a floor; see the method note in\nmigration 0131 — staged import chunks record no size).","example":2147483648,"format":"int64","type":"integer"},"org_id":{"format":"uuid","type":"string"},"refreshed_at":{"description":"When these numbers were computed (the materialized row's own\ntimestamp). `None` while the refresh sweep has not reached the org\nyet, which is exactly when the counts should not be trusted. Every\n`_24h` field below is a window that ENDS at this instant; as the row\nages, so does the window, so read this before trusting those fields.","format":"date-time","type":["string","null"]},"tickets_by_category":{"$ref":"#/components/schemas/TicketsByCategory","description":"Tickets per workflow category (the closed vocabulary). Category,\nnot board status name: names are per-board and repeat across boards,\nso a name-keyed map would collapse distinct statuses."},"tickets_total":{"description":"Tickets across every category — the total an inventory limit\nconstrains.","example":64,"format":"int64","type":"integer"},"unavailable":{"description":"Metrics this build cannot compute honestly, each with the reason.\nEmpty is not possible today; the list exists so a monitoring\nsurface can render the gap rather than silently miss a column.","items":{"$ref":"#/components/schemas/UnavailableMetric"},"type":"array"},"webhook_deliveries":{"description":"Ledgered webhook delivery attempts since the org's first. One row per\nATTEMPT, so a retried delivery counts three times. All-time, so no\nwindow rule — only the row's own `refreshed_at` currency.","example":540,"format":"int64","type":"integer"},"webhook_deliveries_24h":{"description":"Ledgered webhook delivery attempts within\n`[refreshed_at - 24h, refreshed_at]` — the same CLOSED,\nrefresh-time-frozen window as `automation_runs_24h`.","example":40,"format":"int64","type":"integer"}},"required":["org_id","active_users","tickets_by_category","tickets_total","managed_devices","object_store_bytes","automation_runs","automation_runs_24h","webhook_deliveries","webhook_deliveries_24h","audit_events_24h","unavailable"],"type":"object"},"OrgUser":{"description":"Directory view of one staff user (route-level DTO; `org_role` is the\nuser's highest-precedence builtin role, `null` for custom-only).","properties":{"created_at":{"type":"string"},"display_name":{"type":"string"},"email":{"type":"string"},"id":{"format":"uuid","type":"string"},"is_active":{"type":"boolean"},"org_id":{"format":"uuid","type":"string"},"org_role":{"type":["string","null"]}},"required":["id","org_id","email","display_name","is_active","created_at"],"type":"object"},"OrgUserListResponse":{"properties":{"total":{"description":"Total directory size (for offset paging).","format":"int64","type":"integer"},"users":{"items":{"$ref":"#/components/schemas/OrgUser"},"type":"array"}},"required":["users","total"],"type":"object"},"OrgUserMutationResponse":{"allOf":[{"$ref":"#/components/schemas/OrgUser"},{"properties":{"invite":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/InviteInfo","description":"Present on invite only: the setup token for the invitee."}]},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): the invited (still inactive)\nuser at the top level plus the one-time setup token and the additive\n`mutation` metadata."},"OutboundSettingsView":{"properties":{"brand_name":{"type":"string"},"credentials":{"$ref":"#/components/schemas/CredentialsView"},"from_address":{"type":"string"},"from_name":{"type":"string"},"logo_url":{"type":"string"},"not_ready_reason":{"description":"Human reason when `ready` is false.","type":["string","null"]},"org_greeting":{"description":"The org's own greeting — what opens mail the workspace sends to its\nstaff, and the fallback for a member with none (Wave 42).","type":"string"},"postmark_base_url":{"type":"string"},"ready":{"description":"Whether the SELECTED transport has a usable credential/endpoint\ncombination (the honest not-configured bit the UI renders).","type":"boolean"},"reply_to":{"type":"string"},"ses_region":{"description":"The SES region this org sends through, when one is set. Plain config,\nnever a secret — the admin UI shows it so an operator can see which\ndata centre is in play.","type":"string"},"signature":{"type":"string"},"signature_policy":{"description":"`allowed` | `org_locked`: whether members may sign with their own\ndetails. The UI renders a locked org's state rather than a disabled\nfield with no reason.","type":"string"},"smtp_host":{"type":"string"},"smtp_port":{"format":"int64","type":"integer"},"smtp_username":{"type":"string"},"transport":{"type":"string"}},"required":["transport","from_address","from_name","reply_to","signature","org_greeting","signature_policy","brand_name","logo_url","smtp_host","smtp_port","smtp_username","postmark_base_url","ses_region","ready","credentials"],"type":"object"},"OverrideDeclarationView":{"description":"One registry declaration, projected for the settings/admin surface.","properties":{"allow_disable":{"type":"boolean"},"description":{"type":"string"},"key":{"type":"string"},"label":{"type":"string"},"org_default_setting":{"description":"The settings-registry key carrying the org default for toggles.","type":["string","null"]},"value_kind":{"description":"board_ref | status_ref | sla_policy_ref | toggle.","type":"string"}},"required":["key","value_kind","allow_disable","label","description"],"type":"object"},"OverrideEntry":{"description":"One resolved override entry: the declaration it validated against, the\nresolution outcome, which layer supplied it, the raw org default and client\nrow, the renderable detail, and any resolution issues (e.g. a stale\ncross-board status override that fell back to the board's entry status).","properties":{"allow_disable":{"type":"boolean"},"client_override":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ClientOverrideRow"}]},"description":{"type":"string"},"detail":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ResolvedDetail"}]},"issues":{"description":"Typed drift/validation notes (never empty on a fallback).","items":{"type":"string"},"type":"array"},"key":{"type":"string"},"label":{"type":"string"},"org_default":{"description":"The org default this resolution inherited from, when one existed.","type":["object","null"]},"resolution":{"$ref":"#/components/schemas/Resolution"},"source":{"$ref":"#/components/schemas/ResolutionSource"},"value_kind":{"$ref":"#/components/schemas/OverrideValueKind"}},"required":["key","label","description","value_kind","allow_disable","resolution","source","org_default","issues"],"type":"object"},"OverrideMutationResponse":{"properties":{"client_id":{"format":"uuid","type":"string"},"detail":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ResolvedDetail"}]},"key":{"type":"string"},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"resolution":{"$ref":"#/components/schemas/Resolution"},"source":{"$ref":"#/components/schemas/ResolutionSource","description":"Which layer supplied the effective value."}},"required":["client_id","key","resolution","source","mutation"],"type":"object"},"OverrideRegistryResponse":{"properties":{"declarations":{"items":{"$ref":"#/components/schemas/OverrideDeclarationView"},"type":"array"}},"required":["declarations"],"type":"object"},"OverrideValueKind":{"description":"The value shapes a client override accepts. The wire value is the plain\nJSON payload (uuid string / boolean); the kind exists so validation can\nreject a mistyped payload before persistence.","enum":["board_ref","status_ref","sla_policy_ref","toggle"],"type":"string"},"OwnedDeleteQuery":{"properties":{"user_id":{"description":"Owner of the row being deleted (must match self-or-admin rule).","format":"uuid","type":"string"}},"required":["user_id"],"type":"object"},"PackRuleView":{"properties":{"active":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"item_name":{"type":["string","null"]},"kit_id":{"format":"uuid","type":["string","null"]},"kit_name":{"type":["string","null"]},"note":{"type":["string","null"]},"qty_milli":{"format":"int64","type":"integer"},"scope_id":{"format":"uuid","type":"string"},"scope_kind":{"type":"string"},"scope_name":{"type":"string"},"stock_item_id":{"format":"uuid","type":["string","null"]},"target_kind":{"type":"string"}},"required":["id","scope_kind","scope_id","scope_name","target_kind","qty_milli","active"],"type":"object"},"PasswordEntry":{"description":"A vault credential. `secret_ciphertext` is AES-256-GCM from\ncore-api crypto::seal — plaintext NEVER reaches the database layer,\nand this entity never carries it either (reveal is a dedicated,\naudited endpoint response type).","properties":{"client_id":{"description":"NULL = org-level vault entry.","format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"expires_at":{"format":"date-time","type":["string","null"]},"has_notes":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"last_rotated_at":{"description":"Stamped by the same update that moves `rotate_at` forward.","format":"date-time","type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"rotate_at":{"description":"Operator-scheduled rotation due date (Wave 19 lane 3); the worker\nsweep reminds once past it (durable at-most-once marker on the\nrow in the DB; the entities carry no sweep housekeeping).","format":"date-time","type":["string","null"]},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"updated_by":{"format":"uuid","type":["string","null"]},"url":{"type":"string"},"username":{"type":"string"}},"required":["id","org_id","title","created_at","updated_at"],"type":"object"},"PasswordHistoryEntry":{"description":"One archived secret ciphertext from a credential's change history.","properties":{"changed_by":{"format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"password_id":{"format":"uuid","type":"string"}},"required":["id","org_id","password_id","created_at"],"type":"object"},"PasswordListQuery":{"properties":{"client_id":{"format":"uuid","type":["string","null"]},"global":{"description":"`true` = org-level entries only.","type":["boolean","null"]}},"type":"object"},"PasswordMutationResponse":{"allOf":[{"$ref":"#/components/schemas/PasswordEntry"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2)."},"PasswordResetConfirm":{"properties":{"new_password":{"type":"string"},"token":{"type":"string"}},"required":["token","new_password"],"type":"object"},"PasswordResetRequest":{"properties":{"email":{"type":"string"}},"required":["email"],"type":"object"},"PasswordReveal":{"description":"The audited reveal response: plaintext is returned exactly once,\nover TLS, to a caller holding `passwords:edit`; the reveal emits a\n`password.revealed` contract event (provenance query: \"who looked\nat this credential and when\").","properties":{"id":{"format":"uuid","type":"string"},"notes":{"type":["string","null"]},"secret":{"type":"string"},"title":{"type":"string"}},"required":["id","title","secret"],"type":"object"},"PatchAlertSourceRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive."},{"properties":{"board_id":{"format":"uuid","type":["string","null"]},"client_id":{"description":"`Some(None)` clears; `Some(Some(v))` sets; `None` = no change.\n`deserialize_with` is what keeps the first two apart: serde's own\n`Option` layer reads a JSON `null` as `None`, which is the absent\nshape, so without it a demotion was indistinguishable from a rename.","format":"uuid","type":["string","null"]},"correlation_window_secs":{"format":"int32","type":["integer","null"]},"enabled":{"type":["boolean","null"]},"event_type":{"description":"Promote/demote a source between alert and generic M2M profiles.\n`Some(None)` reverts to alert-source; `Some(Some(v))` sets the\nemitted event type.","type":["string","null"]},"name":{"type":["string","null"]},"payload_schema_ref":{"description":"Set/clear the payload contract pin the same way.","type":["string","null"]}},"type":"object"}]},"PatchAppointmentRequest":{"properties":{"kind":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AppointmentKind"}]},"notes":{"type":["string","null"]},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AppointmentStatus"}]},"ticket_id":{"format":"uuid","type":["string","null"]},"title":{"type":["string","null"]},"travel_minutes":{"format":"int32","type":["integer","null"]}},"type":"object"},"PatchContractRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key +\nIf-Match `version`. Absent in legacy clients — purely additive."},{"properties":{"amount_minor":{"format":"int64","type":["integer","null"]},"billing_anchor":{"format":"date","type":["string","null"]},"billing_frequency":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BillingFrequency"}]},"blocks":{"description":"When present, replaces the whole prepaid-block set.","items":{"$ref":"#/components/schemas/BlockSpec"},"type":["array","null"]},"caps":{"description":"When present, replaces the whole cap set.","items":{"$ref":"#/components/schemas/CapSpec"},"type":["array","null"]},"clear_escalator":{"type":["boolean","null"]},"clear_minimum":{"description":"Explicit clear for the minimum commitment: PATCH cannot\ndistinguish \"absent\" from \"null\" on the flat field, so removing\na minimum sends `clear_minimum: true`.","type":["boolean","null"]},"client_id":{"format":"uuid","type":["string","null"]},"contract_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ContractType"}]},"currency":{"type":["string","null"]},"effective_from":{"format":"date","type":["string","null"]},"effective_until":{"format":"date","type":["string","null"]},"escalator":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/EscalatorSpec","description":"When present, sets the escalator; `clear_escalator: true` removes it."}]},"fees":{"description":"When present, replaces the whole fee schedule.","items":{"$ref":"#/components/schemas/FeeSpec"},"type":["array","null"]},"hourly_rate_minor":{"format":"int64","type":["integer","null"]},"incident_price_minor":{"format":"int64","type":["integer","null"]},"incident_priority":{"type":["string","null"]},"included_minutes":{"format":"int32","type":["integer","null"]},"minimum_commitment_minor":{"description":"When present, sets/clears the minimum commitment (`None` clears).","format":"int64","type":["integer","null"]},"name":{"type":["string","null"]},"notes":{"type":["string","null"]},"number":{"type":["string","null"]},"overage_rate_minor":{"format":"int64","type":["integer","null"]},"po_number":{"type":["string","null"]},"rates":{"description":"When present, replaces the whole rate card.","items":{"$ref":"#/components/schemas/RateSpec"},"type":["array","null"]},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ContractStatus"}]},"unit_basis":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/UnitBasis"}]},"unit_price_minor":{"format":"int64","type":["integer","null"]}},"type":"object"}]},"PatchMeRequest":{"properties":{"display_name":{"type":["string","null"]},"timezone":{"type":["string","null"]}},"type":"object"},"PatchOrgRequest":{"properties":{"base_currency":{"description":"Present + different ⇒ a governed base-currency change (ADR 0002):\nrefused with a typed conflict while conversion snapshots exist.","type":["string","null"]},"name":{"type":["string","null"]},"ticket_prefix":{"type":["string","null"]}},"type":"object"},"PatchPhaseRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive."},{"properties":{"budget_currency":{"type":["string","null"]},"budget_minor":{"description":"Wave 39 lane 4: tri-state budget/fee updates on the phase's\nbilling record (`Some(Some(v))` sets, `Some(None)` clears,\n`None` = no change; `clear_*` drops the whole record when both\nfields would be empty).","format":"int64","type":["integer","null"]},"description":{"type":["string","null"]},"end_date":{"format":"date","type":["string","null"]},"fee_currency":{"type":["string","null"]},"fee_minor":{"format":"int64","type":["integer","null"]},"is_milestone":{"type":["boolean","null"]},"milestone_amount_minor":{"format":"int64","type":["integer","null"]},"milestone_currency":{"type":["string","null"]},"name":{"type":["string","null"]},"sort_order":{"format":"int32","type":["integer","null"]},"start_date":{"description":"Tri-state, matching the budget fields below: `Some(Some(d))` sets a\ndate, `Some(None)` takes the phase off the Gantt, and an absent field\nleaves it alone. Without the distinction, renaming a phase would\nquietly erase its planned window.","format":"date","type":["string","null"]},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PhaseStatus"}]}},"type":"object"}]},"PatchPortalUserRequest":{"properties":{"display_name":{"type":["string","null"]},"password":{"description":"Optional credential reset (hashed before storage).","type":["string","null"]},"role":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PortalRole"}]},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PortalUserStatus","description":"`disabled` blocks login AND every authenticated request immediately."}]}},"type":"object"},"PatchProjectRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive."},{"properties":{"billing_contract_id":{"description":"`Some(Some(v))` links the contract; `Some(None)` unlinks;\n`None` = no change (Wave 39 lane 4, report P2).","format":"uuid","type":["string","null"]},"billing_method":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BillingMethod"}]},"budget_minor":{"format":"int64","type":["integer","null"]},"client_id":{"format":"uuid","type":["string","null"]},"currency":{"type":["string","null"]},"description":{"description":"`Some(Some(v))` sets; `Some(None)` clears; `None` = no change.","type":["string","null"]},"due_date":{"format":"date","type":["string","null"]},"name":{"type":["string","null"]},"start_date":{"format":"date","type":["string","null"]},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ProjectStatus"}]}},"type":"object"}]},"PatchRuleRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"active":{"type":"boolean"}},"required":["active"],"type":"object"}]},"PatchTaskRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive."},{"properties":{"assignee_id":{"format":"uuid","type":["string","null"]},"description":{"type":["string","null"]},"due_at":{"format":"date-time","type":["string","null"]},"is_milestone":{"type":["boolean","null"]},"milestone_amount_minor":{"format":"int64","type":["integer","null"]},"milestone_currency":{"type":["string","null"]},"phase_id":{"format":"uuid","type":["string","null"]},"sort_order":{"format":"int32","type":["integer","null"]},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TaskStatus","description":"Lifecycle moves other than completion (pending / in_progress /\nblocked / cancelled). Reaching `completed` MUST go through the\ncomplete endpoint so dependency gating + billing events cannot be\nskipped."}]},"title":{"type":["string","null"]}},"type":"object"}]},"PatchTemplateRequest":{"properties":{"description":{"type":["string","null"]},"name":{"type":["string","null"]},"spec":{}},"type":"object"},"PatchTicketRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive."},{"properties":{"assignee_id":{"description":"`Some(Some(id))` assigns; `Some(None)` unassigns; `None` = no change.","format":"uuid","type":["string","null"]},"body":{"type":["string","null"]},"category_id":{"format":"uuid","type":["string","null"]},"client_id":{"format":"uuid","type":["string","null"]},"contact_id":{"format":"uuid","type":["string","null"]},"custom_fields":{"description":"Custom-field values to upsert. Partial semantics: only listed fields\nchange, each validated against its definition.","items":{"$ref":"#/components/schemas/TicketFieldInput"},"type":["array","null"]},"due_at":{"format":"date-time","type":["string","null"]},"priority":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Priority"}]},"site_id":{"format":"uuid","type":["string","null"]},"title":{"type":["string","null"]},"type_id":{"description":"Classification (Wave 11 lane 3): `\"type_id\": null` clears,\nan id sets (the node must exist in this org and not be retired),\nan absent member leaves it untouched. Purely descriptive: behavior\nbinds to the semantic categories, never these trees.","format":"uuid","type":["string","null"]}},"type":"object"}]},"PatchTravelDraftRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"billable":{"type":["boolean","null"]},"distance":{"format":"double","type":["number","null"]},"distance_unit":{"type":["string","null"]},"ended_at":{"format":"date-time","type":["string","null"]},"notes":{"type":["string","null"]},"started_at":{"format":"date-time","type":["string","null"]},"ticket_id":{"format":"uuid","type":["string","null"]},"user_id":{"format":"uuid","type":["string","null"]},"vehicle":{"type":["string","null"]}},"type":"object"}],"description":"PATCH /api/dispatch/travel-drafts/{id} body — every member optional;\nabsent members are left untouched."},"PatchViewRequest":{"properties":{"is_default":{"type":["boolean","null"]},"name":{"type":["string","null"]},"spec":{}},"type":"object"},"PauseTimerRequest":{"properties":{"reason":{"description":"Optional reason, preserved on the segment forever.","type":["string","null"]},"session_id":{"description":"Omit to act on the caller's active timer.","format":"uuid","type":["string","null"]}},"type":"object"},"PaymentMutationResponse":{"description":"Payment receipt response. The invoice is nested (not flattened) so\nthe stored replay payload — `{payment, invoice}` plus the merged\nmutation metadata — deserializes into this shape byte-identically.","properties":{"invoice":{"$ref":"#/components/schemas/Invoice"},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"payment":{"$ref":"#/components/schemas/InvoicePayment"}},"required":["payment","invoice","mutation"],"type":"object"},"PhaseAllocationListView":{"description":"Every allocation in the org, or in one project.","properties":{"allocations":{"items":{"$ref":"#/components/schemas/PhaseAllocationView"},"type":"array"}},"required":["allocations"],"type":"object"},"PhaseAllocationMutation":{"properties":{"end_date":{"format":"date","type":["string","null"]},"note":{"type":["string","null"]},"phase_id":{"format":"uuid","type":"string"},"share_percent":{"description":"1..=100, CHECK-enforced in the database. A 0 or a 150 entering the\nforecast would corrupt every capacity number on the page, so the\nbound is enforced in the schema rather than only in this handler.","format":"int32","type":"integer"},"start_date":{"format":"date","type":["string","null"]},"user_id":{"format":"uuid","type":"string"}},"required":["phase_id","user_id","share_percent"],"type":"object"},"PhaseAllocationQuery":{"properties":{"project_id":{"format":"uuid","type":["string","null"]}},"type":"object"},"PhaseAllocationView":{"description":"One stated allocation of a technician to a project phase.","properties":{"end_date":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"note":{"type":["string","null"]},"phase_id":{"format":"uuid","type":"string"},"phase_name":{"type":"string"},"phase_status":{"type":"string"},"project_id":{"format":"uuid","type":"string"},"project_name":{"type":"string"},"share_percent":{"format":"int32","type":"integer"},"start_date":{"type":["string","null"]},"user_display_name":{"type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["id","phase_id","user_id","share_percent","phase_name","phase_status","project_id","project_name","user_display_name"],"type":"object"},"PhaseBurn":{"description":"The burn read model for one phase: consumed minutes priced against the\nproject's linked contract versus the contracted budget, with a typed\nalert state (report P3). Amounts are in the budget's currency.","properties":{"alert":{"$ref":"#/components/schemas/PhaseBurnAlert","description":"Typed alert: `none` under budget, `warn` past 80%, `over` past 100%."},"budget_minor":{"description":"The contracted budget (minor units); `None` = unbudgeted phase.","format":"int64","type":["integer","null"]},"consumed_minor":{"description":"Consumed value in minor units (unit_amount_minor per-line pricing\nfolded up; 0 when the phase carries a pure fee).","format":"int64","type":"integer"},"consumed_minutes":{"description":"Consumed minutes (approved billable entries scoped to the phase).","format":"int64","type":"integer"},"currency":{"description":"Currency of both amounts.","type":["string","null"]},"phase_id":{"format":"uuid","type":"string"},"phase_name":{"type":"string"},"phase_status":{"$ref":"#/components/schemas/PhaseStatus"}},"required":["phase_id","phase_name","phase_status","consumed_minutes","consumed_minor","alert"],"type":"object"},"PhaseBurnAlert":{"description":"The phase-burn alert state. `warn` fires at ≥80% consumed; `over` at\n≥100%. Never silent: the read model carries the label, the UI renders it.","enum":["none","warn","over"],"type":"string"},"PhaseGanttView":{"description":"One phase as the Gantt draws it, with its budget burn attached.","properties":{"budget_minor":{"format":"int64","type":["integer","null"]},"burn_alert":{"description":"`none` under budget, `warn` at/over 80%, `over` at/over 100%.\n`None` for an unbudgeted phase, because \"no budget\" is not \"under\nbudget\" and rendering it green would be a false reassurance.","type":["string","null"]},"completed_at":{"type":["string","null"]},"consumed_minor":{"format":"int64","type":"integer"},"consumed_minutes":{"format":"int64","type":"integer"},"currency":{"type":["string","null"]},"end_date":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_milestone":{"type":"boolean"},"name":{"type":"string"},"planned_days":{"description":"Planned days, inclusive of both ends. `None` when undated.","format":"int64","type":["integer","null"]},"sort_order":{"format":"int32","type":"integer"},"start_date":{"type":["string","null"]},"status":{"type":"string"}},"required":["id","name","sort_order","status","is_milestone","consumed_minor","consumed_minutes"],"type":"object"},"PhaseMutationResponse":{"allOf":[{"$ref":"#/components/schemas/ProjectPhase"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}]},"PhaseStatus":{"description":"Lifecycle of a phase.","enum":["pending","active","completed","cancelled"],"type":"string"},"PickMutationResponse":{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"pick":{"$ref":"#/components/schemas/TicketPickView"}},"required":["pick","mutation"],"type":"object"},"PipelineDeal":{"description":"An opportunity: one client, optionally one quote, one current stage.","properties":{"amount_minor":{"format":"int64","type":"integer"},"client_id":{"format":"uuid","type":"string"},"closed_at":{"description":"Set when the deal lands on a won/lost stage.","format":"date-time","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"currency":{"type":"string"},"expected_close_on":{"format":"date","type":["string","null"]},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"notes":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"owner_user_id":{"format":"uuid","type":["string","null"]},"quote_id":{"format":"uuid","type":["string","null"]},"stage_id":{"format":"uuid","type":"string"},"status":{"$ref":"#/components/schemas/DealStatus"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","client_id","stage_id","name","amount_minor","currency","status","created_at","updated_at"],"type":"object"},"PipelineDealTransition":{"description":"One immutable stage move (or the initial placement, with a NULL\n`from_stage_id`). Appended in the same transaction as the move.","properties":{"actor_id":{"format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"deal_id":{"format":"uuid","type":"string"},"from_stage_id":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"note":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"to_stage_id":{"format":"uuid","type":"string"}},"required":["id","org_id","deal_id","to_stage_id","created_at"],"type":"object"},"PipelineStage":{"description":"One ordered stage of the org's pipeline.","properties":{"active":{"type":"boolean"},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"position":{"format":"int32","type":"integer"},"stage_type":{"$ref":"#/components/schemas/StageType"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","name","position","stage_type","active","created_at","updated_at"],"type":"object"},"PluginCapability":{"description":"The CLOSED capability vocabulary a v2 manifest may declare and an\ninstall may be granted. Capabilities are declarative names that map\nonto the EXISTING permission matrix — a grant never creates a new\npermission, it resolves to a standard [`Permission`] and every check\nflows through `core_domain::policy` like any human actor's. There is\ndeliberately NO admin capability: `special:manage_plugins`, user\nmanagement, billing, and audit-log specials are human-only grants a\nplugin can never hold (deny-by-default — the vocabulary simply has\nno key for them).","enum":["TicketsRead","TicketsWrite","ClientsRead","ClientsWrite","TimeRead","TimeWrite","ProjectsRead","ProjectsWrite","KbRead","KbWrite","DocumentsRead","DocumentsWrite","ReportingRead","FinancialRead","VaultIngress","RoutingMatrix","RoutingRoute","RoutingGeocode","RoutingIsochrone","UiWidgets","AutomationActions","ReportsRender","FieldsValidate","AutomationTriggers","AutomationReads","EventsEmit","ServiceAccountManage","OrgUsageRead","RagRead","RagWrite","OrgCustomDomainManage","OrgSuspensionRead","ObservabilityRead","DirectoryGroupsRead","DirectoryGroupsWrite"],"type":"string"},"PluginInstallStatus":{"description":"Install lifecycle. Deny-by-default: `Disabled` until an explicit\nadmin enable; `Revoked` is terminal (registry revocation or explicit\nrevoke) — a revoked install never runs again until re-installed.","enum":["disabled","enabled","revoked"],"type":"string"},"PluginManifestBody":{"description":"The declarative manifest as submitted. `interface_version` is\nserver-pinned (the runtime speaks exactly one version), so a caller\ncannot publish an entry the runtime would refuse.","properties":{"artifact_kind":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PluginRuntimeKind","description":"ADR 0009 §6 — which runtime the published artifact speaks. Absent\n⇒ `wasm`, byte-identical to every request body published before\nWave 43 lane 4. Without this field the route FORCED wasm: a\nconnector's `connector_profile` is refused on a wasm manifest, so\n`POST /api/plugins/catalog` could never publish the artifact kind\nWave 43 lane 4's outbound seeding actually needs. `validate()` is\ncalled on the built manifest, so an incoherent combination is\nstill rejected here rather than at install time."}]},"capabilities":{"items":{"type":"string"},"type":"array"},"connector_profile":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ConnectorProfile","description":"ADR 0009 §6 full semantics (Wave 19 lane 2) — the modeled\nconnector governance profile (webhook endpoints + scopes).\nValid only on http_connector manifests; wasm manifests refuse it."}]},"description":{"type":"string"},"settings_schema":{"description":"ADR 0009 §4 — optional JSON-Schema for manifest-rendered settings."},"surface_profile":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PluginSurfaceProfile","description":"§4.6 (Wave 34 lane 4, amended per kind by ADR 0017 §3 in Wave 43\nlane 4) — the declarative surface profile. On a wasm manifest every\nsection is available; on an http_connector the three UI sections\nrefuse and the three executor sections\n(automation_action / automation_trigger / read) are allowed."}]}},"required":["capabilities"],"type":"object"},"PluginManifestV2":{"description":"The declarative v2 plugin manifest: name/version/capabilities — a\nDATA document describing what a plugin IS and what it may ask for.\nIt is never executable content: the runtime executes nothing from a\nmanifest, and \"install\" only ever adopts a catalog entry's manifest\nsnapshot.","properties":{"artifact_kind":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PluginRuntimeKind","description":"ADR 0009 §6 — the artifact GOVERNANCE profile the manifest\ndeclares: WASM executables and HTTP connectors ship in one\nlifecycle track but distinct tabs. Absent ⇒ `wasm` (the\ndeclarative manifest/permission baseline every v1 manifest was\npublished as); connectors must DECLARE the kind. Additive to the\nv2 shape; unknown keys reject at deserialization (closed\nvocabulary)."}]},"capabilities":{"description":"The capabilities the plugin MAY be granted. Grants at install\ntime are a subset of this list — a grant outside the manifest's\ndeclared set is rejected.","items":{"type":"string"},"type":"array"},"connector_profile":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ConnectorProfile","description":"ADR 0009 §6 full semantics (Wave 19 lane 2) — the connector\ngovernance profile: webhook endpoints + outbound scopes as MODELED\nmanifest fields, never free text. Optional on any artifact kind since\nADR 0023 (Wave 53 lane 2): its two connector-only members are\n`webhook_endpoints` and `routing`, and the egress declaration\n(`outbound_scopes`) is legal on a wasm manifest too, because that is\nwhere `http_request` reads its allowlist from. Declared endpoints are\nthe connector's egress-allowlist seed (the same posture the WASM\nresource profile carries); declared scopes render on the consent\nscreen verbatim so an admin consents to exactly the SaaS scopes the\nconnector asks for."}]},"description":{"type":"string"},"interface_version":{"description":"The interface version this manifest was written against; must\nequal [`PLUGIN_RUNTIME_INTERFACE_VERSION`] or the runtime refuses\nthe entry.","format":"int32","minimum":0,"type":"integer"},"name":{"type":"string"},"settings_schema":{"description":"ADR 0009 §4 — the optional JSON-Schema the manifest declares for\nits per-install settings. The Plugins surface renders config FROM\nthis schema (typed-registry doctrine: no hand-authored\nper-plugin pages); a manifest with no schema gets\nconsent/enable/disable only. Additive to the v2 shape."},"surface_profile":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PluginSurfaceProfile","description":"Wave 34 lane 4 (§4.6) — the declarative UI-surface profile:\nwidgets, automation action kinds, report render kinds, custom-field\nvalidators, trigger sources, and read nodes.\n\nThe rule is PER KIND (ADR 0017 §3, Wave 43 lane 4). On a `wasm`\nmanifest every section is available. On an `http_connector` the\nthree UI sections (`widget`, `report_render`, `field_validator`)\nrefuse — a connector has no in-product UI surface to govern — while\nthe three EXECUTOR sections (`automation_action`,\n`automation_trigger`, `read`) are allowed, because a connector\nalready holds credentials and network egress and a sandbox does\nnot. Optional on both: an EMPTY profile carries no consent weight.\nConsent renders the declarations verbatim, and each section only\nregisters when its capability (`ui.widgets` /\n`automation.actions` / `reports.render` / `fields.validate` /\n`automation.triggers` / `automation.reads`) is granted. Additive to\nthe v2 shape."}]},"version":{"type":"string"}},"required":["name","version","interface_version","capabilities"],"type":"object"},"PluginNodeListView":{"properties":{"declared_triggers":{"description":"The event types the org's plugins declare as triggers. They belong\nto the trigger catalogue, not to this list, because a plugin\ntrigger arrives through the signed ingest and needs no surface\nnode at all.","items":{"type":"string"},"type":"array"},"nodes":{"items":{"$ref":"#/components/schemas/PluginNodeView"},"type":"array"}},"required":["nodes","declared_triggers"],"type":"object"},"PluginNodeView":{"description":"One plugin-contributed node as the palette and the picker consume it.\nOnly live entries are listed — a revoked install's rows are gone, not\npresent-and-broken — and the entry carries its install so the canvas\ncan say whose node it is.","properties":{"agent_description":{"type":"string"},"audience":{"$ref":"#/components/schemas/NodeAudience"},"emits":{"items":{"type":"string"},"type":"array"},"idempotent":{"type":"boolean"},"input_schema":{"type":["object","null"]},"install_id":{"format":"uuid","type":"string"},"kind":{"description":"`action`, `read` or `trigger`. A `trigger` is an EVENT TYPE the\nconnector declares through the signed ingest, not a node — the\ntrigger catalogue makes it subscribable.","type":"string"},"label":{"type":"string"},"node_id":{"type":"string"},"output_schema":{"type":["object","null"]}},"required":["node_id","kind","install_id","label","agent_description","audience","idempotent","emits"],"type":"object"},"PluginPublisherRevoked":{"description":"`plugin.publisher_revoked` — the org-side vouch was withdrawn;\nthe badge disappears and history is preserved.","properties":{"publisher":{"type":"string"},"revoked_at":{"format":"date-time","type":"string"},"revoked_by":{"format":"uuid","type":"string"},"verification_id":{"format":"uuid","type":["string","null"]}},"required":["publisher","revoked_by","revoked_at"],"type":"object"},"PluginPublisherVerification":{"description":"Marketplace v2 verified-publisher registry row (Wave 22). A\npublisher is a NORMALIZED SOURCE IDENTITY; verification is per-org\n(this org vouches this publisher), admin-curated, and additive over\nthe v1 distribution formats.","properties":{"display_name":{"type":"string"},"id":{"format":"uuid","type":"string"},"note":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"publisher":{"description":"Normalized identity, e.g. `github.com/owner` or `registry://slug`.","type":"string"},"revoked_at":{"format":"date-time","type":["string","null"]},"revoked_by":{"format":"uuid","type":["string","null"]},"status":{"description":"`verified` | `revoked`.","type":"string"},"verified_at":{"format":"date-time","type":"string"},"verified_by":{"format":"uuid","type":["string","null"]}},"required":["id","org_id","publisher","display_name","status","verified_at","note"],"type":"object"},"PluginPublisherVerified":{"description":"`plugin.publisher_verified` — Marketplace v2: an owner/admin vouched\nfor a publisher identity on behalf of the org (per-org badge, never\nglobal). Additive over the v1 distribution surfaces.","properties":{"display_name":{"type":"string"},"publisher":{"type":"string"},"verification_id":{"format":"uuid","type":["string","null"]},"verified_at":{"format":"date-time","type":"string"},"verified_by":{"format":"uuid","type":"string"}},"required":["publisher","display_name","verified_by","verified_at"],"type":"object"},"PluginRating":{"description":"Marketplace v2 per-user rating on one artifact RELEASE (the\n`{source_kind, source_url, artifact_digest}` triple). One rating per\nuser per release; re-rating is an upsert.","properties":{"artifact_digest":{"description":"sha256 hex of the artifact release.","type":"string"},"comment":{"type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"rated_at":{"format":"date-time","type":"string"},"rated_by":{"format":"uuid","type":["string","null"]},"rating":{"description":"1..=5 stars.","format":"int32","type":"integer"},"source_kind":{"$ref":"#/components/schemas/PluginSourceKind"},"source_url":{"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","source_kind","source_url","artifact_digest","rating","comment","rated_at","updated_at"],"type":"object"},"PluginRatingRecorded":{"description":"`plugin.rating_recorded` — Marketplace v2 social layer: one staff\nmember's 1–5 star rating on an artifact release (upsert semantics —\na re-rate wins and the event fires once per upsert).","properties":{"artifact_digest":{"type":"string"},"comment":{"type":"string"},"rated_at":{"format":"date-time","type":"string"},"rated_by":{"format":"uuid","type":"string"},"rating":{"format":"int32","type":"integer"},"source_kind":{"description":"The artifact release triple.","type":"string"},"source_url":{"type":"string"}},"required":["source_kind","source_url","artifact_digest","rating","comment","rated_by","rated_at"],"type":"object"},"PluginRuntimeKind":{"description":"The plugin-side artifact vocabulary for the roster tabs (WASM vs HTTP\nconnector). Skills are NOT in this closed set — the skill half lives\non `PluginArtifactKind::Skill` in the distribution seam (0079). Parse\nis strict: a manifest naming an ungovernable kind cannot exist.","enum":["wasm","http_connector"],"type":"string"},"PluginSettingsUpdated":{"description":"`plugin.settings_updated` — a per-install config rendered FROM the\npinned manifest's settings JSON-Schema changed (validated at the\nwrite boundary; no hand-authored per-plugin settings pages exist).","properties":{"config_keys":{"description":"The validated config keys (values stay server-side; consumers\nrefetch through the settings surface).","items":{"type":"string"},"type":"array"},"install_id":{"format":"uuid","type":"string"},"occurred_at":{"format":"date-time","type":"string"},"slug":{"type":"string"},"updated_by":{"format":"uuid","type":"string"},"version":{"type":"string"}},"required":["install_id","slug","version","config_keys","updated_by","occurred_at"],"type":"object"},"PluginSourceKind":{"description":"The closed source vocabulary — every distribution adapter records the\nsame provenance chain, typed by the kind of source it is.","enum":["marketplace_registry","github_repo","local_zip","skill_community","manual_upload"],"type":"string"},"PluginStagedImportApproved":{"description":"`plugin.staged_import_approved` — an owner/admin reviewed the consent\ndiff and the wait ended in adoption: the catalog entry/version +\ninstall were minted (plugins) or the agent skill was imported\n(skills), provenance recorded, still deny-by-default on enable.","properties":{"adopted_install_id":{"format":"uuid","type":["string","null"]},"adopted_skill_id":{"format":"uuid","type":["string","null"]},"approved_at":{"format":"date-time","type":"string"},"artifact_kind":{"type":"string"},"granted_capabilities":{"items":{"type":"string"},"type":"array"},"review_note":{"type":["string","null"]},"reviewed_by":{"format":"uuid","type":"string"},"staged_item_id":{"format":"uuid","type":"string"}},"required":["staged_item_id","reviewed_by","artifact_kind","granted_capabilities","approved_at"],"type":"object"},"PluginStagedImportCreated":{"description":"`plugin.staged_import_created` — a member upload (skill or plugin)\nlanded staged: validated, never executed, visible only to its\nuploader and admins. Capabilities are the staged manifest's DECLARED\nkeys; consent happens at approval, never here.","properties":{"artifact_digest":{"type":"string"},"artifact_kind":{"description":"`plugin` | `skill`.","type":"string"},"declared_capabilities":{"items":{"type":"string"},"type":"array"},"fetched_ref":{"type":["string","null"]},"source_kind":{"description":"`marketplace_registry` | `github_repo` | `local_zip` |\n`skill_community` | `manual_upload`.","type":"string"},"source_url":{"type":"string"},"staged_item_id":{"format":"uuid","type":"string"},"uploaded_at":{"format":"date-time","type":"string"},"uploaded_by":{"format":"uuid","type":"string"}},"required":["staged_item_id","artifact_kind","source_kind","source_url","artifact_digest","declared_capabilities","uploaded_by","uploaded_at"],"type":"object"},"PluginStagedImportRefused":{"description":"`plugin.staged_import_refused` — the reviewer declined the staged\nimport (terminal for that row; the uploader may re-stage fresh).","properties":{"refused_at":{"format":"date-time","type":"string"},"review_note":{"type":["string","null"]},"reviewed_by":{"format":"uuid","type":"string"},"staged_item_id":{"format":"uuid","type":"string"}},"required":["staged_item_id","reviewed_by","refused_at"],"type":"object"},"PluginSurfaceProfile":{"description":"Wave 34 lane 4 (§4.6): the modeled UI-surface profile a WASM manifest\nmay declare — dashboard/ticket-pane WIDGETS, automation ACTION KINDS,\nreport RENDER KINDS, and custom-field VALIDATORS. Everything here is\nDECLARATIVE: closed render/effect vocabularies plus JSON-Schema\nsubsets the core validates and executes. A plugin ships declarations,\nnever code the browser or the rule engine runs raw (agent-safety\n§4.8).","properties":{"actions":{"description":"Automation action kinds registered alongside the built-ins.","items":{"$ref":"#/components/schemas/SurfaceAction"},"type":"array"},"field_validators":{"description":"JSON-Schema validators for board custom fields.","items":{"$ref":"#/components/schemas/SurfaceFieldValidator"},"type":"array"},"reads":{"description":"Wave 43 lane 4 (ADR 0017 §3, §5): READ nodes, the only kind that\nlets a workflow FETCH from an external system. Every entry carries\nits own guardrails; there is no default that weakens them.","items":{"$ref":"#/components/schemas/SurfaceRead"},"type":"array"},"render_kinds":{"description":"Render kinds contributed to the report builder.","items":{"$ref":"#/components/schemas/SurfaceRenderKind"},"type":"array"},"triggers":{"description":"Wave 43 lane 4 (ADR 0017 §3): TRIGGER sources the plugin\ncontributes — the event type a rule can fire on. This is the half\nof the `automation_action` symmetry that was missing: a plugin\ncould contribute an action but never a trigger.","items":{"$ref":"#/components/schemas/SurfaceTrigger"},"type":"array"},"widgets":{"description":"Widgets the plugin registers (dashboard or ticket-pane slots).","items":{"$ref":"#/components/schemas/SurfaceWidget"},"type":"array"}},"type":"object"},"PluginUpdatePending":{"description":"`plugin.update_pending` — an update proposal is waiting on the\nre-consent diff: the OLD version stays pinned and running until the\nowner/admin consents; consent commits the swap.","properties":{"added_capabilities":{"items":{"type":"string"},"type":"array"},"catalog_id":{"format":"uuid","type":"string"},"current_version":{"description":"The version still running (pinned until consented).","type":"string"},"install_id":{"format":"uuid","type":"string"},"occurred_at":{"format":"date-time","type":"string"},"pending_digest":{"type":"string"},"pending_version":{"description":"The candidate version awaiting consent.","type":"string"},"removed_capabilities":{"items":{"type":"string"},"type":"array"},"slug":{"type":"string"}},"required":["install_id","catalog_id","slug","current_version","pending_version","pending_digest","added_capabilities","removed_capabilities","occurred_at"],"type":"object"},"PmRun":{"description":"One append-only PM run ledger row.","properties":{"created_at":{"format":"date-time","type":"string"},"detail":{},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"schedule_id":{"format":"uuid","type":"string"},"slot_at":{"format":"date-time","type":"string"},"status":{"description":"\"generated\" | \"skipped\"","type":"string"},"ticket_id":{"format":"uuid","type":["string","null"]}},"required":["id","org_id","schedule_id","slot_at","status","detail","created_at"],"type":"object"},"PmSchedule":{"description":"A recurring/scheduled ticket (PM schedule): a ticket template plus the\ntyped cadence (same shape as report schedules). The worker claims due\nrows; core-api generates the instance and advances the cadence.","properties":{"assignee_id":{"format":"uuid","type":["string","null"]},"board_id":{"format":"uuid","type":"string"},"body":{"type":["string","null"]},"cadence":{"$ref":"#/components/schemas/Cadence"},"client_id":{"format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":"string"},"day_of_month":{"description":"1–28; monthly only.","format":"int32","type":["integer","null"]},"day_of_week":{"description":"0 = Monday .. 6 = Sunday; weekly only.","format":"int32","type":["integer","null"]},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"last_error":{"type":["string","null"]},"last_run_at":{"format":"date-time","type":["string","null"]},"name":{"type":"string"},"next_run_at":{"format":"date-time","type":"string"},"org_id":{"format":"uuid","type":"string"},"priority":{"$ref":"#/components/schemas/Priority"},"site_id":{"format":"uuid","type":["string","null"]},"time_of_day":{"type":"string"},"timezone":{"type":"string"},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","name","board_id","title","priority","cadence","time_of_day","timezone","enabled","next_run_at","created_by","created_at","updated_at"],"type":"object"},"PodVisibilityView":{"properties":{"config":{"description":"Pod id → RBAC level → visible. Absent entries fall back to the\nclient registry defaults."},"updated_at":{"format":"date-time","type":"string"},"updated_by":{"format":"uuid","type":["string","null"]}},"required":["config","updated_at"],"type":"object"},"PolicyView":{"properties":{"calendar_id":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_default":{"type":"boolean"},"name":{"type":"string"},"targets":{"description":"Targets keyed by priority (\"p1\"..\"p4\"), business minutes."},"uses_embedded_calendar":{"description":"True while the policy governs itself via its embedded calendar.","type":"boolean"}},"required":["id","name","is_default","targets","uses_embedded_calendar"],"type":"object"},"PortalAppointmentRequestMutationResponse":{"allOf":[{"$ref":"#/components/schemas/AppointmentRequest"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2) for appointment-request writes.\nThe request fields sit at the TOP LEVEL (flattened) so the stored\nreplay payload (`request` serialized + merged mutation metadata)\nround-trips byte-identically."},"PortalArticleListQuery":{"properties":{"category_id":{"format":"uuid","type":["string","null"]},"limit":{"format":"int64","type":"integer"},"offset":{"format":"int64","type":"integer"}},"type":"object"},"PortalAttachmentView":{"description":"Portal attachment view: metadata only (bytes stream on download).","properties":{"content_type":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"filename":{"type":"string"},"id":{"format":"uuid","type":"string"},"sha256":{"type":"string"},"size_bytes":{"format":"int64","type":"integer"}},"required":["id","filename","content_type","size_bytes","sha256","created_at"],"type":"object"},"PortalCancelRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."}],"description":"Body for the portal cancel (idempotency parameters only)."},"PortalCreateAppointmentRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key."},{"properties":{"ends_at":{"format":"date-time","type":"string"},"kind":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AppointmentKind","description":"`on_site | remote` (internal/personal are staff-only kinds)."}]},"notes":{"type":["string","null"]},"starts_at":{"format":"date-time","type":"string"},"title":{"type":"string"},"user_id":{"description":"The staff member whose calendar the client asked for.","format":"uuid","type":"string"}},"required":["user_id","title","starts_at","ends_at"],"type":"object"}]},"PortalCsatSubmit":{"properties":{"comment":{"type":["string","null"]},"score":{"description":"1 (poor) – 5 (excellent).","format":"int32","type":"integer"}},"required":["score"],"type":"object"},"PortalCsatView":{"description":"Portal CSAT survey state as the in-app response route sees it.","properties":{"expires_at":{"format":"date-time","type":"string"},"status":{"description":"`valid` renders the form; `responded` renders the thank-you state.","type":"string"},"ticket_id":{"format":"uuid","type":"string"},"ticket_number":{"type":"string"},"ticket_subject":{"type":"string"}},"required":["ticket_id","ticket_number","ticket_subject","status","expires_at"],"type":"object"},"PortalInvoice":{"description":"The client-facing invoice document view. Deliberately NOT the staff\nentity: dunning internals and staff notes never reach the portal.","properties":{"created_at":{"format":"date-time","type":"string"},"currency":{"type":"string"},"due_on":{"format":"date","type":"string"},"id":{"format":"uuid","type":"string"},"issued_at":{"format":"date","type":"string"},"number":{"type":"string"},"outstanding_minor":{"description":"`total_minor - total_paid_minor`, never negative.","format":"int64","type":"integer"},"period_from":{"format":"date","type":["string","null"]},"period_to":{"format":"date","type":["string","null"]},"status":{"description":"`issued | paid` (void/draft documents are staff-internal).","type":"string"},"total_minor":{"format":"int64","type":"integer"},"total_paid_minor":{"format":"int64","type":"integer"}},"required":["id","number","status","currency","issued_at","due_on","total_minor","total_paid_minor","outstanding_minor","created_at"],"type":"object"},"PortalInvoiceDetail":{"description":"The client-facing invoice aggregate.","properties":{"invoice":{"$ref":"#/components/schemas/PortalInvoice"},"lines":{"items":{"$ref":"#/components/schemas/InvoiceLine"},"type":"array"},"payments":{"items":{"$ref":"#/components/schemas/PortalPaymentView"},"type":"array"}},"required":["invoice","lines","payments"],"type":"object"},"PortalKbSearchHit":{"properties":{"article":{"$ref":"#/components/schemas/KbArticle"},"score":{"description":"FTS rank blended with trigram similarity — ordering signal only.","format":"double","type":"number"}},"required":["article","score"],"type":"object"},"PortalKbSearchQuery":{"properties":{"limit":{"format":"int64","type":"integer"},"offset":{"format":"int64","type":"integer"},"q":{"description":"Websearch query (plain words, quoted phrases, `OR`, `-`\nexclusions — Postgres websearch syntax).","type":"string"}},"required":["q"],"type":"object"},"PortalLoginRequest":{"properties":{"email":{"type":"string"},"password":{"type":"string"}},"required":["email","password"],"type":"object"},"PortalLoginResponse":{"properties":{"access_token":{"type":"string"},"expires_in":{"format":"int64","type":"integer"},"portal_user":{"$ref":"#/components/schemas/PortalProfile"},"refresh_token":{"type":"string"},"session_id":{"format":"uuid","type":"string"}},"required":["access_token","refresh_token","session_id","expires_in","portal_user"],"type":"object"},"PortalMagicLinkConfirm":{"properties":{"token":{"type":"string"}},"required":["token"],"type":"object"},"PortalMagicLinkRequest":{"properties":{"email":{"type":"string"}},"required":["email"],"type":"object"},"PortalPayRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + If-Match\n`version` on the invoice."},{"properties":{"amount_minor":{"description":"Amount to settle, minor units; must not exceed the outstanding\nbalance. Currency is always the invoice's own.","format":"int64","type":"integer"},"notes":{"type":["string","null"]},"payment_method":{"description":"PSP payment-method surface (e.g. a confirmed Stripe `pm_*` id\nfrom the embedded payment form). None = ledger-only none-mode\nsettlement; required when the org resolved provider is Stripe.","type":["string","null"]}},"required":["amount_minor"],"type":"object"}]},"PortalPayResponse":{"description":"Instant-first mutation response (IF2) for a portal payment: the\nreceipt, the refreshed client-facing invoice, and the additive\n`mutation` metadata. Stored/replayed byte-identically under the key.","properties":{"invoice":{"$ref":"#/components/schemas/PortalInvoice"},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"payment":{"$ref":"#/components/schemas/InvoicePayment"}},"required":["payment","invoice","mutation"],"type":"object"},"PortalPaymentView":{"description":"A payment receipt as the portal sees it. `created_by` stays internal\n(portal identities are not staff rows; the field is always NULL on\nportal-recorded rows anyway).","properties":{"amount_minor":{"format":"int64","type":"integer"},"currency":{"type":"string"},"id":{"format":"uuid","type":"string"},"provider":{"description":"`'none'` while no external PSP is connected — the receipt is a\nledger entry, not a card charge.","type":"string"},"received_at":{"format":"date-time","type":"string"}},"required":["id","amount_minor","currency","provider","received_at"],"type":"object"},"PortalProfile":{"description":"The signed-in portal identity (also served by GET /api/portal/me).","properties":{"client_id":{"format":"uuid","type":"string"},"display_name":{"type":"string"},"email":{"type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"role":{"$ref":"#/components/schemas/PortalRole"}},"required":["id","org_id","client_id","email","display_name","role"],"type":"object"},"PortalQuote":{"description":"The client-facing quote view. Deliberately NOT the staff entity: the\ncreator provenance and conversion seam ids stay staff-internal — the\nportal sees the document, its validity, and its lifecycle state.","properties":{"created_at":{"format":"date-time","type":"string"},"currency":{"type":"string"},"decided_at":{"format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"issued_on":{"format":"date","type":"string"},"notes":{"type":["string","null"]},"number":{"type":"string"},"sent_at":{"format":"date-time","type":["string","null"]},"status":{"description":"`sent | accepted | declined | expired` (drafts are staff-internal).","type":"string"},"subtotal_minor":{"format":"int64","type":"integer"},"tax_minor":{"format":"int64","type":"integer"},"tax_rate_bps":{"format":"int32","type":"integer"},"terms":{"type":["string","null"]},"total_minor":{"format":"int64","type":"integer"},"valid_until":{"description":"Last day the quote can be accepted; shown honestly on the portal.","format":"date","type":"string"}},"required":["id","number","status","currency","issued_on","valid_until","subtotal_minor","tax_rate_bps","tax_minor","total_minor","created_at"],"type":"object"},"PortalQuoteDecisionRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"reason":{"description":"Optional reason, recorded in the audit trail (decline-with-reason).","type":["string","null"]}},"type":"object"}],"description":"A quote decision body (approve/decline): envelope params only; the\ntarget status is the URL. The SAME instant-first envelope the staff\nlifecycle legs use."},"PortalQuoteDecisionResponse":{"allOf":[{"$ref":"#/components/schemas/PortalQuote"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2) for a portal quote decision."},"PortalQuoteDetail":{"allOf":[{"$ref":"#/components/schemas/PortalQuote"},{"properties":{"lines":{"items":{"$ref":"#/components/schemas/PortalQuoteLine"},"type":"array"}},"required":["lines"],"type":"object"}],"description":"The quote aggregate as the portal sees it: document + lines."},"PortalQuoteLine":{"description":"A client-facing quote line: description/qty/price/amount only.","properties":{"amount_minor":{"format":"int64","type":"integer"},"description":{"type":"string"},"position":{"format":"int32","type":"integer"},"quantity_milli":{"description":"Quantity in thousandths of a unit (1.5 = 1500).","format":"int64","type":"integer"},"taxable":{"type":"boolean"},"unit":{"type":"string"},"unit_amount_minor":{"format":"int64","type":"integer"}},"required":["description","unit","quantity_milli","unit_amount_minor","taxable","amount_minor","position"],"type":"object"},"PortalRefreshRequest":{"properties":{"refresh_token":{"type":["string","null"]},"session_id":{"format":"uuid","type":["string","null"]}},"type":"object"},"PortalReplyRequest":{"properties":{"body":{"type":"string"}},"required":["body"],"type":"object"},"PortalRole":{"description":"Portal role tiers. `contact` is the default read/reply tier;\n`approver` is behaviorally identical in v1 but exists so approval\ngrants (Stage 3 quote approval / Stage 4 optional client approvals)\ncan be layered on without re-provisioning identities; `it_manager`\nADDITIONALLY reads `in_house_update` notes. NO tier ever reads\n`internal` notes — enforced by `notes::visible_kinds`, tested in\n`policy` and `notes`.","enum":["contact","approver","it_manager"],"type":"string"},"PortalSlotInterval":{"description":"Free intervals for one staff member over the query range.","properties":{"ends_at":{"format":"date-time","type":"string"},"starts_at":{"format":"date-time","type":"string"}},"required":["starts_at","ends_at"],"type":"object"},"PortalSlotsQuery":{"properties":{"from":{"description":"Range start (inclusive), ISO date.","format":"date","type":"string"},"to":{"description":"Range end (inclusive), ISO date.","format":"date","type":"string"},"user_id":{"description":"Optional: narrow to one staff member.","format":"uuid","type":["string","null"]}},"required":["from","to"],"type":"object"},"PortalStaffSlots":{"description":"One staff member's published free slots over the query range.","properties":{"display_name":{"type":"string"},"slots":{"description":"Sorted, internally disjoint, contiguous FREE stretches. A client\nrequests a slice of one; the server re-validates at request time\nand again at approval.","items":{"$ref":"#/components/schemas/PortalSlotInterval"},"type":"array"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","display_name","slots"],"type":"object"},"PortalTicketDetail":{"properties":{"can_view_in_house_updates":{"type":"boolean"},"comments":{"description":"Only the kinds this tier may read, filtered by the domain policy.","items":{"$ref":"#/components/schemas/TicketComment"},"type":"array"},"ticket":{"$ref":"#/components/schemas/Ticket"}},"required":["ticket","comments","can_view_in_house_updates"],"type":"object"},"PortalTicketListQuery":{"properties":{"board_id":{"format":"uuid","type":["string","null"]},"category":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TicketStatusCategory"}]},"limit":{"format":"int64","type":"integer"},"offset":{"format":"int64","type":"integer"}},"type":"object"},"PortalTicketView":{"allOf":[{"$ref":"#/components/schemas/Ticket"},{"properties":{"status_id":{"description":"The status id, so a client-side filter can group without guessing.","format":"uuid","type":"string"},"status_label":{"description":"The status's own label, when the org allows clients to see it.","type":["string","null"]}},"required":["status_id"],"type":"object"}],"description":"A ticket as a CLIENT sees it (Wave 41 lane 12).\n\nThe ticket fields are flattened exactly as they were, so the portal client\nkeeps reading the same keys; two status fields are added. `status_label`\nis `None` when the status is not client-visible, and the client shows the\ncategory word instead — which is the whole point of the per-status switch\nin Settings: it changes what a client is told, in words, without hiding\nwhere the ticket stands."},"PortalTierView":{"description":"One portal tier in the preset roster (RB1). Portal authorization is\ndeliberately NOT the staff matrix (see `core_domain::policy`) — the\ntiers render here as data with their capability keys, but they are\nper-contact settings, never staff `roles` rows and never forkable.","properties":{"capabilities":{"description":"Capability keys from the tier×capability matrix (`policy::portal_tier_allows`).","items":{"type":"string"},"type":"array"},"description":{"type":"string"},"name":{"type":"string"},"tier":{"type":"string"}},"required":["tier","name","description","capabilities"],"type":"object"},"PortalUser":{"properties":{"client_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"display_name":{"type":"string"},"email":{"type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"role":{"$ref":"#/components/schemas/PortalRole"},"status":{"$ref":"#/components/schemas/PortalUserStatus"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","client_id","email","display_name","role","status","created_at","updated_at"],"type":"object"},"PortalUserStatus":{"description":"Lifecycle of a portal identity. `invited` = admin-provisioned, has not\nlogged in yet; first successful login flips to `active`. `disabled`\nblocks login AND every authenticated portal request (checked per\nrequest, never cached in the token).","enum":["invited","active","disabled"],"type":"string"},"PreferenceView":{"description":"One resolved preference row: which chain link supplied it (`user`,\n`org_default`, or `builtin`), the channel toggles, and the quiet-hours\nwindow with its timezone.","properties":{"email":{"type":"boolean"},"in_app":{"type":"boolean"},"kind":{"$ref":"#/components/schemas/NotificationKind"},"quiet_from":{"type":["string","null"]},"quiet_until":{"type":["string","null"]},"source":{"type":"string"},"timezone":{"type":"string"}},"required":["kind","source","in_app","email","timezone"],"type":"object"},"PresenceRoster":{"description":"GET /api/tickets/{id}/presence response.","properties":{"viewers":{"items":{"$ref":"#/components/schemas/PresenceViewer"},"type":"array"}},"required":["viewers"],"type":"object"},"PresenceViewer":{"description":"One presence viewer on an entity (roster + roster frames).","properties":{"at":{"type":"string"},"editing":{"description":"True while the user is actively editing (vs. just viewing).","type":"boolean"},"name":{"type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","name","at","editing"],"type":"object"},"PresetView":{"description":"The canonical definition of one shipped preset (RB1): the presets stay\ncanonical even though every org carries its own rows — restore resets\na row to this set.","properties":{"builtin":{"type":"string"},"description":{"type":"string"},"name":{"type":"string"},"permissions":{}},"required":["builtin","name","description","permissions"],"type":"object"},"PreviewLabelRequest":{"description":"Draft render body (the designer preview). Same shape as create minus the\ntemplate identity: nothing is persisted.","properties":{"barcode_token":{"type":["string","null"]},"dpi":{"format":"int32","type":["integer","null"]},"fields":{"items":{"type":"string"},"type":"array"},"height_mm":{"format":"double","type":["number","null"]},"include_qr":{"type":"boolean"},"name":{"type":["string","null"]},"purpose":{"type":"string"},"size_preset":{"type":"string"},"width_mm":{"format":"double","type":["number","null"]}},"required":["purpose","size_preset"],"type":"object"},"PreviewQuery":{"properties":{"from":{"description":"Window start (inclusive), ISO date.","format":"date","type":["string","null"]},"to":{"description":"Window end (inclusive), ISO date.","format":"date","type":["string","null"]}},"type":"object"},"PricingFeedApplyResponse":{"properties":{"batch_id":{"format":"uuid","type":"string"},"matched":{"format":"int64","type":"integer"},"outcomes":{"items":{"$ref":"#/components/schemas/PricingFeedOutcomeView"},"type":"array"},"unmatched":{"format":"int64","type":"integer"},"unmatched_skus":{"items":{"type":"string"},"type":"array"}},"required":["batch_id","matched","unmatched","unmatched_skus","outcomes"],"type":"object"},"PricingFeedBatchRequest":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/PricingFeedEntryRequest"},"type":"array"},"note":{"type":["string","null"]},"source":{"type":"string"}},"required":["source","entries"],"type":"object"},"PricingFeedBatchResponse":{"properties":{"batch_id":{"format":"uuid","type":"string"}},"required":["batch_id"],"type":"object"},"PricingFeedEntryRequest":{"properties":{"cost_minor":{"format":"int64","type":"integer"},"currency":{"type":"string"},"sku":{"type":"string"},"unit_price_minor":{"format":"int64","type":"integer"}},"required":["sku","currency","unit_price_minor","cost_minor"],"type":"object"},"PricingFeedOutcomeView":{"properties":{"matched":{"type":"boolean"},"reason":{"type":"string"},"sku":{"type":"string"}},"required":["sku","matched","reason"],"type":"object"},"Priority":{"description":"Industry-standard four-tier priority.","enum":["p1","p2","p3","p4"],"type":"string"},"Project":{"description":"A project engagement under a client (or internal when `client_id` is\n`None`).","properties":{"billing_contract_id":{"description":"The contract this project bills through (Wave 39 lane 4, report\nP2): its rate card, blocks, and caps price the project's T&M work.\n`None` = non-billable or not linked. The link validates in the\napp layer (same client, active-or-draft contract).","format":"uuid","type":["string","null"]},"billing_method":{"$ref":"#/components/schemas/BillingMethod"},"budget_minor":{"description":"Project budget in minor units; `None` = unbudgeted.","format":"int64","type":["integer","null"]},"client_id":{"format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"currency":{"description":"ISO 4217 currency for `budget_minor` (required when a budget is set).","type":["string","null"]},"description":{"type":["string","null"]},"due_date":{"format":"date","type":["string","null"]},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"start_date":{"format":"date","type":["string","null"]},"status":{"$ref":"#/components/schemas/ProjectStatus"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","name","status","billing_method","created_at","updated_at"],"type":"object"},"ProjectBillingRelease":{"description":"One released milestone (report P1/P4): the durable record that a\ncompleted milestone or fixed-fee phase released its amount to the\ninvoicing pipeline. `pending` rows bill on the first run that reaches\nthe window; the run flips them `billed` with the consuming invoice,\nso a replay can never double-bill.","properties":{"amount_minor":{"format":"int64","type":"integer"},"created_at":{"format":"date-time","type":"string"},"currency":{"type":"string"},"id":{"format":"uuid","type":"string"},"invoice_id":{"format":"uuid","type":["string","null"]},"invoice_line_id":{"format":"uuid","type":["string","null"]},"label":{"description":"The milestone name at release time (frozen from the event).","type":"string"},"org_id":{"format":"uuid","type":"string"},"phase_id":{"format":"uuid","type":["string","null"]},"project_id":{"format":"uuid","type":"string"},"released_on":{"format":"date","type":"string"},"status":{"$ref":"#/components/schemas/ReleaseStatus"},"task_id":{"format":"uuid","type":["string","null"]},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","project_id","label","amount_minor","currency","released_on","status","created_at","updated_at"],"type":"object"},"ProjectCostRollupView":{"properties":{"base_currency":{"type":"string"},"bills":{"items":{"$ref":"#/components/schemas/RollupBillView"},"type":"array"},"budget_currency":{"type":["string","null"]},"budget_minor":{"format":"int64","type":["integer","null"]},"po_bill_base_minor":{"format":"int64","type":"integer"},"project_id":{"format":"uuid","type":"string"}},"required":["project_id","base_currency","po_bill_base_minor","bills"],"type":"object"},"ProjectGanttView":{"description":"The project's Gantt: dated phases as bars, undated ones as list items.","properties":{"dated":{"description":"Phases with BOTH dates set, ordered by start then sort_order.","items":{"$ref":"#/components/schemas/PhaseGanttView"},"type":"array"},"project_id":{"format":"uuid","type":"string"},"undated":{"description":"Phases with a missing end, in sort_order. The UI shows these in the\nordered list and says they are not on the timeline, rather than\nplacing them at an invented date.","items":{"$ref":"#/components/schemas/PhaseGanttView"},"type":"array"},"window_from":{"description":"The union of dated windows, so the chart can scale to the plan\ninstead of to \"today\".","type":["string","null"]},"window_to":{"type":["string","null"]}},"required":["project_id","dated","undated"],"type":"object"},"ProjectInstantiateResponse":{"allOf":[{"$ref":"#/components/schemas/Project"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"phase_count":{"format":"int64","type":"integer"},"task_count":{"format":"int64","type":"integer"}},"required":["phase_count","task_count","mutation"],"type":"object"}],"description":"The instantiation result: the applied project plus the expansion\ncounts (clients render \"created N phases, M tasks\" without refetching)."},"ProjectListQuery":{"properties":{"client_id":{"format":"uuid","type":["string","null"]},"limit":{"format":"int64","type":"integer"},"offset":{"format":"int64","type":"integer"},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ProjectStatus"}]}},"type":"object"},"ProjectMutationResponse":{"allOf":[{"$ref":"#/components/schemas/Project"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response for the project entity (IF2): applied\nsnapshot at the top level plus the additive `mutation` metadata."},"ProjectPhase":{"description":"A phase groups tasks into a delivery stage. A phase flagged\n`is_milestone` is itself a billing milestone.","properties":{"completed_at":{"format":"date-time","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"description":{"type":["string","null"]},"end_date":{"description":"Inclusive end date. CHECK-enforced `>= start_date` in the database,\nbecause a bar drawn backwards reads as a rendering bug and is\nactually a data problem the operator needs told about.","format":"date","type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_milestone":{"type":"boolean"},"milestone_amount_minor":{"description":"Billing amount released when this milestone phase completes\n(minor units; `None` = informational milestone).","format":"int64","type":["integer","null"]},"milestone_currency":{"description":"ISO 4217 code for `milestone_amount_minor`.","type":["string","null"]},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"project_id":{"format":"uuid","type":"string"},"sort_order":{"format":"int32","type":"integer"},"start_date":{"description":"The phase's PLANNED window (Wave 34 claims close-out, claim 6).\n\nBoth ends are nullable and that is the honest state rather than a\ncompromise: most real projects have phases nobody has dated yet, and\na NOT NULL pair with a \"today\" default would draw a Gantt that lies\nabout the plan. A phase is drawn on the bar chart only when BOTH\nends are set; a half-dated phase stays in the ordered list and the\nUI says which it is.","format":"date","type":["string","null"]},"status":{"$ref":"#/components/schemas/PhaseStatus"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","project_id","name","sort_order","status","is_milestone","created_at","updated_at"],"type":"object"},"ProjectPhaseBudget":{"description":"A phase's billing budget and/or fixed-fee record (Wave 39 lane 4,\nreport P3/P4). One optional row per phase: `budget_minor` is the\ncontracted denominator the burn math consumes (T&M phases), and\n`fee_minor` is the fixed fee a completed NON-milestone phase releases\n(milestone phases carry their own amount on the phase row).","properties":{"budget_minor":{"description":"Contracted budget for the phase (minor units); `None` = unbudgeted.","format":"int64","type":["integer","null"]},"created_at":{"format":"date-time","type":"string"},"currency":{"description":"ISO 4217 code for `budget_minor`.","type":["string","null"]},"fee_currency":{"description":"ISO 4217 code for `fee_minor`.","type":["string","null"]},"fee_minor":{"description":"Fixed fee released on phase completion (minor units); `None` = no fee.","format":"int64","type":["integer","null"]},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"phase_id":{"format":"uuid","type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","phase_id","created_at","updated_at"],"type":"object"},"ProjectStatus":{"description":"Lifecycle of a project.","enum":["planning","active","on_hold","completed","cancelled"],"type":"string"},"ProjectTask":{"description":"A unit of project work. A task flagged `is_milestone` is a billing\nmilestone: completing it emits `project.milestone_completed`.","properties":{"assignee_id":{"format":"uuid","type":["string","null"]},"completed_at":{"format":"date-time","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"description":{"type":["string","null"]},"due_at":{"format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_milestone":{"type":"boolean"},"milestone_amount_minor":{"format":"int64","type":["integer","null"]},"milestone_currency":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"phase_id":{"format":"uuid","type":["string","null"]},"project_id":{"format":"uuid","type":"string"},"sort_order":{"format":"int32","type":"integer"},"status":{"$ref":"#/components/schemas/TaskStatus"},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","project_id","title","status","sort_order","is_milestone","created_at","updated_at"],"type":"object"},"ProjectTemplate":{"description":"A reusable project blueprint. `spec` follows `core-projects::template`\n(`TemplateSpec`, additive-only evolution); instantiation is a plain\ntransactional insert plan — templates are data, not code (policy §5).","properties":{"created_at":{"format":"date-time","type":"string"},"description":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"spec":{"type":"object"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","name","spec","created_at","updated_at"],"type":"object"},"ProjectView":{"description":"A saved task-list view: filter/group/sort spec over one project's tasks\n(`project_id: None` = org-wide). The `spec` is a versioned, additive-only\nJSON document validated at the route layer; unknown fields are tolerated\n(policy §2 posture for declared customizations-as-data).","properties":{"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"is_default":{"type":"boolean"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"project_id":{"format":"uuid","type":["string","null"]},"spec":{"type":"object"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","name","spec","is_default","created_at","updated_at"],"type":"object"},"ProposalClassification":{"description":"Proposal classification: who will see the change if it lands.","enum":["internal","client_visible","external_commit"],"type":"string"},"ProposalListQuery":{"description":"GET /api/agent-proposals query.","properties":{"cursor":{"description":"Opaque keyset cursor from a previous page's `next_cursor`.","type":["string","null"]},"limit":{"description":"Page size, clamped to [1, 200].","format":"int64","type":["integer","null"]},"status":{"description":"Filter by lifecycle state.","type":["string","null"]}},"type":"object"},"ProposalStatus":{"description":"Proposal lifecycle state.","enum":["proposed","approved","declined","withdrawn","applied"],"type":"string"},"ProposeUpdateRequest":{"properties":{"granted_capabilities":{"description":"The grant set that WOULD apply post-consent (defaults to the\ncurrent grants; a widening validates against the new manifest).","items":{"type":"string"},"type":["array","null"]},"version":{"description":"Pin this catalog version as the update candidate.","type":"string"}},"required":["version"],"type":"object"},"ProvenanceEvent":{"description":"One provenance entry: a single field change within one applied write.","properties":{"actor_id":{"description":"The acting user (or machine actor) id, when attributed.","format":"uuid","type":["string","null"]},"actor_kind":{"description":"Actor kind from the write-trail vocabulary (human, agent, ...).","type":"string"},"after":{"description":"Applied value; null when the field was removed in this write.","type":["object","null"]},"before":{"description":"Prior value; null when the field is new.","type":["object","null"]},"entity":{"description":"Entity family, e.g. \"ticket\".","type":"string"},"entity_id":{"format":"uuid","type":"string"},"field":{"description":"The changed field.","type":"string"},"idempotency_key":{"description":"The idempotency key that claimed the write, when the caller\nsupplied one.","type":["string","null"]},"occurred_at":{"type":"string"},"op":{"description":"Machine-readable operation (\"create\", \"update\", \"status_change\", ...).","type":"string"},"ruleset_hash":{"description":"Rule-set hash that governed the write, when present.","type":["string","null"]},"seq":{"description":"The write's per-entity sequence (mirrors `mutation.version`).","format":"int64","type":"integer"},"session_id":{"description":"Agent session, when present (IF8-approved writes).","format":"uuid","type":["string","null"]},"skill":{"description":"Skill that authored the write, when present.","type":["string","null"]},"undo":{"description":"True for undo reverts (IF7) riding the normal write path.","type":"boolean"},"write_id":{"format":"uuid","type":"string"}},"required":["write_id","entity","entity_id","seq","op","actor_kind","actor_id","field","before","after","undo","occurred_at"],"type":"object"},"ProvenancePage":{"properties":{"events":{"items":{"$ref":"#/components/schemas/ProvenanceEvent"},"type":"array"},"next_cursor":{"description":"Opaque cursor for the next page; absent when exhausted.","type":["string","null"]}},"required":["events"],"type":"object"},"ProvenanceView":{"properties":{"action":{"type":"string"},"artifact_digest":{"type":"string"},"fetched_ref":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"install_id":{"format":"uuid","type":"string"},"recorded_at":{"format":"date-time","type":"string"},"source_kind":{"type":"string"},"source_url":{"type":"string"},"uploader":{"format":"uuid","type":["string","null"]}},"required":["id","install_id","source_kind","source_url","artifact_digest","action","recorded_at"],"type":"object"},"ProviderMutationResponse":{"allOf":[{"$ref":"#/components/schemas/KbEmbedProvider"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2) for provider rows."},"PublicLinkView":{"description":"What the public page needs to render the booking UI: the template's\nbookable shape plus a slot preview over the booking window. The\nhorizon follows `booking_window_days`; intervals are computed in\nserver-side chunks and capped at [`MAX_PREVIEW_SLOTS`].","properties":{"booking_window_days":{"format":"int32","type":"integer"},"buffer_minutes":{"format":"int32","type":"integer"},"duration_minutes":{"format":"int32","type":"integer"},"expires_at":{"description":"Absolute expiry, when the tech set one.","format":"date-time","type":["string","null"]},"is_expired":{"type":"boolean"},"label":{"type":["string","null"]},"link_id":{"format":"uuid","type":"string"},"meeting_type_description":{"type":["string","null"]},"meeting_type_name":{"type":"string"},"parameters":{},"slots":{"description":"Free intervals (unioned across the assignee scope) within the\nbookable horizon; clients slice them into bookable choices.","items":{"$ref":"#/components/schemas/SlotInterval"},"type":"array"}},"required":["link_id","meeting_type_name","duration_minutes","buffer_minutes","booking_window_days","parameters","is_expired","slots"],"type":"object"},"PublicProvider":{"properties":{"display_name":{"type":"string"},"name":{"description":"URL path segment for /api/auth/oidc/{name}/login.","type":"string"}},"required":["name","display_name"],"type":"object"},"PublishCatalogEntryRequest":{"properties":{"description":{"type":"string"},"manifest":{"$ref":"#/components/schemas/PluginManifestBody"},"name":{"type":"string"},"publisher":{"type":"string"},"slug":{"type":"string"},"version":{"type":"string"}},"required":["slug","name","publisher","version","manifest"],"type":"object"},"PublishSkillRequest":{"properties":{"manifest":{"description":"`{capabilities: [...], prompts: [...], rules: [...]}`.","type":"object"},"slug":{"description":"Slug; version is derived server-side (max + 1) — append-only\nhistory, never an in-place edit.","type":"string"}},"required":["slug","manifest"],"type":"object"},"PublisherVerificationView":{"properties":{"display_name":{"type":"string"},"id":{"format":"uuid","type":"string"},"note":{"type":"string"},"publisher":{"type":"string"},"revoked_at":{"format":"date-time","type":["string","null"]},"status":{"type":"string"},"verified_at":{"format":"date-time","type":["string","null"]}},"required":["id","publisher","display_name","status","note"],"type":"object"},"PullItem":{"description":"One exported row: internal id, stable source key when the row was ever\npushed under one, and the canonical projection (the SAME field catalog\nthe push side diffs against).","properties":{"external_key":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"projection":{"type":"object"}},"required":["id","projection"],"type":"object"},"PullPage":{"description":"GET /api/migration/pull/{family} envelope.","properties":{"family":{"$ref":"#/components/schemas/EntityKind"},"items":{"items":{"$ref":"#/components/schemas/PullItem"},"type":"array"},"next_cursor":{"description":"Pass back as `cursor`; absent when exhausted.","type":["string","null"]}},"required":["family","items"],"type":"object"},"PullQuery":{"description":"GET /api/migration/pull/{family} query.","properties":{"cursor":{"description":"Opaque keyset cursor from a previous page's `next_cursor` (the last\nrow id). Absent = start of the family.","type":["string","null"]},"limit":{"description":"Page size, clamped to [1, 200] (default 100).","format":"int64","type":["integer","null"]}},"type":"object"},"PurchaseBill":{"description":"The vendor bill: recorded as a draft, posted (FX-locked) into project\ncost rollups.","properties":{"amount_minor":{"format":"int64","type":"integer"},"base_amount_minor":{"format":"int64","type":["integer","null"]},"bill_number":{"type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"currency":{"type":"string"},"fx_rate_date":{"format":"date","type":["string","null"]},"fx_rate_used":{"type":["string","null"]},"fx_source":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"posted_at":{"format":"date-time","type":["string","null"]},"purchase_order_id":{"format":"uuid","type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","purchase_order_id","amount_minor","currency","created_at","updated_at"],"type":"object"},"PurchaseOrder":{"description":"One purchase order header.","properties":{"cancel_reason":{"type":["string","null"]},"cancelled_at":{"format":"date-time","type":["string","null"]},"close_kind":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CloseKind"}]},"closed_at":{"format":"date-time","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"currency":{"description":"ISO 4217; every line cost is in this currency.","type":"string"},"expected_at":{"format":"date","type":["string","null"]},"id":{"format":"uuid","type":"string"},"notes":{"type":["string","null"]},"number":{"type":"string"},"ordered_at":{"format":"date-time","type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"project_id":{"description":"Cost-object hook for project cost rollups (NULL = unassigned).","format":"uuid","type":["string","null"]},"status":{"$ref":"#/components/schemas/PurchaseOrderStatus"},"updated_at":{"format":"date-time","type":"string"},"vendor_id":{"format":"uuid","type":"string"},"vendor_reference":{"description":"The vendor's own order reference.","type":["string","null"]}},"required":["id","org_id","vendor_id","number","status","currency","created_at","updated_at"],"type":"object"},"PurchaseOrderDeeplinks":{"properties":{"links":{"items":{"$ref":"#/components/schemas/DeeplinkEntry"},"type":"array"},"number":{"type":"string"},"order_id":{"format":"uuid","type":"string"},"vendor":{"$ref":"#/components/schemas/DeeplinkVendor"}},"required":["order_id","number","vendor","links"],"type":"object"},"PurchaseOrderDetail":{"properties":{"bill":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PurchaseBill"}]},"lines":{"items":{"$ref":"#/components/schemas/PurchaseOrderLineView"},"type":"array"},"order":{"$ref":"#/components/schemas/PurchaseOrder"},"receipts":{"items":{"$ref":"#/components/schemas/PurchaseReceiptView"},"type":"array"},"totals":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PurchaseOrderRollup"}]}},"required":["order","lines","receipts"],"type":"object"},"PurchaseOrderLine":{"description":"One PO line: quantity at milli precision, cost in minor units.","properties":{"catalog_item_id":{"format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"description":{"type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"position":{"format":"int32","type":"integer"},"purchase_order_id":{"format":"uuid","type":"string"},"qty_ordered_milli":{"format":"int64","type":"integer"},"qty_received_milli":{"format":"int64","type":"integer"},"receipt_state":{"$ref":"#/components/schemas/ReceiptState"},"stock_item_id":{"format":"uuid","type":["string","null"]},"unit_cost_minor":{"format":"int64","type":"integer"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","purchase_order_id","description","qty_ordered_milli","qty_received_milli","unit_cost_minor","receipt_state","position","created_at","updated_at"],"type":"object"},"PurchaseOrderLineView":{"description":"Ledger-visible PO line (cost fields are `financial:read`-gated).","properties":{"catalog_item_id":{"format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"description":{"type":"string"},"id":{"format":"uuid","type":"string"},"ordered_amount_minor":{"format":"int64","type":["integer","null"]},"position":{"format":"int32","type":"integer"},"purchase_order_id":{"format":"uuid","type":"string"},"qty_ordered_milli":{"format":"int64","type":"integer"},"qty_received_milli":{"format":"int64","type":"integer"},"receipt_state":{"$ref":"#/components/schemas/ReceiptState"},"received_amount_minor":{"format":"int64","type":["integer","null"]},"stock_item_id":{"format":"uuid","type":["string","null"]},"unit_cost_minor":{"format":"int64","type":["integer","null"]},"updated_at":{"format":"date-time","type":"string"}},"required":["id","purchase_order_id","description","qty_ordered_milli","qty_received_milli","receipt_state","position","created_at","updated_at"],"type":"object"},"PurchaseOrderRollup":{"description":"The rolled-up money on a PO: ordered and received amounts in the PO\ncurrency, integer minor units.","properties":{"ordered_minor":{"format":"int64","type":"integer"},"received_minor":{"format":"int64","type":"integer"}},"required":["ordered_minor","received_minor"],"type":"object"},"PurchaseOrderStatus":{"enum":["draft","ordered","partially_received","received","closed","cancelled"],"type":"string"},"PurchaseOrderSummary":{"allOf":[{"$ref":"#/components/schemas/PurchaseOrder"},{"properties":{"totals":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PurchaseOrderRollup"}]}},"type":"object"}],"description":"One row of the order list: the order itself plus its own totals.\n\nThe order fields are flattened, so this is the order's old wire shape plus\na `totals` key — a client that ignores the new key keeps working, and one\nthat reads it stops needing a request per order to know what an order is\nworth. `totals` is `null` for a caller without `ViewFinancials`, which is\nthe same gate the detail endpoint's totals obey: the list never leaks money\nthe detail page would hide."},"PurchaseReceipt":{"description":"One receipt document: goods that landed at a location against a PO.","properties":{"carrier":{"type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"location_id":{"format":"uuid","type":"string"},"note":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"purchase_order_id":{"format":"uuid","type":"string"},"received_at":{"format":"date-time","type":"string"},"tracking_ref":{"type":["string","null"]}},"required":["id","org_id","purchase_order_id","location_id","received_at","created_at"],"type":"object"},"PurchaseReceiptLine":{"description":"One receipt line: what moved, and the ledger row it posted.","properties":{"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"movement_id":{"description":"The lane-1 append-only ledger row this line posted.","format":"uuid","type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"po_line_id":{"format":"uuid","type":"string"},"qty_milli":{"format":"int64","type":"integer"},"receipt_id":{"format":"uuid","type":"string"}},"required":["id","org_id","receipt_id","po_line_id","qty_milli","created_at"],"type":"object"},"PurchaseReceiptView":{"allOf":[{"$ref":"#/components/schemas/PurchaseReceipt"},{"properties":{"lines":{"items":{"$ref":"#/components/schemas/PurchaseReceiptLine"},"type":"array"}},"required":["lines"],"type":"object"}],"description":"A receipt document with its traceability lines (each stamps the\nlane-1 ledger `movement_id` it posted)."},"PushCheckpoint":{"description":"The checkpoint cursor of a push session: what the server has committed\nand what it expects next. `last_committed_chunk` is `-1` on a fresh\nsession.","properties":{"last_committed_chunk":{"description":"Highest committed chunk index; -1 before the first chunk.","format":"int64","type":"integer"},"next_chunk":{"description":"The chunk index the next POST must carry.","format":"int64","type":"integer"}},"required":["last_committed_chunk","next_chunk"],"type":"object"},"PushChunkReport":{"description":"The per-row report of one chunk — the D2 contract shape, kept\nSerialize+Deserialize so a committed chunk's stored copy replays\nfaithfully on an exact re-send.","properties":{"batch_id":{"format":"uuid","type":"string"},"conflict_policy":{"$ref":"#/components/schemas/ConflictPolicy"},"dry_run":{"type":"boolean"},"entity_kind":{"$ref":"#/components/schemas/EntityKind"},"rows":{"items":{"$ref":"#/components/schemas/RowReport"},"type":"array"},"summary":{"$ref":"#/components/schemas/PushChunkSummary"}},"required":["batch_id","dry_run","entity_kind","conflict_policy","summary","rows"],"type":"object"},"PushChunkRequest":{"allOf":[{"$ref":"#/components/schemas/RowsPayload"},{"properties":{"chunk_index":{"description":"Position of this chunk in the run. Must be the checkpoint's\n`next_chunk`; a lower index replays (same payload) or 409s\n(`migration_chunk_reused`, different payload); a higher index 409s\n(`migration_cursor_mismatch`) — chunks are sequential.","format":"int64","type":"integer"}},"required":["chunk_index"],"type":"object"}],"description":"POST /api/migration/push/sessions/{id}/chunks body."},"PushChunkResponse":{"properties":{"checkpoint":{"$ref":"#/components/schemas/PushCheckpoint"},"chunk_index":{"format":"int64","type":"integer"},"replay":{"description":"`true` when an exact re-send replayed the stored report without\ntouching data (retry after a lost response is free).","type":"boolean"},"report":{"$ref":"#/components/schemas/PushChunkReport"},"rows_created":{"format":"int64","type":"integer"},"rows_failed":{"format":"int64","type":"integer"},"rows_skipped":{"format":"int64","type":"integer"},"rows_total":{"description":"Cumulative batch counters after this chunk.","format":"int64","type":"integer"},"rows_updated":{"format":"int64","type":"integer"},"session_id":{"format":"uuid","type":"string"}},"required":["session_id","chunk_index","replay","report","checkpoint","rows_total","rows_created","rows_updated","rows_skipped","rows_failed"],"type":"object"},"PushChunkSummary":{"description":"u32-friendly mirror of the per-chunk summary so the generated SDK gets\nplain integers without overflow caveats (same shape as D2's\n`ChunkSummaryWire`, but also deserialize for replay).","properties":{"failed":{"format":"int32","minimum":0,"type":"integer"},"ok":{"format":"int32","minimum":0,"type":"integer"},"total":{"format":"int32","minimum":0,"type":"integer"},"would_skip":{"format":"int32","minimum":0,"type":"integer"},"would_update":{"format":"int32","minimum":0,"type":"integer"}},"required":["total","ok","would_update","would_skip","failed"],"type":"object"},"PushSessionCreated":{"description":"201 body: the session id, its D2 batch, and the starting cursor.","properties":{"batch_id":{"format":"uuid","type":"string"},"checkpoint":{"$ref":"#/components/schemas/PushCheckpoint"},"conflict_policy":{"$ref":"#/components/schemas/ConflictPolicy"},"entity_kind":{"$ref":"#/components/schemas/EntityKind"},"label":{"type":"string"},"session_id":{"format":"uuid","type":"string"}},"required":["session_id","batch_id","entity_kind","conflict_policy","label","checkpoint"],"type":"object"},"PushSessionFinishedResponse":{"description":"POST /api/migration/push/sessions/{id}/finish — closes the session and\nits D2 batch; returns the cumulative run totals.","properties":{"batch_id":{"format":"uuid","type":"string"},"checkpoint":{"$ref":"#/components/schemas/PushCheckpoint"},"chunks_committed":{"format":"int64","type":"integer"},"conflict_policy":{"$ref":"#/components/schemas/ConflictPolicy"},"entity_kind":{"$ref":"#/components/schemas/EntityKind"},"rows_created":{"format":"int32","type":"integer"},"rows_failed":{"format":"int32","type":"integer"},"rows_skipped":{"format":"int32","type":"integer"},"rows_total":{"format":"int32","type":"integer"},"rows_updated":{"format":"int32","type":"integer"},"session_id":{"format":"uuid","type":"string"}},"required":["session_id","batch_id","entity_kind","conflict_policy","chunks_committed","rows_total","rows_created","rows_updated","rows_skipped","rows_failed","checkpoint"],"type":"object"},"PushSessionView":{"description":"GET /api/migration/push/sessions/{id} — the resume view a tool asks\nafter a crash: where the checkpoint stands and which chunks committed.","properties":{"batch_id":{"format":"uuid","type":"string"},"checkpoint":{"$ref":"#/components/schemas/PushCheckpoint"},"committed_chunks":{"items":{"$ref":"#/components/schemas/CommittedChunkRef"},"type":"array"},"conflict_policy":{"$ref":"#/components/schemas/ConflictPolicy"},"created_at":{"type":"string"},"entity_kind":{"$ref":"#/components/schemas/EntityKind"},"finished_at":{"type":["string","null"]},"label":{"type":"string"},"session_id":{"format":"uuid","type":"string"},"source_note":{"type":["string","null"]}},"required":["session_id","batch_id","entity_kind","conflict_policy","label","created_at","checkpoint","committed_chunks"],"type":"object"},"PushStatusMutationResponse":{"allOf":[{"$ref":"#/components/schemas/PushStatusResponse"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"201 body: the pushed row plus the read-your-write metadata (IF2)."},"PushStatusRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"note":{"type":["string","null"]},"occurred_at":{"description":"When the status was observed; defaults to now (server clock).","format":"date-time","type":["string","null"]},"status":{"description":"Field-service status: en_route | arrived | in_progress | completed\n| blocked | delayed.","type":"string"}},"required":["status"],"type":"object"}],"description":"POST /api/dispatch-connector/appointments/{id}/status body."},"PushStatusResponse":{"description":"The pushed row (entity fields; the mutation envelope flattens it).","properties":{"appointment_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"note":{"type":["string","null"]},"occurred_at":{"format":"date-time","type":"string"},"source":{"type":"string"},"status":{"type":"string"}},"required":["id","appointment_id","status","source","occurred_at"],"type":"object"},"PutAiCapRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"hard_token_limit":{"format":"int64","type":["integer","null"]},"period":{"type":"string"},"scope":{"description":"`org` | `user`.","type":"string"},"soft_token_limit":{"format":"int64","type":["integer","null"]},"user_id":{"format":"uuid","type":["string","null"]}},"required":["scope"],"type":"object"}]},"PutAiCatalogRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"capabilities":{"description":"OMITTED (vs explicit empty) keeps the existing row's tags — a\nre-seal without a capabilities payload must not silently wipe the\ncapability enforcement data (Wave 18 lane 2b, item 4).","items":{"type":"string"},"type":["array","null"]},"cost_weight":{"format":"int32","type":"integer"},"enabled":{"type":"boolean"},"label":{"type":"string"},"model":{"type":"string"},"provider":{"type":"string"}},"required":["provider","model"],"type":"object"}]},"PutAiCredentialRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"credential_meta":{},"kind":{"description":"`api_key` | `oauth_token` | `subscription_account`.","type":"string"},"label":{"type":"string"},"provider":{"type":"string"},"secret":{"description":"Plaintext secret — sealed at rest, never surfaced back.","type":"string"}},"required":["provider","kind","secret"],"type":"object"}]},"PutAiGrantRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"grantee_kind":{"description":"`role` | `user`.","type":"string"},"role_id":{"format":"uuid","type":["string","null"]},"surface_key":{"type":"string"},"user_id":{"format":"uuid","type":["string","null"]}},"required":["surface_key","grantee_kind"],"type":"object"}]},"PutAiPickRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"model":{"type":"string"},"provider":{"type":"string"}},"required":["provider","model"],"type":"object"}]},"PutAiPluginSurfaceRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"description":{"type":"string"},"key":{"description":"The org-unique plugin surface key (lowercase snake). Re-PUT with\nthe same key UPDATES the registration and re-enables it.","type":"string"},"label":{"type":"string"}},"required":["key"],"type":"object"}]},"PutBoardAddressRequest":{"properties":{"address":{"description":"`None` clears the address.","type":["string","null"]}},"type":"object"},"PutBoardIdentityRequest":{"properties":{"from_address":{"description":"All fields optional and partial: only present keys are written; an\nexplicit `null` (or blank string) clears that field back to the org\nidentity.","type":["string","null"]},"from_name":{"type":["string","null"]},"reply_to":{"type":["string","null"]},"signature":{"type":["string","null"]}},"type":"object"},"PutByokSecretRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive."},{"properties":{"api_key":{"description":"Plaintext API key / token to be sealed at rest.","type":"string"},"provider":{"description":"Provider name: an AI provider (\"openai\", \"anthropic\", \"ollama\",\n\"custom\") or an export-sync connector (\"qbo\", \"xero\").","type":"string"}},"required":["provider","api_key"],"type":"object"}],"description":"Request to store or update a sealed BYOK API key."},"PutCapabilityRequest":{"properties":{"capability":{"description":"`<namespace>:read` | `<namespace>:write`.","type":"string"},"mode":{"$ref":"#/components/schemas/CapabilityMode"}},"required":["capability","mode"],"type":"object"},"PutChecklistTemplateRequest":{"properties":{"items":{"description":"The complete required-step list; replaces any previous items.","items":{"type":"string"},"type":"array"},"name":{"type":"string"},"require_checklist_close":{"description":"Enforcement toggle (defaults-first: OFF until explicitly enabled).","type":"boolean"}},"required":["name","items"],"type":"object"},"PutDecisionConfigRequest":{"properties":{"config":{"description":"Full `SurfaceConfig` JSON (state_template + questions +\nthresholds). Threshold editors ride this."},"enabled":{"type":["boolean","null"]},"instant":{"$ref":"#/components/schemas/MutationParams"},"mode":{"description":"`shadow` (record only) | `live`. Shadow is the landing posture.","type":["string","null"]},"model_id":{"type":["string","null"]},"model_provider":{"description":"Pinned decision model (e.g. `typesafe` / `jev-1.13.0`, never a\nfloating tag — answer provenance records exactly this id).","type":["string","null"]},"reset_to_starter":{"description":"Discard the fork; re-reference the LATEST curated starter set.","type":["boolean","null"]}},"type":"object"},"PutDecisionFeaturesRequest":{"properties":{"enabled":{"type":"boolean"},"instant":{"$ref":"#/components/schemas/MutationParams"}},"required":["enabled"],"type":"object"},"PutExposureRequest":{"properties":{"board_id":{"format":"uuid","type":"string"},"exposed":{"type":"boolean"},"visible_status_ids":{"description":"Empty = all statuses visible (v1 default). Non-empty narrows to\nstatuses OF THAT BOARD — unknown ids are rejected, not silently kept.","items":{"format":"uuid","type":"string"},"type":"array"}},"required":["board_id","exposed","visible_status_ids"],"type":"object"},"PutInboundSettingsRequest":{"properties":{"catchall_address":{"type":"string"},"catchall_board_id":{"format":"uuid","type":"string"},"enabled":{"description":"Enables/disables catch-all routing; absent keeps the stored value.","type":["boolean","null"]}},"required":["catchall_address","catchall_board_id"],"type":"object"},"PutInstallSettingsRequest":{"properties":{"config":{}},"required":["config"],"type":"object"},"PutNoteDraftRequest":{"properties":{"body":{"type":"string"},"instant":{"$ref":"#/components/schemas/MutationParams","description":"Autosave claim key (IF1): same key on a debounced retry is a no-op\noverwrite (last-writer-wins per §5.2 cadence)."}},"required":["body"],"type":"object"},"PutOutboundSettingsRequest":{"properties":{"brand_name":{"type":["string","null"]},"digest_policy":{"type":["string","null"]},"from_address":{"type":["string","null"]},"from_name":{"type":["string","null"]},"logo_url":{"type":["string","null"]},"org_greeting":{"description":"The org's own greeting (Wave 42).","type":["string","null"]},"postmark_base_url":{"type":["string","null"]},"reply_to":{"type":["string","null"]},"ses_endpoint":{"description":"Optional SES endpoint override, for a VPC interface endpoint.","type":["string","null"]},"ses_region":{"description":"AWS region whose SES endpoint this org sends through (transport=ses).","type":["string","null"]},"signature":{"type":["string","null"]},"signature_policy":{"description":"`allowed` | `org_locked` (Wave 42). Absent leaves the policy alone,\nso an older client that does not know the key cannot reset it.","type":["string","null"]},"smtp_host":{"type":["string","null"]},"smtp_port":{"format":"int64","type":["integer","null"]},"smtp_username":{"type":["string","null"]},"transport":{"type":["string","null"]}},"type":"object"},"PutOverrideRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"state":{"description":"\"set\" (value replaces the org default) or \"disabled\" (explicit deny\nthat wins over an org-enabled default).","type":"string"},"value":{"description":"The typed payload (uuid string for reference kinds, boolean for\ntoggles); MUST be absent for \"disabled\"."}},"required":["state"],"type":"object"}],"description":"PUT body: `{ \"state\": \"set\", \"value\": <typed payload> }` or\n`{ \"state\": \"disabled\" }`, plus the instant-first parameters."},"PutPodVisibilityRequest":{"properties":{"config":{"description":"The full matrix; the store is whole-document (admins edit the\nmatrix in one pass). Must satisfy [`validate_matrix`]."}},"required":["config"],"type":"object"},"PutRatingRequest":{"properties":{"artifact_digest":{"description":"sha256 hex of the artifact release the rating pins.","type":"string"},"comment":{"type":["string","null"]},"rating":{"format":"int32","type":"integer"},"source_kind":{"description":"`marketplace_registry` | `github_repo` | `local_zip` |\n`skill_community` | `manual_upload`.","type":"string"},"source_url":{"type":"string"}},"required":["source_kind","source_url","artifact_digest","rating"],"type":"object"},"PutTransportCredentialsRequest":{"properties":{"postmark_server_token":{"description":"Postmark server token (required when transport = postmark).","type":["string","null"]},"ses_access_key_id":{"description":"SES IAM access key id (required when transport = ses).","type":["string","null"]},"ses_secret_access_key":{"description":"SES IAM secret access key (required when transport = ses). The\nsecret half of the pair — required alongside the id, never instead\nof it, because a lone key id cannot sign a request.","type":["string","null"]},"smtp_password":{"description":"SMTP password (required when transport = smtp).","type":["string","null"]},"transport":{"description":"`postmark`, `smtp` or `ses` — which credential this request rotates.","type":"string"}},"required":["transport"],"type":"object"},"QuarantineItemView":{"description":"One quarantined object as the operator surface reads it.\n\nThe digest is the identity: `attachment_scans` is keyed by\n`(org_id, sha256)` because the verdict belongs to the BYTES, not to the\nupload that brought them in.","properties":{"content_type":{"type":"string"},"detail":{"description":"The scanner's own words — usually a signature name.","type":["string","null"]},"filename":{"type":"string"},"quarantined_at":{"format":"date-time","type":"string"},"release_reason":{"type":["string","null"]},"released_at":{"format":"date-time","type":["string","null"]},"released_by_user_id":{"format":"uuid","type":["string","null"]},"scanned_at":{"format":"date-time","type":"string"},"scanner":{"description":"Adapter that produced the verdict (`clamd`, `none`).","type":"string"},"sha256":{"type":"string"},"size_bytes":{"format":"int64","type":"integer"},"state":{"$ref":"#/components/schemas/QuarantineState"},"storage_key":{"description":"Where the bytes live now: the quarantine key while held, the canonical\nkey after a release.","type":"string"},"ticket_id":{"description":"The ticket the upload was aimed at, when there was one. The row is\n`ON DELETE SET NULL`, so a deleted ticket leaves the record intact.","format":"uuid","type":["string","null"]},"uploaded_by_portal_user_id":{"format":"uuid","type":["string","null"]},"uploaded_by_user_id":{"format":"uuid","type":["string","null"]}},"required":["sha256","filename","content_type","size_bytes","scanner","state","quarantined_at","scanned_at","storage_key"],"type":"object"},"QuarantinePage":{"description":"One page of the quarantine list, with the org-wide counts beside it.","properties":{"items":{"description":"Newest first. Released rows stay listed: the trail is the point.","items":{"$ref":"#/components/schemas/QuarantineItemView"},"type":"array"},"next_offset":{"description":"Offset for the next page; absent when this page is the last.","format":"int64","type":["integer","null"]},"pending":{"description":"Objects still held, across the whole org.","format":"int64","type":"integer"},"released":{"description":"Objects released anyway, across the whole org.","format":"int64","type":"integer"}},"required":["items","pending","released"],"type":"object"},"QuarantineState":{"description":"Where one object sits in the quarantine: still held, or released anyway.","enum":["pending","released"],"type":"string"},"QueueMutationResponse":{"description":"Queue mutation envelope (set + clear share the shape; clears carry\n`cleared: true` and null note).","properties":{"cleared":{"type":"boolean"},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"note":{"type":["string","null"]},"rank":{"format":"int32","type":"integer"},"ticket_id":{"format":"uuid","type":"string"}},"required":["ticket_id","rank","cleared","mutation"],"type":"object"},"QueueQuery":{"properties":{"decided_by":{"description":"Per-approver decision history: entries this approver decided.","format":"uuid","type":["string","null"]},"limit":{"format":"int64","type":"integer"},"status":{"description":"Filter by approval state; defaults to `submitted` (the queue).","type":["string","null"]},"user_id":{"description":"Narrow to one submitter.","format":"uuid","type":["string","null"]}},"type":"object"},"QueuedTicket":{"allOf":[{"$ref":"#/components/schemas/MyWorkItem"},{"properties":{"override_note":{"type":["string","null"]},"rank_override":{"description":"The dispatcher-set rank pinning this ticket's position (overrides\nsort ahead of the computed order); absent = computed order only.","format":"int32","type":["integer","null"]}},"type":"object"}],"description":"One queued ticket: the day-view's queue row shape plus the explicit\noverride when one exists."},"QuotaMetricView":{"description":"A single metric's reading: the measured usage, the configured limit\n(when one exists), and the percentage of the limit in use.","properties":{"limit":{"description":"The configured limit, or `None` when the org configured none.","format":"int64","type":["integer","null"]},"metric":{"description":"Stable metric token (SDK-visible).","type":"string"},"percent":{"description":"`usage` as a percentage of `limit`, to one decimal. `None` whenever\nthere is no limit to measure against (or the limit is zero), never\nan error and never clamped: 300.0 is a valid, reportable reading.","format":"double","type":["number","null"]},"setting_key":{"description":"The org-settings key the limit is read from.","type":"string"},"usage":{"description":"The measured value, or `None` when the org's usage row has never\nbeen refreshed — an unmeasured org is not a zero org.","format":"int64","type":["integer","null"]}},"required":["metric","setting_key"],"type":"object"},"Quote":{"description":"The quote document. `subtotal/tax/total` are persisted roll-ups of the\nlines, recomputed on every draft edit.","properties":{"client_id":{"format":"uuid","type":"string"},"converted_at":{"format":"date-time","type":["string","null"]},"converted_contract_id":{"description":"The contract the convert-to-invoice seam materialized (NULL until\nconverted).","format":"uuid","type":["string","null"]},"converted_invoice_id":{"description":"The DRAFT invoice the seam created (NULL until converted).","format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"currency":{"type":"string"},"decided_at":{"description":"When the quote reached a terminal accept/decline/expire state.","format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"issued_on":{"format":"date","type":"string"},"notes":{"type":["string","null"]},"number":{"description":"Human quote number, unique per org (e.g. \"Q-000012\").","type":"string"},"org_id":{"format":"uuid","type":"string"},"sent_at":{"format":"date-time","type":["string","null"]},"status":{"$ref":"#/components/schemas/QuoteStatus"},"subtotal_minor":{"format":"int64","type":"integer"},"tax_minor":{"format":"int64","type":"integer"},"tax_rate_bps":{"format":"int32","type":"integer"},"terms":{"type":["string","null"]},"total_minor":{"format":"int64","type":"integer"},"updated_at":{"format":"date-time","type":"string"},"valid_until":{"description":"Last day the quote is valid; conversion after this is refused.","format":"date","type":"string"}},"required":["id","org_id","number","client_id","status","currency","issued_on","valid_until","subtotal_minor","tax_rate_bps","tax_minor","total_minor","created_at","updated_at"],"type":"object"},"QuoteDetail":{"allOf":[{"$ref":"#/components/schemas/Quote"},{"properties":{"lines":{"items":{"$ref":"#/components/schemas/QuoteLine"},"type":"array"}},"required":["lines"],"type":"object"}],"description":"The quote aggregate: document + lines."},"QuoteLifecycleRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"}],"description":"A lifecycle leg body (send/accept/decline/expire): envelope params\nonly; the target status is the URL."},"QuoteLine":{"description":"One quote line. `amount_minor` is the half-even product of\n`quantity_milli × unit_amount_minor / 1000`; the API recomputes it.","properties":{"amount_minor":{"format":"int64","type":"integer"},"catalog_item_id":{"format":"uuid","type":["string","null"]},"description":{"type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"position":{"format":"int32","type":"integer"},"quantity_milli":{"description":"Quantity in thousandths of a unit (1.5 = 1500).","format":"int64","type":"integer"},"quote_id":{"format":"uuid","type":"string"},"taxable":{"type":"boolean"},"unit":{"type":"string"},"unit_amount_minor":{"format":"int64","type":"integer"}},"required":["id","org_id","quote_id","description","unit","quantity_milli","unit_amount_minor","taxable","amount_minor","position"],"type":"object"},"QuoteLineInput":{"description":"One requested line. Catalog-backed lines snapshot the item's\ndescription/unit/price/taxability at write time; free lines name\ntheir own.","properties":{"catalog_item_id":{"format":"uuid","type":["string","null"]},"description":{"description":"Required when no catalog item backs the line.","type":["string","null"]},"quantity_milli":{"description":"Quantity in thousandths of a unit (1.5 = 1500).","format":"int64","type":"integer"},"taxable":{"type":["boolean","null"]},"unit":{"description":"Defaults to the catalog item's unit or `each`.","type":["string","null"]},"unit_amount_minor":{"description":"Per-unit price, minor units; required when no catalog item backs\nthe line.","format":"int64","type":["integer","null"]}},"required":["quantity_milli"],"type":"object"},"QuoteListQuery":{"description":"GET /api/quotes query.","properties":{"client_id":{"format":"uuid","type":["string","null"]},"status":{"description":"Exact status: `draft|sent|accepted|declined|expired`.","type":["string","null"]}},"type":"object"},"QuoteMutationResponse":{"allOf":[{"$ref":"#/components/schemas/Quote"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2) for quote mutations."},"QuoteStatus":{"description":"Lifecycle of a quote. `accepted` / `declined` / `expired` are terminal.","enum":["draft","sent","accepted","declined","expired"],"type":"string"},"RagChunkInput":{"description":"Input chunk in a batch ingest request before provider embedding resolution.","properties":{"chunk_index":{"description":"Zero-based passage or frame/segment index within the source.","format":"int32","type":"integer"},"content_hash":{"description":"Content hash for idempotent re-ingest provenance. Computed\ndeterministically as SHA-256 hex when omitted.","type":["string","null"]},"content_text":{"description":"Passage text (required when `modality == text` and `embedding` is\nomitted; optional caption/transcript snippet for media chunks to\nparticipate in the lexical BM25 leg).","type":["string","null"]},"embedding":{"description":"Precomputed 768-dim vector. Required for `image | video | audio`\nmodalities; optional for `text` chunks when the org has an active\nembedding provider.","items":{"format":"float","type":"number"},"type":["array","null"]},"metadata":{"description":"Attribution metadata (title, uri/locator, span/page/timecode,\nmime_type, etc.) returned on every query hit. Defaults to `{}`.","type":"object"},"modality":{"$ref":"#/components/schemas/RagModality","description":"Modality of this chunk (`text | image | video | audio`)."},"model":{"description":"Embedding model identifier. Required when `embedding` is supplied;\ndefaults to the org's configured embedding model when Core embeds a\n`text` chunk.","type":["string","null"]},"source_id":{"description":"Stable identifier of the source within `source_kind` (UUID or external\nlocator).","type":"string"},"source_kind":{"description":"Source entity family (e.g. `\"ticket\"`, `\"kb_article\"`, `\"attachment\"`,\n`\"document\"`, or a connector-defined kind).","type":"string"}},"required":["source_kind","source_id","chunk_index","modality"],"type":"object"},"RagChunkKey":{"description":"Composite chunk identity within an org + namespace:\n`(source_kind, source_id, chunk_index, modality)`.","properties":{"chunk_index":{"format":"int32","type":"integer"},"modality":{"$ref":"#/components/schemas/RagModality"},"source_id":{"type":"string"},"source_kind":{"type":"string"}},"required":["source_kind","source_id","chunk_index","modality"],"type":"object"},"RagDeleteSourceRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"namespace":{"type":"string"},"source_id":{"type":"string"},"source_kind":{"type":"string"}},"required":["namespace","source_kind","source_id"],"type":"object"}],"description":"Request body for `POST /api/v1/rag/delete`."},"RagDeleteSourceResponse":{"description":"Response from deleting a source's chunks in a namespace.","properties":{"mutation":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/MutationMeta"}]},"receipt":{"$ref":"#/components/schemas/RagDeletionReceipt"}},"required":["receipt"],"type":"object"},"RagDeletionReceipt":{"description":"Provable deletion receipt recorded when a source's chunks are removed from\n`rag_embeddings` (ADR 0020 §6).","properties":{"content_hashes":{"items":{"type":"string"},"type":"array"},"deleted_at":{"type":"string"},"deleted_by":{"format":"uuid","type":["string","null"]},"deleted_chunks":{"format":"int32","type":"integer"},"namespace":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"receipt_id":{"format":"uuid","type":"string"},"source_id":{"type":"string"},"source_kind":{"type":"string"}},"required":["receipt_id","org_id","namespace","source_kind","source_id","deleted_chunks","content_hashes","deleted_at"],"type":"object"},"RagDeletionReceiptList":{"description":"Response from `GET /api/v1/rag/deletion-receipts`.","properties":{"receipts":{"items":{"$ref":"#/components/schemas/RagDeletionReceipt"},"type":"array"}},"required":["receipts"],"type":"object"},"RagIngestRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"chunks":{"description":"Chunks to upsert (1..=128 per batch).","items":{"$ref":"#/components/schemas/RagChunkInput"},"type":"array"},"namespace":{"description":"Logical vector namespace within the tenant (e.g. `\"default\"`,\n`\"kb-passages\"`, `\"ticket-media\"`). Queries never cross namespaces.","type":"string"}},"required":["namespace","chunks"],"type":"object"}],"description":"Batch upsert request for `/api/v1/rag/chunks`."},"RagIngestResponse":{"description":"Response from `POST /api/v1/rag/chunks`.","properties":{"inserted":{"minimum":0,"type":"integer"},"mutation":{"$ref":"#/components/schemas/MutationMeta"},"namespace":{"type":"string"},"total":{"minimum":0,"type":"integer"},"unchanged":{"minimum":0,"type":"integer"},"updated":{"minimum":0,"type":"integer"}},"required":["namespace","total","inserted","updated","unchanged","mutation"],"type":"object"},"RagModality":{"description":"First-class modality of a stored chunk or query filter (ADR 0020 §3).","enum":["text","image","video","audio"],"type":"string"},"RagQueryHit":{"description":"Ranked RAG hit with mandatory source attribution (ADR 0020 §4).","properties":{"chunk_index":{"format":"int32","type":"integer"},"content_hash":{"type":"string"},"content_text":{"type":["string","null"]},"embedded_at":{"type":"string"},"lexical_score":{"description":"Lexical FTS rank (`ts_rank_cd`) when the BM25/FTS leg ran and matched\nthis chunk.","format":"double","type":["number","null"]},"metadata":{"type":"object"},"modality":{"$ref":"#/components/schemas/RagModality"},"model":{"type":"string"},"namespace":{"type":"string"},"score":{"description":"Reciprocal Rank Fusion score combining the active retrieval legs.","format":"double","type":"number"},"source_id":{"type":"string"},"source_kind":{"type":"string"},"vector_distance":{"description":"Cosine distance (`embedding <=> $query_vec`) when the vector leg ran\nand matched this chunk.","format":"double","type":["number","null"]}},"required":["namespace","source_kind","source_id","chunk_index","modality","model","content_hash","score","metadata","embedded_at"],"type":"object"},"RagQueryRequest":{"description":"Hybrid BM25 + exact-KNN query request for `POST /api/v1/rag/query`.","properties":{"limit":{"description":"Maximum hits to return (1..=100, default 20).","format":"int64","type":"integer"},"modalities":{"description":"Optional modality filter (`text | image | video | audio`). When empty\nor omitted, all modalities in the namespace participate (cross-modal\nretrieval).","items":{"$ref":"#/components/schemas/RagModality"},"type":"array"},"namespace":{"description":"Target namespace within the caller's org. Queries never cross\nnamespaces.","type":"string"},"query_space":{"description":"The embedding space a caller-supplied `query_vector` belongs to\n(`model@version/dimensions`, migration 0152). Optional: without it the\nvector leg ranks in the instance's space, which is the right answer when\nCore embedded the query itself. A connector that produced the vector\nwith its own model names that space here; naming a space nothing carries\nis a miss, never a comparison against vectors from another model.","type":["string","null"]},"query_text":{"description":"Optional lexical query string. When supplied without `query_vector`,\nCore also embeds `query_text` via the org's configured embedding\nprovider so both the BM25 and vector legs run and fuse via RRF.","type":["string","null"]},"query_vector":{"description":"Optional precomputed 768-dim query vector. Enables media->text,\nmedia->media, and caller-embedded text->media queries across the shared\n768-dimension space.","items":{"format":"float","type":"number"},"type":["array","null"]},"source_ids":{"description":"Optional `source_id` filter.","items":{"type":"string"},"type":"array"},"source_kinds":{"description":"Optional `source_kind` filter.","items":{"type":"string"},"type":"array"}},"required":["namespace"],"type":"object"},"RagQueryResponse":{"description":"Ranked response from `POST /api/v1/rag/query`.","properties":{"hits":{"items":{"$ref":"#/components/schemas/RagQueryHit"},"type":"array"},"lexical_leg_active":{"description":"Whether the lexical BM25/FTS leg participated in this query.","type":"boolean"},"limit":{"format":"int64","type":"integer"},"namespace":{"type":"string"},"vector_leg_active":{"description":"Whether the 768-dim exact-KNN vector leg participated in this query.","type":"boolean"}},"required":["namespace","limit","lexical_leg_active","vector_leg_active","hits"],"type":"object"},"RangeParams":{"properties":{"from":{"format":"date-time","type":["string","null"]},"to":{"format":"date-time","type":["string","null"]},"user_id":{"format":"uuid","type":["string","null"]}},"type":"object"},"RateListQuery":{"properties":{"base_currency":{"type":["string","null"]},"quote_currency":{"type":["string","null"]}},"type":"object"},"RateMutationResponse":{"allOf":[{"$ref":"#/components/schemas/FxRateEntry"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}]},"RateSpec":{"description":"T&M rate-card row spec (hourly minor units per `work_type`).","properties":{"rate_minor":{"format":"int64","type":"integer"},"work_type":{"type":"string"}},"required":["work_type","rate_minor"],"type":"object"},"RatingListView":{"properties":{"ratings":{"items":{"$ref":"#/components/schemas/PluginRating"},"type":"array"},"summary":{"$ref":"#/components/schemas/RatingSummary","description":"Aggregate for the requested target (or the whole org list when\nunfiltered — the aggregate then spans targets and is advisory)."}},"required":["ratings","summary"],"type":"object"},"RatingMineView":{"properties":{"comment":{"type":"string"},"rating":{"format":"int32","type":"integer"},"updated_at":{"format":"date-time","type":"string"}},"required":["rating","comment","updated_at"],"type":"object"},"RatingSummary":{"properties":{"average":{"format":"double","type":["number","null"]},"count":{"description":"Aggregated over the org: count of ratings and the mean.","format":"int64","type":"integer"},"mine":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RatingMineView","description":"The caller's own rating on this target, when one exists."}]}},"required":["count"],"type":"object"},"ReadGuardrails":{"description":"The guardrails a declared read runs under (ADR 0017 §5).\n\nThree of the four are ceilings a declaration may not exceed; the fourth\nis a promise that may only be declared one way. Validation applies the\nplatform floors here so a manifest cannot write itself a weaker cap.","properties":{"max_calls_per_run":{"description":"Calls this node may make in ONE workflow run. The per-run cap is\nwhat stops a loop over a device list becoming a hammer.","format":"int32","minimum":0,"type":"integer"},"max_output_bytes":{"description":"Hard ceiling on the returned payload's size.","format":"int32","minimum":0,"type":"integer"},"may_return_secrets":{"description":"Must be `false`, and validation refuses anything else. A read node\nmay never return a vault secret, because the run ledger is a place\noperators read.","type":"boolean"},"min_interval_secs":{"description":"Minimum seconds between this ORG's read-node invocations. The\nper-org cap is what stops a schedule becoming a stream.","format":"int32","minimum":0,"type":"integer"}},"required":["max_calls_per_run","min_interval_secs","max_output_bytes","may_return_secrets"],"type":"object"},"ReceiptMutationResponse":{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"order":{"$ref":"#/components/schemas/PurchaseOrderDetail"},"receipt":{"$ref":"#/components/schemas/PurchaseReceiptView"}},"required":["receipt","order","mutation"],"type":"object"},"ReceiptState":{"enum":["pending","partial","complete","over"],"type":"string"},"ReceiveLineInput":{"properties":{"line_id":{"format":"uuid","type":"string"},"qty_milli":{"format":"int64","type":"integer"},"serial_numbers":{"description":"Serialized stock items only: one serial per unit (qty_milli must be\n`count × 1000`). Each serial posts its own ledger row + receipt line.","items":{"type":"string"},"type":["array","null"]}},"required":["line_id","qty_milli"],"type":"object"},"ReceiveOrderRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"allow_over_receipt":{"description":"Explicit acknowledgement for an over-receipt; absent/false refuses\nwith a typed 409.","type":"boolean"},"carrier":{"type":["string","null"]},"lines":{"items":{"$ref":"#/components/schemas/ReceiveLineInput"},"type":"array"},"location_id":{"description":"Where the goods landed (lane-1 stock location).","format":"uuid","type":"string"},"note":{"type":["string","null"]},"tracking_ref":{"type":["string","null"]}},"required":["location_id","lines"],"type":"object"}]},"ReceiveRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"item_id":{"format":"uuid","type":"string"},"location_id":{"format":"uuid","type":"string"},"note":{"description":"Optional receiving note.","type":["string","null"]},"qty_milli":{"description":"MILLI of the item's unit; serialized items move 1000/serial.","format":"int64","type":"integer"},"serial_numbers":{"description":"Serialized receives carry one entry per physical unit.","items":{"type":"string"},"type":["array","null"]},"ticket_id":{"description":"Optional ticket anchor. A return/restock of parts from a job carries\nthe ticket so the movement lands on that ticket's timeline instead of\nforcing a detour through the stock surfaces (Wave 32 lane 6).","format":"uuid","type":["string","null"]},"unit_cost_minor":{"description":"Unit cost in the item's cost currency (locked at receipt on the\nledger row; ADR 0002 inventory-receipt lock moment).","format":"int64","type":["integer","null"]}},"required":["item_id","location_id","qty_milli"],"type":"object"}],"description":"POST /api/stock/{receive|consume|adjust|transfer|ship-out} bodies.\nAll quantities are `milli` thousandths of the item's unit (parity with\nquote lines); signed legs are normalized per kind below."},"ReconciliationQuery":{"properties":{"from":{"description":"Inclusive window start (default: 14 days before `to`).","format":"date-time","type":["string","null"]},"to":{"description":"Exclusive window end (default: now). Capped at 366 days.","format":"date-time","type":["string","null"]},"user_id":{"format":"uuid","type":["string","null"]}},"type":"object"},"ReconciliationRow":{"properties":{"billable_minor":{"description":"Linked expenses flagged billable (client-facing).","format":"int64","type":"integer"},"display_name":{"type":"string"},"distance":{"description":"Distance normalized to `mileage_rate_unit`.","format":"double","type":"number"},"distance_unit":{"type":"string"},"draft_count":{"format":"int64","type":"integer"},"expense_minor":{"description":"Linked expenses total in the org's base currency.","format":"int64","type":"integer"},"expensed_count":{"format":"int64","type":"integer"},"minutes":{"format":"int64","type":"integer"},"reimbursement_minor":{"description":"Mileage reimbursement owed to the technician at the org rate.","format":"int64","type":"integer"},"submitted_count":{"format":"int64","type":"integer"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","display_name","draft_count","submitted_count","expensed_count","distance","distance_unit","minutes","expense_minor","reimbursement_minor","billable_minor"],"type":"object"},"ReconciliationView":{"properties":{"currency":{"type":"string"},"from":{"format":"date-time","type":"string"},"mileage_rate_minor":{"format":"int64","type":"integer"},"mileage_rate_unit":{"type":"string"},"reimburse_techs":{"type":"boolean"},"rows":{"items":{"$ref":"#/components/schemas/ReconciliationRow"},"type":"array"},"to":{"format":"date-time","type":"string"},"totals":{"$ref":"#/components/schemas/ReconciliationRow"}},"required":["from","to","currency","mileage_rate_minor","mileage_rate_unit","reimburse_techs","rows","totals"],"type":"object"},"RecordBillRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"amount_minor":{"description":"Absent → the received total from the PO lines.","format":"int64","type":["integer","null"]},"bill_number":{"type":["string","null"]},"currency":{"type":["string","null"]}},"type":"object"}]},"RecordPaymentRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key +\nIf-Match `version` on the invoice."},{"properties":{"amount_minor":{"description":"Amount to collect, minor units; must not exceed the outstanding\nbalance. Currency is always the invoice's own.","format":"int64","type":"integer"},"notes":{"type":["string","null"]}},"required":["amount_minor"],"type":"object"}]},"RecordRateRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"as_of_date":{"type":"string"},"base_currency":{"description":"1 base major unit buys `rate` quote major units.","type":"string"},"quote_currency":{"type":"string"},"rate":{"description":"Positive decimal, max 12 fractional digits (\"17.85\").","type":"string"}},"required":["base_currency","quote_currency","as_of_date","rate"],"type":"object"}]},"RecoveryCodesResponse":{"properties":{"recovery_codes":{"description":"Plaintext codes — displayed once, stored only as argon2id hashes.","items":{"type":"string"},"type":"array"}},"required":["recovery_codes"],"type":"object"},"RedriveResponse":{"description":"What a redrive did, as the caller sees it.","properties":{"changed":{"description":"True when this call changed the row, which is also when an audit row was\nwritten. False means the call was a no-op repeat.","type":"boolean"},"id":{"format":"uuid","type":"string"},"outcome":{"description":"`requeued` — the row is back in its ledger, due now, with a fresh\nattempt budget. `not_dead` — someone already moved it; nothing changed.","type":"string"},"source":{"type":"string"}},"required":["source","id","outcome","changed"],"type":"object"},"RefineNoteRequest":{"properties":{"body":{"type":"string"}},"required":["body"],"type":"object"},"RefineNoteResponse":{"description":"Refine outcome. `proposal` is a SUGGESTION — applying it is an explicit\nuser action that posts through the normal comment-create envelope with\n`workflow_meta.kind = \"refined\"`; AI provenance rides the approval\npipeline (docs/10-agent-platform.md), never a direct ledger write.","properties":{"model":{"type":["string","null"]},"proposal":{"type":["string","null"]},"provider":{"type":["string","null"]},"reason":{"type":["string","null"]},"status":{"description":"`\"proposal\"` = a rewrite is offered for the user's Apply/Discard;\n`\"unavailable\"` = the provider seam is not configured — Tier-1\nTidy remains fully functional.","type":"string"}},"required":["status"],"type":"object"},"RefreshRequest":{"properties":{"refresh_token":{"description":"Optional: when omitted, the refresh pair rides the httpOnly\nsession cookie (browser clients); bearer-mode clients keep\nsending both fields exactly as before.","type":["string","null"]},"session_id":{"format":"uuid","type":["string","null"]}},"type":"object"},"RefundCreditNoteRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"amount_minor":{"description":"Refund amount, minor units; must not exceed the credit's\nremaining value.","format":"int64","type":"integer"},"notes":{"type":["string","null"]}},"required":["amount_minor"],"type":"object"}],"description":"POST /api/credit-notes/{id}/refund body."},"RefuseStagedRequest":{"properties":{"review_note":{"type":"string"}},"required":["review_note"],"type":"object"},"RegenerateRecoveryRequest":{"properties":{"code":{"type":"string"},"password":{"type":"string"}},"required":["password","code"],"type":"object"},"RegisterRequest":{"properties":{"display_name":{"type":"string"},"email":{"type":"string"},"org_name":{"description":"Organization name — becomes the tenant for this deployment.","type":"string"},"org_slug":{"description":"URL-safe org slug (unique).","type":"string"},"password":{"type":"string"},"ticket_prefix":{"description":"Ticket number prefix, e.g. \"MAR\" → MAR-2841. Defaults to \"MAR\".","type":["string","null"]},"timezone":{"default":"UTC","type":["string","null"]}},"required":["org_name","org_slug","email","password","display_name"],"type":"object"},"RegistryResponse":{"properties":{"settings":{"items":{"$ref":"#/components/schemas/DeclarationView"},"type":"array"}},"required":["settings"],"type":"object"},"ReleaseQuarantineRequest":{"description":"The reason is the whole point of the override, so it is a required field\nrather than an optional one with a default.","properties":{"reason":{"description":"Why these bytes are being admitted despite the scanner's verdict.\nRecorded verbatim on the row and in the audit trail.","type":"string"}},"required":["reason"],"type":"object"},"ReleaseStatus":{"description":"Release lifecycle: `pending` (released, not yet invoiced), `billed`\n(carried on an invoice line), `cancelled` (terminal — an admin\ncorrection; the same milestone may re-release afterwards).","enum":["pending","billed","cancelled"],"type":"string"},"RenderLabelRequest":{"properties":{"entity_id":{"description":"The entity the template's purpose points at (stock item id, serial\nid, location id, or device asset id).","format":"uuid","type":"string"},"include_zpl":{"type":"boolean"},"template_id":{"format":"uuid","type":"string"}},"required":["template_id","entity_id"],"type":"object"},"RenderReplySnippetRequest":{"properties":{"context_id":{"description":"The ticket the variables resolve from.","format":"uuid","type":"string"}},"required":["context_id"],"type":"object"},"RenderTemplateRequest":{"properties":{"context_id":{"description":"The entity the variables resolve from: a ticket id for\nticket/note/email/workflow kinds, a project id for `project`, a\nquote id for `quote_invoice`. `null` (or omitted) for `kb`.","format":"uuid","type":["string","null"]}},"type":"object"},"RenderedReplySnippet":{"properties":{"body":{"type":"string"}},"required":["body"],"type":"object"},"RenderedTemplate":{"properties":{"body":{"type":"string"},"subject":{"type":["string","null"]}},"required":["body"],"type":"object"},"ReorderCategoriesRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"ordered_ids":{"description":"Category ids in their new display order.","items":{"format":"uuid","type":"string"},"type":"array"}},"required":["ordered_ids"],"type":"object"}]},"ReparentCategoryRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + version."},{"properties":{"parent_id":{"description":"New root category (must be a root of this org).","format":"uuid","type":"string"}},"required":["parent_id"],"type":"object"}]},"ReplaceContactsRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"contacts":{"items":{"$ref":"#/components/schemas/VendorContactInput"},"type":"array"}},"required":["contacts"],"type":"object"}]},"ReplaceLadderRequest":{"properties":{"steps":{"items":{"$ref":"#/components/schemas/LadderStepSpec"},"type":"array"}},"required":["steps"],"type":"object"},"ReplaceTicketSkillsRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"skills":{"description":"Full replacement set (an empty set clears every requirement).","items":{"type":"string"},"type":"array"}},"required":["skills"],"type":"object"}],"description":"PUT /api/dispatch/tickets/{ticket_id}/required-skills body."},"ReplaceUserSkillsRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"skills":{"description":"Full replacement set (an empty set clears every skill).","items":{"$ref":"#/components/schemas/SkillInput"},"type":"array"}},"required":["skills"],"type":"object"}],"description":"PUT /api/dispatch/users/{user_id}/skills body."},"ReplyByEmailView":{"properties":{"board_addresses":{"minimum":0,"type":"integer"},"catchall_address":{"type":["string","null"]},"enabled":{"description":"Inbound routing is what makes client replies land on tickets.","type":"boolean"}},"required":["enabled","board_addresses"],"type":"object"},"ReplySnippetMutationResponse":{"allOf":[{"$ref":"#/components/schemas/ReplySnippetView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): the snippet at the top level\nplus the additive `mutation` metadata."},"ReplySnippetView":{"description":"One canned reply in the org's snippet library.","properties":{"board_ids":{"description":"Boards the snippet is offered on. Empty = every board.","items":{"format":"uuid","type":"string"},"type":"array"},"body":{"type":"string"},"forked_from":{"description":"Fork-on-edit provenance: the origin snippet this copy was forked\nfrom, when it is a fork.","format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"updated_at":{"type":"string"},"updated_by":{"format":"uuid","type":"string"}},"required":["id","name","body","board_ids","updated_by","updated_at"],"type":"object"},"ReplySnippetsListResponse":{"properties":{"snippets":{"items":{"$ref":"#/components/schemas/ReplySnippetView"},"type":"array"},"vocabulary":{"description":"The closed variable vocabulary snippets may use (ticket context).","items":{"type":"string"},"type":"array"}},"required":["snippets","vocabulary"],"type":"object"},"ReportCatalog":{"description":"GET /api/report-catalog envelope.","properties":{"views":{"items":{"$ref":"#/components/schemas/CatalogView"},"type":"array"}},"required":["views"],"type":"object"},"ReportDefinitionList":{"description":"GET /api/report-definitions envelope.","properties":{"definitions":{"items":{"$ref":"#/components/schemas/ReportDefinitionView"},"type":"array"}},"required":["definitions"],"type":"object"},"ReportDefinitionMutationResponse":{"allOf":[{"$ref":"#/components/schemas/ReportDefinitionView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Mutation envelope: applied definition (flattened) + read-your-write\nmetadata (IF2)."},"ReportDefinitionRunQuery":{"description":"Query surface for running a stored definition (mirrors the prebuilt\nkinds' loose GET shape; irrelevant fields ignored).","properties":{"format":{"description":"`\"csv\"` streams text/csv instead of the JSON envelope.","type":["string","null"]},"from":{"description":"Inclusive window start (UTC day, YYYY-MM-DD). Default: 30 trailing days.","format":"date","type":["string","null"]},"limit":{"description":"Page size, clamped to [1, 500].","format":"int64","type":["integer","null"]},"to":{"description":"Inclusive window end (UTC day). Max span with `from`: 366 days.","format":"date","type":["string","null"]}},"type":"object"},"ReportDefinitionView":{"description":"A stored report definition with its registry-validated spec\nreassembled (`spec.view` == the stored `kind` column).","properties":{"created_at":{"type":"string"},"created_by":{"format":"uuid","type":"string"},"description":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"scope":{"$ref":"#/components/schemas/DefinitionScope"},"spec":{"$ref":"#/components/schemas/ReportSpec"},"updated_at":{"type":"string"}},"required":["id","name","spec","scope","created_by","created_at","updated_at"],"type":"object"},"ReportDeliveryPage":{"description":"GET /api/report-schedules/{id}/deliveries envelope — offset-paged\ndelivery history, newest slot first.","properties":{"deliveries":{"items":{"$ref":"#/components/schemas/ReportDeliveryView"},"type":"array"},"total":{"description":"Total ledger rows for the schedule (org-scoped).","format":"int64","type":"integer"}},"required":["deliveries","total"],"type":"object"},"ReportDeliveryView":{"description":"One delivery-history row (R4): the claimed slot, the outcome status\n(`delivered|partial|failed|skipped`), the per-recipient outcome\ndetail, and when the ledger row landed.","properties":{"detail":{"description":"Per-recipient outcome counters\n(`{\"delivered\": n, \"disabled\": n, \"quiet_deferred\": n, \"failed\": [..]}`)."},"id":{"format":"uuid","type":"string"},"recorded_at":{"type":"string"},"slot_at":{"description":"The claimed slot this run discharged.","type":"string"},"status":{"description":"`delivered` | `partial` | `failed` | `skipped`.","type":"string"}},"required":["id","slot_at","status","detail","recorded_at"],"type":"object"},"ReportPage":{"description":"JSON envelope for a report page.","properties":{"columns":{"description":"Stable column order (JSON objects here preserve insertion order via\nserde_json preserve_order feature transparently at runtime).","items":{"type":"string"},"type":"array"},"kind":{"type":"string"},"next_cursor":{"description":"Pass back as `cursor`; absent when exhausted or unpaged.","type":["string","null"]},"rows":{"items":{},"type":"array"},"summary":{"description":"Whole-window aggregates (never paginated)."},"view":{"description":"Versioned read-model view backing this kind (documentation parity).","type":"string"}},"required":["kind","view","columns","rows","summary"],"type":"object"},"ReportScheduleMutationResponse":{"allOf":[{"$ref":"#/components/schemas/ReportScheduleView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Mutation envelope: applied schedule + read-your-write metadata (IF2)."},"ReportSchedulePage":{"description":"GET /api/report-schedules envelope.","properties":{"next_cursor":{"description":"Pass back as `cursor`; absent when exhausted.","type":["string","null"]},"schedules":{"description":"Deterministically ordered `(name, id)`.","items":{"$ref":"#/components/schemas/ReportScheduleView"},"type":"array"}},"required":["schedules"],"type":"object"},"ReportScheduleView":{"description":"One delivery schedule with its next-run preview (the materialized\n`next_run_at` the worker claims against) and definition coordinates.","properties":{"cadence":{"$ref":"#/components/schemas/ScheduleCadence"},"created_at":{"type":"string"},"created_by":{"format":"uuid","type":"string"},"day_of_month":{"description":"1..=28 (monthly only).","format":"int32","type":["integer","null"]},"day_of_week":{"description":"0 = Monday .. 6 = Sunday (weekly only).","format":"int32","type":["integer","null"]},"definition_kind":{"type":"string"},"definition_name":{"description":"Denormalized for the listing UI (`definition_name`, `definition_kind`).","type":"string"},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"last_error":{"type":["string","null"]},"last_run_at":{"type":["string","null"]},"name":{"type":"string"},"next_run_at":{"description":"Next-run preview — the instant the worker will next claim.","type":"string"},"recipients":{"description":"Recipient staff user ids.","items":{"format":"uuid","type":"string"},"type":"array"},"report_definition_id":{"format":"uuid","type":"string"},"time_of_day":{"description":"Local wall-clock fire time, `HH:MM`.","type":"string"},"timezone":{"type":"string"},"updated_at":{"type":"string"},"window_days":{"description":"Trailing-day window each run covers.","format":"int32","type":"integer"}},"required":["id","name","report_definition_id","definition_name","definition_kind","recipients","cadence","time_of_day","timezone","window_days","enabled","next_run_at","created_by","created_at","updated_at"],"type":"object"},"ReportSpec":{"description":"A stored report spec (the private build tracker R3). Every member is registry-validated;\nsee [`ReportSpec::validate`].","properties":{"filters":{"items":{"$ref":"#/components/schemas/SpecFilter"},"type":"array"},"group_by":{"description":"Group-by dimensions (any projected column except `org_id`). Empty\nmeans a single grand-total row.","items":{"type":"string"},"type":"array"},"measures":{"description":"Aggregate measures, one or more.","items":{"$ref":"#/components/schemas/MeasureSpec"},"type":"array"},"view":{"$ref":"#/components/schemas/ViewId","description":"The versioned read-model view this report reads (the stored `kind`\ncolumn). NEVER a physical table — the enum has no other variants."},"viz":{"$ref":"#/components/schemas/VizHint","description":"Presentation hint (never affects the compiled SQL)."}},"required":["view","measures","viz"],"type":"object"},"Resolution":{"description":"The effective state of one construct after resolution.","oneOf":[{"description":"A concrete effective value is in force.","properties":{"state":{"enum":["set"],"type":"string"},"value":{"type":"object"}},"required":["value","state"],"type":"object"},{"description":"An explicit deny/opt-out is in force.","properties":{"state":{"enum":["disabled"],"type":"string"}},"required":["state"],"type":"object"},{"description":"Neither an org default nor a client override exists.","properties":{"state":{"enum":["unset"],"type":"string"}},"required":["state"],"type":"object"}]},"ResolutionSource":{"description":"Which layer supplied the effective value.","enum":["client_override","org_default","unset"],"type":"string"},"ResolveEmbedRequest":{"properties":{"fetch":{"description":"Fetch the provider's oEmbed HTML now (server-side, https, 10s\nbudget). Default false: registry-only resolution.","type":"boolean"},"url":{"description":"The embed URL to validate against the allowlist.","type":"string"}},"required":["url"],"type":"object"},"ResolveRateQuery":{"properties":{"as_of":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to","as_of"],"type":"object"},"ResolvedDetail":{"description":"Render-ready details of a resolved reference value: names and the semantic\ncategory travel WITH the resolution so consumers never branch on labels and\nnever need a second lookup.","oneOf":[{"properties":{"board_id":{"format":"uuid","type":"string"},"kind":{"enum":["board"],"type":"string"},"name":{"type":"string"}},"required":["board_id","name","kind"],"type":"object"},{"properties":{"board_id":{"format":"uuid","type":"string"},"category":{"$ref":"#/components/schemas/TicketStatusCategory","description":"The semantic category the behavior layer keys on."},"kind":{"enum":["status"],"type":"string"},"name":{"type":"string"},"status_id":{"format":"uuid","type":"string"}},"required":["status_id","name","category","board_id","kind"],"type":"object"},{"properties":{"kind":{"enum":["sla_policy"],"type":"string"},"name":{"type":"string"},"policy_id":{"format":"uuid","type":"string"}},"required":["policy_id","name","kind"],"type":"object"},{"properties":{"enabled":{"type":"boolean"},"kind":{"enum":["toggle"],"type":"string"}},"required":["enabled","kind"],"type":"object"}]},"ResolvedLabel":{"description":"The deterministic rendered label: absolute geometry in millimetres.","properties":{"barcode":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/LabelBarcode"}]},"dpi":{"format":"int32","type":"integer"},"dropped_lines":{"description":"Lines dropped because they could not fit at the minimum line height.","minimum":0,"type":"integer"},"height_mm":{"format":"double","type":"number"},"lines":{"items":{"$ref":"#/components/schemas/LabelLine"},"type":"array"},"purpose":{"type":"string"},"qr":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/LabelQr"}]},"width_mm":{"format":"double","type":"number"}},"required":["purpose","width_mm","height_mm","dpi","lines","dropped_lines"],"type":"object"},"ResolvedLink":{"allOf":[{"$ref":"#/components/schemas/DocumentLink"},{"properties":{"target_client_id":{"format":"uuid","type":["string","null"]},"target_deleted":{"description":"The target row is gone; the link outlives it (a typed read model\nsurfaces the vanishing rather than dropping the row).","type":"boolean"},"target_denied":{"description":"The caller's gate for the target's module denied, so this row\ncarries no label and no client id. Distinct from\n`target_deleted`: the interface says \"not yours\", never \"gone\".","type":"boolean"},"target_label":{"description":"Present when the target row still exists AND the caller may read\nit. `None` with `target_denied` set means the row exists for\nsomeone else.","type":["string","null"]}},"type":"object"}],"description":"A link with its target resolved to a display label for the UI\n(typed read model; the raw row never carries the label).\n\nA label is a READ OF THE TARGET, so it is present only when the\ncaller's own gate for the target's module passed (Wave 55 decision 3:\n`design/qa-p55-link-labels`). A denied target keeps the link row and\ncarries no title."},"RestoreDocumentRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"change_summary":{"type":["string","null"]},"version":{"description":"The historical version whose content the NEW version copies.","format":"int32","type":"integer"}},"required":["version"],"type":"object"}]},"RestoreRoleRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + version."}]},"RestoreTemplateRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + version."}]},"RetireTaxonomyNodeRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + version."}]},"RetryRunRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"mode":{"$ref":"#/components/schemas/WorkflowRetryMode","description":"How far back the re-run goes. `resume` (the default) starts at the\nfirst step that did not succeed, so the steps before it are not run\nagain. `from_start` re-runs the whole document and is only allowed\nwhen every step that already succeeded is safe to repeat."}},"type":"object"}],"description":"POST /api/workflows/runs/{id}/retry — the operator action."},"ReturnLoanRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"lines":{"description":"Lines to return; empty/absent = every outstanding device.","items":{"$ref":"#/components/schemas/LoanReturnLineRequest"},"type":"array"},"notes":{"type":["string","null"]},"to_location_id":{"description":"Required when any returned device came from stock.","format":"uuid","type":["string","null"]}},"type":"object"}],"description":"POST /api/client-loans/{id}/return."},"RevertDetail":{"description":"What the revert restored.","properties":{"fields":{"description":"Record fields whose values were restored.","items":{"type":"string"},"type":"array"},"reverted_seq":{"description":"The write-trail seq the revert stepped back over.","format":"int64","type":"integer"},"whole_record":{"description":"Whether this was a whole-record step (no explicit field list).","type":"boolean"}},"required":["reverted_seq","fields","whole_record"],"type":"object"},"RevertTicketRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion, exactly as any autosave write."},{"properties":{"fields":{"description":"Explicit per-field revert: restore ONLY these fields to their values\nat the target write. Omit for a whole-record step (rejected with\n`undo_reverts_only` while the record is closed).","items":{"type":"string"},"type":["array","null"]},"seq":{"description":"The write-trail `seq` to step back over (from the undo stack).","format":"int64","type":"integer"}},"required":["seq"],"type":"object"}],"description":"POST /api/tickets/{id}/revert body."},"RevokeCatalogEntryRequest":{"properties":{"reason":{"type":"string"}},"required":["reason"],"type":"object"},"RevokeInstallRequest":{"properties":{"reason":{"description":"Required. A kill switch with no recorded reason is an incident nobody\ncan review.","type":"string"}},"required":["reason"],"type":"object"},"RevokeSignOffRequest":{"description":"POST /api/sign-offs/{id}/revoke","properties":{"reason":{"description":"Why the sign-off no longer stands. Part of the audit record.","type":"string"}},"required":["reason"],"type":"object"},"RoleMutationResponse":{"allOf":[{"$ref":"#/components/schemas/RoleView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): the role at the top level plus\nthe additive `mutation` metadata."},"RoleView":{"description":"One org role row (preset or custom). `permissions` is the raw matrix\nvocabulary: `{\"matrix\": {\"module\", \"action\"}}` entries plus bare\nspecial strings (`\"financial:read\"`, `\"special:manage_users\"`, …).","properties":{"builtin":{"description":"Shipped preset tag (owner, admin, …), `null` for org-created roles.","type":["string","null"]},"forked_from":{"description":"For a forked copy: the preset tag it was seeded from.","type":["string","null"]},"holders":{"description":"Active staff holding this role.","format":"int64","type":"integer"},"id":{"format":"uuid","type":"string"},"is_system":{"description":"Preset rows are system data: restorable, never editable in place.","type":"boolean"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"permissions":{}},"required":["id","org_id","name","is_system","permissions","holders"],"type":"object"},"RolesListResponse":{"properties":{"portal_tiers":{"description":"The portal tiers (client contacts; a separate authorization model).","items":{"$ref":"#/components/schemas/PortalTierView"},"type":"array"},"presets":{"description":"The canonical shipped presets (the restore source of truth).","items":{"$ref":"#/components/schemas/PresetView"},"type":"array"},"roles":{"description":"Every org role: presets (system rows) + org-created custom roles.","items":{"$ref":"#/components/schemas/RoleView"},"type":"array"}},"required":["roles","presets","portal_tiers"],"type":"object"},"RollupBillView":{"properties":{"amount_minor":{"format":"int64","type":"integer"},"base_amount_minor":{"format":"int64","type":["integer","null"]},"bill_id":{"format":"uuid","type":"string"},"currency":{"type":"string"},"posted_at":{"format":"date-time","type":["string","null"]},"purchase_order_id":{"format":"uuid","type":"string"},"purchase_order_number":{"type":"string"}},"required":["bill_id","purchase_order_id","purchase_order_number","amount_minor","currency"],"type":"object"},"RoutingDayQuery":{"properties":{"date":{"description":"The day to assemble; defaults to today (UTC), matching the board.","format":"date","type":["string","null"]}},"type":"object"},"RoutingDayView":{"properties":{"date":{"format":"date","type":"string"},"from":{"format":"date-time","type":"string"},"status":{"$ref":"#/components/schemas/RoutingProviderStatus"},"suggestions":{"$ref":"#/components/schemas/SuggestionSetView"},"technicians":{"items":{"$ref":"#/components/schemas/TechnicianJourneyView"},"type":"array"},"to":{"format":"date-time","type":"string"}},"required":["date","from","to","status","technicians","suggestions"],"type":"object"},"RoutingProfile":{"description":"The routing-capability declaration a provider pack carries. Closed\nvocabularies only; a pack cannot declare a capability or mode the seam\ndoes not speak.","properties":{"base_url":{"description":"The pack's contract root: https-only absolute URL, no credentials.","type":"string"},"capabilities":{"description":"Closed set: `matrix` | `route` | `geocode` | `isochrone`.","items":{"type":"string"},"type":"array"},"contract_revision":{"description":"Contract revision the pack implements; the core speaks revision 1.","format":"int32","minimum":0,"type":"integer"},"modes":{"description":"Closed set: `driving` | `walking` | `cycling` | `public_transport`.","items":{"type":"string"},"type":"array"},"quality":{"description":"`measured` | `heuristic` — relay packs are measured; a heuristic\npack is allowed but never unlocks suggestion-grade features.","type":"string"},"time_control":{"description":"`none` | `departure` | `arrival` | `both` — which time control the\npack honors. Anything it does not honor must not be claimed.","type":"string"}},"required":["base_url"],"type":"object"},"RoutingProviderStatus":{"description":"Serialized provider posture (mirrors `core_routing::RoutingStatusView`\nwith a schema-friendly shape).","properties":{"capabilities":{"items":{"type":"string"},"type":"array"},"measured":{"type":"boolean"},"modes":{"items":{"type":"string"},"type":"array"},"provider_id":{"type":"string"},"provider_label":{"type":"string"},"quality":{"description":"`heuristic` | `measured`.","type":"string"},"reason":{"type":["string","null"]},"suggestions_available":{"type":"boolean"}},"required":["measured","provider_id","provider_label","quality","capabilities","modes","suggestions_available"],"type":"object"},"RowFormat":{"description":"Wire format selector for an import payload.","enum":["json","csv"],"type":"string"},"RowOutcome":{"oneOf":[{"description":"Validated clean and unmapped — committing would INSERT.","properties":{"status":{"enum":["ok"],"type":"string"}},"required":["status"],"type":"object"},{"description":"Mapped, differs, update policy — committing would UPDATE.","properties":{"status":{"enum":["would_update"],"type":"string"}},"required":["status"],"type":"object"},{"description":"Nothing would happen; the reason distinguishes identical-vs-policy.","properties":{"reason":{"type":"string"},"status":{"enum":["would_skip"],"type":"string"}},"required":["reason","status"],"type":"object"},{"description":"Validation failed or a fail-policy conflict fired; `errors` carries\nhuman-readable items for operators.","properties":{"errors":{"items":{"type":"string"},"type":"array"},"status":{"enum":["failed"],"type":"string"}},"required":["errors","status"],"type":"object"}]},"RowReport":{"description":"Per-row dry-run/commit report entry. The wire shape contract from D2:\nevery row maps to exactly one of `errors[]`, `ok`, `would_skip`,\n`would_update`.","properties":{"external_key":{"type":["string","null"]},"outcome":{"$ref":"#/components/schemas/RowOutcome"},"row_index":{"format":"int64","type":"integer"}},"required":["row_index","outcome"],"type":"object"},"RowsPayload":{"description":"The encoded row source shared by dry-run and chunk posts.","properties":{"csv":{"description":"Full CSV text including header row (`format=csv`). UTF-8, BOM\ntolerated; RFC 4180 quoting supported.","type":["string","null"]},"format":{"$ref":"#/components/schemas/RowFormat"},"json_rows":{"description":"Row objects as a JSON array of flat objects (`format=json`). Nulls\nbehave exactly like absent keys.","type":"object"}},"required":["format","json_rows"],"type":"object"},"RuleMutationResponse":{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"rule":{"$ref":"#/components/schemas/PackRuleView"}},"required":["rule","mutation"],"type":"object"},"RunBatchResponse":{"description":"POST /imports/batches/{id}/run response: the batch is queued for the\nworker exactly once; poll the batch view for counters.","properties":{"batch_id":{"format":"uuid","type":"string"},"job_id":{"format":"uuid","type":"string"},"staged_chunks":{"format":"int32","type":"integer"}},"required":["batch_id","job_id","staged_chunks"],"type":"object"},"RunDetailView":{"allOf":[{"$ref":"#/components/schemas/RunView"},{"properties":{"resume_cursor":{},"rule_definition":{},"steps":{"description":"The whole trace in execution order.","items":{"$ref":"#/components/schemas/RunStepView"},"type":"array"}},"required":["rule_definition","steps"],"type":"object"}],"description":"A run plus the exact document it ran and the state it continues from.\nThis is the response that makes the ledger an audit record: with it, a\nrun explains itself without a second table."},"RunListQuery":{"description":"Query for the run ledger listing.","properties":{"cursor":{"description":"Cursor from the previous page's `next_cursor`.","type":["string","null"]},"limit":{"description":"Page size (default 50, capped at 200).","format":"int64","type":["integer","null"]},"rule_id":{"description":"Optional rule filter: only that workflow's runs.","format":"uuid","type":["string","null"]},"status":{"description":"Optional status filter: running | waiting | succeeded | failed | abandoned.","type":["string","null"]}},"type":"object"},"RunListResponse":{"properties":{"next_cursor":{"description":"Opaque cursor for the next page; `null` on the last one.","type":["string","null"]},"runs":{"items":{"$ref":"#/components/schemas/RunView"},"type":"array"}},"required":["runs"],"type":"object"},"RunReportResponse":{"description":"Per-row fate report — the D2 contract shape\n(`row_index → errors[] | ok | would_skip | would_update`).","properties":{"batch_id":{"format":"uuid","type":"string"},"conflict_policy":{"$ref":"#/components/schemas/ConflictPolicy"},"dry_run":{"type":"boolean"},"entity_kind":{"$ref":"#/components/schemas/EntityKind"},"rows":{"items":{"$ref":"#/components/schemas/RowReport"},"type":"array"},"summary":{"$ref":"#/components/schemas/ChunkSummaryWire"}},"required":["batch_id","dry_run","entity_kind","conflict_policy","summary","rows"],"type":"object"},"RunRetryResponse":{"description":"The retry's response: the NEW run row (the continuation of the failed\none) and the IF2 mutation metadata, on the same envelope every other\nmutation in the product answers with.","properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"run":{"$ref":"#/components/schemas/RunView"}},"required":["run","mutation"],"type":"object"},"RunSpecRequest":{"description":"POST /api/reports/run-spec body: an inline spec (validated through the\nsame registry as stored definitions) plus the run window. This is the\nbuilder's PREVIEW-before-save surface — nothing is persisted.","properties":{"from":{"description":"Inclusive window start (UTC day, YYYY-MM-DD). Default: 30 trailing days.","format":"date","type":["string","null"]},"limit":{"description":"Page size, clamped to [1, 500].","format":"int64","type":["integer","null"]},"spec":{"$ref":"#/components/schemas/ReportSpec","description":"The registry-validated report spec."},"to":{"description":"Inclusive window end (UTC day). Max span with `from`: 366 days.","format":"date","type":["string","null"]}},"required":["spec"],"type":"object"},"RunStepListQuery":{"description":"Query for one run's step trace.","properties":{"cursor":{"type":["string","null"]},"limit":{"format":"int64","type":["integer","null"]}},"type":"object"},"RunStepListResponse":{"properties":{"next_cursor":{"type":["string","null"]},"steps":{"items":{"$ref":"#/components/schemas/RunStepView"},"type":"array"}},"required":["steps"],"type":"object"},"RunStepView":{"description":"One step attempt inside a run.","properties":{"attempt":{"format":"int32","type":"integer"},"error":{"type":["string","null"]},"error_code":{"type":["string","null"]},"finished_at":{"format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"input":{},"iteration":{"format":"int32","type":["integer","null"]},"node_id":{"type":"string"},"node_label":{"type":"string"},"output":{},"started_at":{"format":"date-time","type":"string"},"status":{"$ref":"#/components/schemas/WorkflowStepStatus"},"step_index":{"format":"int32","type":"integer"}},"required":["id","step_index","node_id","node_label","attempt","status","input","started_at"],"type":"object"},"RunView":{"description":"A run as the operator surface reads it. The document snapshot is NOT\nhere: it is large, it is write-once, and the operator does not need it to\nread a status. It is on the detail response, where an operator who wants\nto know exactly what ran can find it.","properties":{"catalogue_version":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"error":{"type":["string","null"]},"error_code":{"type":["string","null"]},"finished_at":{"format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"resumed_from":{"description":"The parked run this row continues. A row is an EXECUTION ATTEMPT;\na resume is a new row, never an edit of the parked one.","format":"uuid","type":["string","null"]},"retried":{"description":"Derived, never stored: a later run re-runs this one.","type":"boolean"},"retry_of":{"description":"The failed run this row re-runs.","format":"uuid","type":["string","null"]},"rule_id":{"format":"uuid","type":["string","null"]},"rule_name":{"type":"string"},"rule_version":{"format":"int32","type":"integer"},"started_at":{"format":"date-time","type":"string"},"status":{"$ref":"#/components/schemas/WorkflowRunStatus"},"superseded":{"description":"Derived, never stored: a later run continues this one. Storing the\nflag would be a mutation of the parked row wearing a different name.","type":"boolean"},"trigger":{"$ref":"#/components/schemas/WorkflowRunTrigger"},"trigger_event_id":{"format":"uuid","type":["string","null"]},"trigger_job_id":{"format":"uuid","type":["string","null"]},"trigger_user_id":{"format":"uuid","type":["string","null"]},"updated_at":{"format":"date-time","type":"string"},"wake_at":{"format":"date-time","type":["string","null"]},"wake_on":{"type":["string","null"]}},"required":["id","rule_name","rule_version","catalogue_version","status","trigger","superseded","retried","started_at","created_at","updated_at"],"type":"object"},"RunWindowView":{"description":"One issued run window in the overlap warning.","properties":{"number":{"type":"string"},"period_from":{"format":"date","type":"string"},"period_to":{"format":"date","type":"string"},"run_id":{"format":"uuid","type":"string"}},"required":["run_id","number","period_from","period_to"],"type":"object"},"ScanCandidateView":{"description":"One candidate when a code matches more than one row of the same kind.","properties":{"display_name":{"type":"string"},"entity_id":{"format":"uuid","type":"string"},"kind":{"type":"string"},"secondary":{"type":["string","null"]}},"required":["kind","entity_id","display_name"],"type":"object"},"ScanResolutionView":{"description":"The typed meaning of one scanned string. `entity_id` is the resolved\nentity: the stock item, the serialized unit, the location, or the device\nasset. `deep_link` is always the canonical link for that entity, so a\ncaller can print/re-show exactly what it scanned.","properties":{"candidates":{"description":"Populated when the winning kind matched more than one row.","items":{"$ref":"#/components/schemas/ScanCandidateView"},"type":"array"},"client_name":{"description":"Device assets only: the client the asset belongs to. NULL for an\ninternal-org asset — the MSP's own fleet carries no client (ADR 0012).","type":["string","null"]},"deep_link":{"type":["string","null"]},"device_asset_id":{"format":"uuid","type":["string","null"]},"display_name":{"type":"string"},"entity_id":{"format":"uuid","type":["string","null"]},"input":{"type":"string"},"is_tool":{"description":"Serialized units only: the unit is a tool (`is_tool` on its item).","type":"boolean"},"item_kind":{"description":"Stock items/serials only: \"serialized\" | \"consumable\".","type":["string","null"]},"kind":{"description":"\"stock_item\" | \"serialized_unit\" | \"stock_location\" | \"device_asset\" | \"unknown\"","type":"string"},"location_id":{"format":"uuid","type":["string","null"]},"location_name":{"description":"Device assets only: the site the asset sits at.","type":["string","null"]},"secondary":{"type":["string","null"]},"serial_id":{"format":"uuid","type":["string","null"]},"serial_status":{"description":"Serialized units only: in_stock | out | checked_out | loaned.","type":["string","null"]},"stock_item_id":{"description":"Present for stock_item and serialized_unit: the parent item.","format":"uuid","type":["string","null"]},"warranty":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/WarrantyWindow","description":"Device assets only: the warranty horizon, when the asset carries\nenough to derive one. Resolution is one read BECAUSE these ride\nalong: a found sheet prints the pack's three facts without a second\nround trip."}]}},"required":["input","kind","display_name","is_tool"],"type":"object"},"ScanResolveRequest":{"properties":{"text":{"description":"The raw scanned text (keyboard-wedge burst, camera decode, or a\npasted deep link).","type":"string"}},"required":["text"],"type":"object"},"ScanStatus":{"description":"Outcome of the malware scan that runs between webhook receipt and\npersistence. `skipped` is the documented \"none\" mode (no scanner\nconfigured) — never a silent pass of an actual verdict.","enum":["clean","infected","skipped"],"type":"string"},"ScanView":{"properties":{"clamd_addr":{"description":"Configured clamd TCP address — a host:port, never a credential.","type":["string","null"]},"detection_count":{"format":"int64","type":"integer"},"last_detection_at":{"format":"date-time","type":["string","null"]},"last_scan_at":{"format":"date-time","type":["string","null"]},"provider":{"description":"`clamd` (scanner wired) or `none` (scanning disabled).","type":"string"}},"required":["provider","detection_count"],"type":"object"},"ScheduleCadence":{"description":"Wire cadence of a schedule (snake_case slugs matching the stored\ncolumn and the worker's parser).","enum":["daily","weekly","monthly"],"type":"string"},"ScheduleListQuery":{"description":"GET /api/report-schedules query.","properties":{"cursor":{"description":"Opaque keyset cursor from a previous page's `next_cursor`.","type":["string","null"]},"limit":{"description":"Page size, clamped to [1, 200].","format":"int64","type":["integer","null"]}},"type":"object"},"ScheduleMutationResponse":{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"schedule":{"$ref":"#/components/schemas/PmSchedule"}},"required":["schedule","mutation"],"type":"object"},"SchedulingGroup":{"description":"A free-form admin-defined group/department — a pure availability\npool (\"Service Desk\"). Deliberately NOT the org chart: no reporting\nlines, no permissions; membership only feeds unioned availability.","properties":{"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"description":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"member_user_ids":{"description":"Current membership (unioned availability pool).","items":{"format":"uuid","type":"string"},"type":"array"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","name","member_user_ids","created_at","updated_at"],"type":"object"},"SchedulingLink":{"description":"A shareable booking link. The raw token is generated at create,\nreturned ONCE in the create response, and never stored (only its\nSHA-256 hash); the tech's visible lifecycle is the view/schedule/\nrevoke timestamps.","properties":{"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"expires_at":{"format":"date-time","type":["string","null"]},"first_viewed_at":{"format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"label":{"type":["string","null"]},"meeting_type_id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"scheduled_appointment_id":{"format":"uuid","type":["string","null"]},"scheduled_at":{"format":"date-time","type":["string","null"]},"status":{"$ref":"#/components/schemas/SchedulingLinkStatus"},"ticket_id":{"description":"Ticket cross-ref: bookings land back on the originating ticket.","format":"uuid","type":["string","null"]},"updated_at":{"format":"date-time","type":"string"},"view_count":{"format":"int64","type":"integer"}},"required":["id","org_id","meeting_type_id","status","view_count","created_at","updated_at"],"type":"object"},"SchedulingLinkBooking":{"description":"The link → booking trail row (mirrors the appointment for audit).","properties":{"appointment_id":{"format":"uuid","type":"string"},"booker_email":{"type":["string","null"]},"booker_name":{"type":["string","null"]},"booker_notes":{"type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"link_id":{"format":"uuid","type":"string"}},"required":["link_id","appointment_id","created_at"],"type":"object"},"SchedulingLinkStatus":{"description":"Link lifecycle: `active` → `used` (a booking landed) or `revoked`\n(tech pulled it). Expiry is DERIVED (`expires_at` + the template's\nbooking window), never a stored state.","enum":["active","revoked","used"],"type":"string"},"Scorecard":{"description":"One staff member's scorecard over a window.","properties":{"csat":{"$ref":"#/components/schemas/CsatSection"},"display_name":{"type":"string"},"resolution":{"$ref":"#/components/schemas/ScorecardLatency"},"response":{"$ref":"#/components/schemas/ScorecardLatency"},"sla":{"$ref":"#/components/schemas/ScorecardSla"},"user_id":{"format":"uuid","type":"string"},"window":{"$ref":"#/components/schemas/ScorecardWindow"},"workload":{"$ref":"#/components/schemas/ScorecardWorkload"}},"required":["user_id","display_name","window","sla","response","resolution","workload","csat"],"type":"object"},"ScorecardLatency":{"description":"First-staff-response latency block (seconds; staff-authored comments\nonly — portal replies are structurally excluded by the view).","properties":{"count":{"format":"int64","type":"integer"},"median_seconds":{"format":"double","type":["number","null"]},"p95_seconds":{"format":"double","type":["number","null"]}},"required":["count"],"type":"object"},"ScorecardResponse":{"allOf":[{"$ref":"#/components/schemas/Scorecard"}]},"ScorecardSettingsResponse":{"properties":{"gamification_opt_in":{"type":"boolean"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","gamification_opt_in"],"type":"object"},"ScorecardSla":{"description":"SLA attainment block. `response_total`/`resolution_total` count only\ntickets where BOTH the SLA target and the actual instant exist (the\nthree-valued metrics from `ops.v1_sla_outcomes`); attainment is the\nmet/total ratio, `None` when the total is zero.","properties":{"breached":{"format":"int64","type":"integer"},"resolution_attainment":{"format":"double","type":["number","null"]},"resolution_met":{"format":"int64","type":"integer"},"resolution_total":{"format":"int64","type":"integer"},"response_attainment":{"format":"double","type":["number","null"]},"response_met":{"format":"int64","type":"integer"},"response_total":{"format":"int64","type":"integer"}},"required":["response_total","response_met","resolution_total","resolution_met","breached"],"type":"object"},"ScorecardWindow":{"properties":{"from":{"description":"Inclusive start (UTC midnight).","format":"date-time","type":"string"},"to":{"description":"Exclusive end (UTC midnight of the day after `to`).","format":"date-time","type":"string"}},"required":["from","to"],"type":"object"},"ScorecardWorkload":{"description":"Workload mix over APPROVED time entries only — drafts and rejected\nentries are never scorecard truth (Wave 14 lane-1 approval contract).","properties":{"approved_billable_minutes":{"format":"int64","type":"integer"},"approved_entries":{"format":"int64","type":"integer"},"approved_minutes":{"format":"int64","type":"integer"},"billable_ratio":{"description":"billable / total approved minutes; `None` with no approved work.","format":"double","type":["number","null"]},"by_work_type":{"description":"APPROVED minutes per work type; `work_type: null` groups\nunclassified entries.","items":{"$ref":"#/components/schemas/WorkTypeSlice"},"type":"array"}},"required":["approved_entries","approved_minutes","approved_billable_minutes","by_work_type"],"type":"object"},"SearchPinList":{"properties":{"pins":{"items":{"$ref":"#/components/schemas/SearchPinView"},"type":"array"}},"required":["pins"],"type":"object"},"SearchPinView":{"description":"One pinned result as the /search surface renders it.","properties":{"created_at":{"type":"string"},"entity_id":{"format":"uuid","type":"string"},"entity_kind":{"type":"string"},"id":{"format":"uuid","type":"string"},"parent_id":{"description":"e.g. comment → its ticket id; mirrors the live row.","format":"uuid","type":["string","null"]},"query":{"description":"The pinned query, snapshotted (a pin re-runs THIS query).","type":"string"},"title":{"description":"The title AT PIN TIME (the honest snapshot, never re-faked).","type":"string"}},"required":["id","query","entity_kind","entity_id","title","created_at"],"type":"object"},"SearchQuery":{"properties":{"client_id":{"description":"Optional client scope for the `document` kind (0089, per-client\ndocument scoping): when set, indexed documents answer only when\norg-global (client_scope NULL) or scoped to THIS client. Every\nother kind is untouched.","format":"uuid","type":["string","null"]},"cursor":{"description":"Opaque keyset cursor from the previous page's `next_cursor`\n(docs/perf.md F2). When present the walk resumes strictly after the\nanchor row and `offset` is ignored — the two mechanisms answer\ndifferent callers, and the cursor is the one that stays at page-1\ncost as depth grows.","type":["string","null"]},"limit":{"format":"int64","type":"integer"},"offset":{"format":"int64","type":"integer"},"q":{"description":"Free-text query using websearch syntax: `\"exact phrases\"`,\n`-excluded`, plain terms AND-ed. Tokens ≥3 chars also match as\nsubstrings via the trigram fallback.","type":"string"}},"required":["q"],"type":"object"},"SearchResponse":{"properties":{"kinds":{"description":"Kinds the principal may see, mirrored into the query filter.","items":{"type":"string"},"type":"array"},"limit":{"format":"int64","type":"integer"},"next_cursor":{"description":"Present when this page is full and the walk may continue. Feed it\nback as `cursor`. It is absent on the fused (embedding) path: RRF\nscores cannot be resumed cheaply, so that path keeps offset paging\nand never hands out a cursor it cannot honour.","type":["string","null"]},"offset":{"format":"int64","type":"integer"},"query":{"description":"The trimmed query actually executed.","type":"string"},"results":{"items":{"$ref":"#/components/schemas/SearchResult"},"type":"array"},"total":{"description":"Matches across all permitted kinds, counted up to a cap. Compare with\n`total_capped` before showing it as an exact number.","format":"int64","type":"integer"},"total_capped":{"description":"True when the predicate matched more rows than the count scanned, so\n`total` is a floor rather than the exact size.","type":"boolean"}},"required":["query","total","total_capped","kinds","limit","offset","results"],"type":"object"},"SearchResult":{"properties":{"entity_id":{"format":"uuid","type":"string"},"entity_kind":{"description":"`ticket` | `comment` | `client` | `site` | `contact`.","type":"string"},"parent_id":{"description":"comment → its ticket id; NULL otherwise.","format":"uuid","type":["string","null"]},"score":{"description":"Blended FTS + trigram relevance score (higher is better).","format":"double","type":"number"},"snippet":{"description":"Raw body excerpt for client-side snippet rendering.","type":"string"},"title":{"type":"string"}},"required":["entity_kind","entity_id","title","snippet","score"],"type":"object"},"SecretCategoryGrant":{"description":"A role × folder category grant (`secret_category_grants`). Grants are\nDATA flowing through the central policy module: the matrix permission\n(`secrets:read`/`secrets:edit`) is checked FIRST, then the category\ngate — restricted folders deny unless a grant row for one of the\ncaller's roles allows it.","properties":{"can_edit":{"type":"boolean"},"can_read":{"type":"boolean"},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"folder_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"role_id":{"format":"uuid","type":"string"},"updated_at":{"format":"date-time","type":"string"},"updated_by":{"format":"uuid","type":["string","null"]}},"required":["id","org_id","role_id","folder_id","can_read","can_edit","created_at","updated_at"],"type":"object"},"SecretFieldMeta":{"description":"A material field of a secret record — METADATA ONLY view. The\nciphertext and plaintext never ride this entity; `value_ciphertext`\nlives in `secret_fields` only.","properties":{"field_key":{"description":"Closed write-path vocabulary: `secret`, `notes`, `username`, `url`,\nplus the document-field keys (metadata for non-material pointers).","type":"string"},"has_value":{"description":"Whether a sealed value exists for this key.","type":"boolean"},"record_id":{"format":"uuid","type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["record_id","field_key","updated_at"],"type":"object"},"SecretFieldValue":{"properties":{"field_key":{"type":"string"},"value":{"description":"Plaintext on the WIRE ONLY: sealed before any persistence, never\nreturned outside the audited reveal response.","type":"string"}},"required":["field_key","value"],"type":"object"},"SecretFieldValuePatch":{"description":"PATCH field entry (A5, Wave 16 lane 9): `value: null` CLEARS the\nsealed field (row deleted, audited via the record-updated event);\na value rotates the sealed ciphertext.","properties":{"field_key":{"type":"string"},"value":{"description":"None (json null) clears the sealed field; Some rotates it.","type":["string","null"]}},"required":["field_key"],"type":"object"},"SecretFolder":{"description":"A secret category folder (`secret_folders`). Exactly one of org/client\nscope rows: org-scope rows carry `org_id` (client_id NULL),\nclient-scope rows carry `client_id` (org_id NULL — the tenancy spine\nrides the owning client's org via the RLS policy). Seeded defaults\n(`is_default_seed`) are immutable ADR-0004 template rows: orgs fork,\nnever edit in place. `restricted` is deny-by-default: invisible to a\nrole without a `secret_category_grants` row.","properties":{"client_id":{"description":"Client scope; NULL on org-scope rows.","format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"forked_from":{"description":"The seed row this folder forked from (customization provenance).","format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_default_seed":{"description":"Immutable shipped default (ADR 0004 template doctrine).","type":"boolean"},"kind":{"$ref":"#/components/schemas/SecretFolderKind"},"label":{"type":"string"},"org_id":{"description":"Org-scope tenancy root; NULL on client-scope rows.","format":"uuid","type":["string","null"]},"parent_id":{"description":"Parent folder; NULL = root of its scope tree.","format":"uuid","type":["string","null"]},"restricted":{"description":"Deny-by-default category flag (financial:read gate shape).","type":"boolean"},"slug":{"description":"Sibling-unique within (scope, parent); lowercase spellings.","type":"string"},"updated_at":{"format":"date-time","type":"string"},"updated_by":{"format":"uuid","type":["string","null"]}},"required":["id","slug","label","kind","is_default_seed","restricted","created_at","updated_at"],"type":"object"},"SecretFolderKind":{"description":"The closed seeded-configuration vocabulary for folders.","enum":["passwords","api_keys","certificates","recovery_codes","documents"],"type":"string"},"SecretRecord":{"description":"A SecretRecord (`secret_records`, migration 0078) — the generalization\nbeside the shipped `PasswordEntry` (whose UI and responses stay byte-\ncompatible; the vault never becomes a destructive cutover). Scope\nmirrors its owning folder; every material field is sealed in\n`secret_fields` and reachable ONLY via the audited reveal path.","properties":{"client_id":{"description":"Client scope; NULL on org-scope rows.","format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"expires_at":{"format":"date-time","type":["string","null"]},"fields":{"description":"Metadata-only field views (NEVER carries ciphertext or plaintext).","items":{"$ref":"#/components/schemas/SecretFieldMeta"},"type":"array"},"file_bytes":{"format":"int64","type":["integer","null"]},"file_sha256":{"type":["string","null"]},"folder_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"kind":{"$ref":"#/components/schemas/SecretRecordKind"},"last_rotated_at":{"description":"Stamped by the same update that moves `rotate_at` forward.","format":"date-time","type":["string","null"]},"object_key":{"description":"Document-kind files ride object storage as ciphertext with\npointer+hash rows — never material blobs in this table.","type":["string","null"]},"org_id":{"description":"Org-scope tenancy root; NULL on client-scope rows.","format":"uuid","type":["string","null"]},"rotate_at":{"description":"Operator-scheduled rotation due date (Wave 19 lane 3) — additive\non 0078's record shape; the worker sweep reminds once past it.","format":"date-time","type":["string","null"]},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"updated_by":{"format":"uuid","type":["string","null"]}},"required":["id","folder_id","kind","title","created_at","updated_at"],"type":"object"},"SecretRecordKind":{"description":"The kind of a `secret_records` row.","enum":["password","api_key","certificate","recovery_code","document"],"type":"string"},"SecretRecordMutationResponse":{"allOf":[{"$ref":"#/components/schemas/SecretRecord"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): the metadata-only record plus\nthe write-trail/replay metadata (mirrors PasswordMutationResponse)."},"SecretReveal":{"description":"The audited reveal response: the ONLY plaintext carrier for secret\nrecords, mirroring `/passwords/{id}/reveal` (requires secrets:edit,\nemits `secret.revealed`, plaintext returned exactly once). Reveal\npayloads carry no provenance detail beyond the title — provenance\nqueries never carry the secret.","properties":{"fields":{"description":"field_key → plaintext for every sealed field.","items":{"$ref":"#/components/schemas/SecretRevealField"},"type":"array"},"id":{"format":"uuid","type":"string"},"title":{"type":"string"}},"required":["id","title","fields"],"type":"object"},"SecretRevealField":{"properties":{"field_key":{"type":"string"},"value":{"type":"string"}},"required":["field_key","value"],"type":"object"},"SecretShareLink":{"description":"A one-time share link row (`secret_share_links`, migration 0096).\nThe token NEVER round-trips here: the DB stores a SHA-256 hash and\nthe raw token exists exactly once, inside the create response\n(`SecretShareLinkCreated`). Scoped to the org, expiring by\n`expires_at`, single-use by construction (transactional claim),\naudited through `audit_events` at every lifecycle step.","properties":{"consumed_at":{"description":"Consumed exactly once: the claim's stamp; `consumed_by` is the\nclaiming org user (the claimer must belong to the sharing org).","format":"date-time","type":["string","null"]},"consumed_by":{"format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"expires_at":{"description":"Long absolute URL is composed client-side off the claim token;\nresponses here carry none of it.","format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"label":{"type":"string"},"revoked_at":{"description":"Non-NULL only while the link is dead-by-creator (revoked links\ncan never have been consumed — the row CHECK keeps the pair\nexclusive).","format":"date-time","type":["string","null"]},"subject_id":{"format":"uuid","type":"string"},"subject_kind":{"$ref":"#/components/schemas/ShareSubjectKind","description":"Which vault subject the link shares (ids/title only — never the\nmaterial)."}},"required":["id","subject_kind","subject_id","label","expires_at","created_at"],"type":"object"},"SecretShareLinkIssued":{"description":"The create response — the ONLY place the raw token exists on the\nwire; the URL is resolved by the web app at `/{app}/share?token=…`.\n(Named `Issued` because `SecretShareLinkCreated` is the domain-EVENT\npayload's struct; this is the HTTP response.)","properties":{"expires_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"label":{"type":"string"},"link_path":{"description":"Composed from the token: `?token=...` for the consumer surface.","type":"string"},"token":{"description":"The one-time token, verbatim — never stored server-side (SHA-256\nonly).","type":"string"}},"required":["token","link_path","id","expires_at","label"],"type":"object"},"SegmentEnd":{"description":"Why a timer segment ended. Preserved forever — the pause reason is\nbillable-work metadata, not a transient UI state.","enum":["stopped","paused","switched"],"type":"string"},"SelfHostLinksView":{"properties":{"glitchtip":{"type":["string","null"]},"grafana":{"type":["string","null"]},"loki":{"type":["string","null"]},"otel_metrics":{"type":["string","null"]},"tempo":{"type":["string","null"]},"uptime_kuma":{"description":"Uptime Kuma's status-page UI (Wave 34 claims close-out: the section\n2 observability row that had not shipped). Same link-out shape as\nGrafana — a standalone operator UI in the observability profile\nthat Marula never reads from, so the pane can only offer the link\nand never report on it.","type":["string","null"]}},"type":"object"},"SelfHostView":{"properties":{"detections":{"items":{"$ref":"#/components/schemas/DetectionView"},"type":"array"},"garage":{"$ref":"#/components/schemas/GarageView"},"glitchtip_dsns":{"items":{"$ref":"#/components/schemas/DsnRow"},"type":"array"},"links":{"$ref":"#/components/schemas/SelfHostLinksView"},"scan":{"$ref":"#/components/schemas/ScanView"},"services":{"items":{"$ref":"#/components/schemas/ServiceView"},"type":"array"}},"required":["scan","detections","services","glitchtip_dsns","links","garage"],"type":"object"},"SendStatementRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key."}],"description":"POST /api/invoices/{id}/dunning/statement body."},"SendStatementResponse":{"description":"POST /api/invoices/{id}/dunning/statement response.","properties":{"client_id":{"format":"uuid","type":"string"},"currency":{"type":"string"},"open_invoices":{"description":"Open invoices on the statement (number, due date, outstanding).","items":{"$ref":"#/components/schemas/StatementLine"},"type":"array"},"queued_to":{"description":"Addresses the statement email was queued to (client contacts\nwith an address). The message rides the outbound ledger; in\nnone-mode transport it stays queued like every other leg.","items":{"type":"string"},"type":"array"},"total_outstanding_minor":{"description":"Sum outstanding across the statement, minor units.","format":"int64","type":"integer"}},"required":["client_id","currency","open_invoices","total_outstanding_minor","queued_to"],"type":"object"},"SerialListQuery":{"properties":{"limit":{"format":"int64","type":["integer","null"]},"location_id":{"format":"uuid","type":["string","null"]},"status":{"type":["string","null"]}},"type":"object"},"ServiceAccountList":{"properties":{"service_accounts":{"items":{"$ref":"#/components/schemas/ServiceAccountView"},"type":"array"}},"required":["service_accounts"],"type":"object"},"ServiceAccountView":{"properties":{"cert_fingerprint":{"description":"Lowercase hex SHA-256 of the pinned client certificate.","type":"string"},"cert_status":{"description":"Derived lifecycle: \"valid\" | \"expired\" | \"revoked\".","type":"string"},"cert_subject":{"type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"description":"The enrolling principal's id (the human admin), if a human enrolled it.","format":"uuid","type":["string","null"]},"expires_at":{"format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"last_used_at":{"format":"date-time","type":["string","null"]},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"revocation_reason":{"description":"Admin-supplied reason recorded at revocation, if any.","type":["string","null"]},"revoked_at":{"format":"date-time","type":["string","null"]},"scopes":{"items":{"type":"string"},"type":"array"},"status":{"description":"Admin lifecycle: \"active\" | \"suspended\".","type":"string"}},"required":["id","org_id","name","cert_fingerprint","scopes","status","cert_status","created_at"],"type":"object"},"ServiceView":{"properties":{"detail":{"type":["string","null"]},"id":{"type":"string"},"label":{"type":"string"},"loops":{"description":"The raw `/healthz` body when the service speaks JSON (the worker's\nper-loop liveness flags ride here); null otherwise."},"state":{"description":"`up` | `down` | `not_configured` | `unknown`.","type":"string"}},"required":["id","label","state"],"type":"object"},"SessionInfo":{"properties":{"chain_root":{"type":"boolean"},"created_at":{"format":"date-time","type":"string"},"current":{"description":"True for the session making the request.","type":"boolean"},"expires_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"user_agent":{"type":["string","null"]}},"required":["id","created_at","expires_at","current","chain_root"],"type":"object"},"SetChecklistItemRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive."},{"properties":{"checked":{"type":"boolean"},"na":{"description":"Shared not-applicable state (Wave 12 lane 6): `true` marks the item\nN-A as ORG-WIDE server truth and clears the checkmark (the states\nare mutually exclusive; checked semantics are untouched for callers\nthat omit it — legacy payloads default to `false` and behave\nexactly as before). N-A REQUIRED items are exempt from pre-close\nenforcement and derived progress.","type":"boolean"},"note_reason":{"description":"Server-composed ledger reason (Wave 17 lane 11, §5.1): MANDATORY\n(non-empty, ≤2000 chars) when this call sets N-A (na: true) or\nreverses a terminal state (reopen); ignored for plain checks. The\nreason text lands in the ledger as the auto-appended internal note\ncomposed and inserted in THIS transaction — never a client-side\nsecond post.","type":["string","null"]}},"required":["checked"],"type":"object"}]},"SetCustomDomainRequest":{"properties":{"domain":{"description":"The custom domain to claim (e.g. `support.msp.example.com`). Stored\nnormalized (lowercased, trailing dot stripped).","example":"support.msp.example.com","type":"string"}},"required":["domain"],"type":"object"},"SetCustomDomainResponse":{"description":"POST response: exactly the three fields the private build tracker A8 (B) names.","properties":{"domain":{"type":"string"},"instructions":{"$ref":"#/components/schemas/CustomDomainInstructions"},"verification_token":{"type":"string"}},"required":["domain","verification_token","instructions"],"type":"object"},"SetDependenciesRequest":{"properties":{"depends_on":{"description":"The FULL finish-to-start dependency set for the task (PUT\nsemantics): every id must be a task of the same project; the set\nmust not create a cycle (typed 422 names the offending edge).","items":{"format":"uuid","type":"string"},"type":"array"}},"required":["depends_on"],"type":"object"},"SetEmailVerifiedRequest":{"properties":{"verified":{"description":"true = admin-stamped verification; false = revokes the stamp.","type":"boolean"}},"required":["verified"],"type":"object"},"SetJourneyStartRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"address":{"type":["string","null"]},"date":{"format":"date","type":"string"},"kind":{"description":"`home` | `office` | `site` | `address`.","type":"string"},"label":{"type":["string","null"]},"latitude":{"format":"double","type":["number","null"]},"longitude":{"format":"double","type":["number","null"]},"site_id":{"format":"uuid","type":["string","null"]},"user_id":{"format":"uuid","type":["string","null"]}},"required":["date","kind"],"type":"object"}]},"SetQueueRankRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"note":{"type":["string","null"]},"rank":{"description":"Explicit queue position: 0 sorts first among overrides.","format":"int32","type":"integer"}},"required":["rank"],"type":"object"}],"description":"PUT /api/dispatch/queue/{ticket_id} body."},"SetSecretGrantRequest":{"properties":{"can_edit":{"type":"boolean"},"can_read":{"type":"boolean"},"folder_id":{"format":"uuid","type":"string"},"role_id":{"format":"uuid","type":"string"}},"required":["role_id","folder_id","can_read","can_edit"],"type":"object"},"SetSkillEnabledRequest":{"properties":{"enabled":{"type":"boolean"}},"required":["enabled"],"type":"object"},"SetSuspensionRequest":{"properties":{"notify_after":{"description":"Whole seconds after the flag is set before the notice starts showing.\nOmitted or `null` = due immediately.","format":"int64","type":["integer","null"]},"reason":{"description":"Required when `suspended` is `true`, refused when it is `false`:\n`billing_lapsed`, `abuse`, or `other`.","type":["string","null"]},"suspended":{"description":"`true` flags the org; `false` clears the flag and both companions.\nSetting an already-flagged org is idempotent — it keeps the original\n`suspended_at`, so a retry cannot restart the notice countdown.","type":"boolean"}},"required":["suspended"],"type":"object"},"SetTicketFieldValueRequest":{"properties":{"value":{}},"required":["value"],"type":"object"},"SetTransitionsRequest":{"properties":{"edges":{"description":"The complete allowed matrix. Empty list returns the board to\nfloor-mode governance.","items":{"$ref":"#/components/schemas/TransitionEdge"},"type":"array"}},"required":["edges"],"type":"object"},"SetUserRoleRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + version."},{"properties":{"role":{"description":"Builtin org role preset: owner, admin, dispatcher, senior_tech, technician, bookkeeper, auditor, read_only.","type":"string"}},"required":["role"],"type":"object"}]},"SetUserStatusRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + version."},{"properties":{"active":{"description":"`false` deactivates: sessions revoked, login blocked. Never deletes.","type":"boolean"}},"required":["active"],"type":"object"}]},"SettingKind":{"description":"The value shapes the registry understands. A stored value must satisfy\nits declaration's kind or the write is rejected (validation is applied\nbefore persistence, never after).","oneOf":[{"properties":{"type":{"enum":["boolean"],"type":"string"}},"required":["type"],"type":"object"},{"description":"Inclusive integer range.","properties":{"type":{"enum":["integer"],"type":"string"},"value":{"description":"Inclusive integer range.","properties":{"max":{"format":"int64","type":"integer"},"min":{"format":"int64","type":"integer"}},"required":["min","max"],"type":"object"}},"required":["value","type"],"type":"object"},{"description":"Free text with a hard length cap.","properties":{"type":{"enum":["text"],"type":"string"},"value":{"description":"Free text with a hard length cap.","properties":{"max_len":{"minimum":0,"type":"integer"}},"required":["max_len"],"type":"object"}},"required":["value","type"],"type":"object"},{"description":"Closed choice set; the wire value is the option string itself.","properties":{"type":{"enum":["enumeration"],"type":"string"}},"required":["type"],"type":"object"}]},"SettingValue":{"description":"A concrete setting value. Only three payload types exist by design —\nricher structures are entities, not settings.","oneOf":[{"type":"boolean"},{"format":"int64","type":"integer"},{"type":"string"}]},"ShareClaimMaterial":{"description":"The unified claim response: the material exactly once (the claim\nconsumes the link). Mirrors `SecretReveal` / `PasswordReveal`\ncontents under one shape — `fields` carries `field_key` → plaintext.","properties":{"fields":{"items":{"$ref":"#/components/schemas/SecretRevealField"},"type":"array"},"subject_id":{"format":"uuid","type":"string"},"subject_kind":{"$ref":"#/components/schemas/ShareSubjectKind"},"title":{"type":"string"}},"required":["subject_kind","subject_id","title","fields"],"type":"object"},"ShareSubjectKind":{"description":"Which vault subject a share link or rotation refers to. One-time\nshare links cover BOTH the native secret records and the legacy byte-\ncompatible password entries; rotation mirrors the same pair.","enum":["secret_record","password"],"type":"string"},"ShipOutRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"carrier":{"description":"Optional carrier + tracking ref (deeplinked by later lanes).","type":["string","null"]},"client_id":{"description":"Ship-to client (fleet/loan delivery anchor).","format":"uuid","type":["string","null"]},"item_id":{"format":"uuid","type":"string"},"location_id":{"format":"uuid","type":"string"},"note":{"type":["string","null"]},"qty_milli":{"format":"int64","type":"integer"},"serial_id":{"description":"Serialized shipment moves ONE unit.","format":"uuid","type":["string","null"]},"ticket_id":{"description":"The on-site job the shipment belongs to.","format":"uuid","type":["string","null"]},"tracking_ref":{"type":["string","null"]}},"required":["item_id","location_id","qty_milli"],"type":"object"}]},"SignOff":{"description":"The evidence row. Immutable by design: there is no UPDATE path, so a\ncorrection is a second row linked by `supersedes_sign_off_id`.","properties":{"attachment_id":{"description":"The PNG payload in the content-addressed store (`kind = sign_off`).\n`None` exactly when the signer agreed by typing their name instead of\ndrawing, which is the first-class alternative path rather than a\ndegenerate capture.","format":"uuid","type":["string","null"]},"captured_at":{"description":"Device-clock reading when the stroke finished, recorded verbatim —\noffline capture is the normal case, so this is evidence about the\ndevice, not a trusted server timestamp.","format":"date-time","type":"string"},"consent_text_snapshot":{"description":"Verbatim text the signer agreed to. Snapshot, not a live join, so a\nlater consent revision can never change what an earlier signer agreed.","type":"string"},"consent_version":{"description":"Consent wording version the signer saw.","format":"int32","type":"integer"},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"idempotency_key":{"description":"Client-minted, server-enforced unique per org (offline replay lands once).","type":"string"},"org_id":{"format":"uuid","type":"string"},"signer_kind":{"$ref":"#/components/schemas/SignOffSignerKind"},"signer_name":{"description":"Who signed, as they typed it. A drawn mark identifies nobody on its own.","type":"string"},"supersedes_sign_off_id":{"description":"Set only on a correction row; points backward at the original.","format":"uuid","type":["string","null"]},"ticket_id":{"description":"The ticket being signed off. A sign-off always has one subject.","format":"uuid","type":"string"},"typed_agreement":{"description":"True when the signer agreed by typing their name. An auditor needs to\nknow which captures carried ink, because the two are not equally\nstrong evidence and only one can be compared against a sample.","type":"boolean"},"witnessed_by_user_id":{"description":"The staff member who presented the pad and watched the signature.","format":"uuid","type":["string","null"]}},"required":["id","org_id","ticket_id","typed_agreement","signer_name","signer_kind","captured_at","consent_version","consent_text_snapshot","idempotency_key","created_at"],"type":"object"},"SignOffConsent":{"description":"Append-only consent wording per org. Revision N+1 never rewrites revision N.","properties":{"body":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"version":{"format":"int32","type":"integer"}},"required":["id","org_id","version","body","created_at"],"type":"object"},"SignOffRevocation":{"description":"One entry in a sign-off's revocation trail. The row and its bytes survive;\n\"is revoked\" is answered by the existence of one of these.","properties":{"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"reason":{"type":"string"},"revoked_by_user_id":{"format":"uuid","type":["string","null"]},"seq":{"description":"1-based per sign-off; a second revocation of the same sign-off is a\ndata error, not an overwrite.","format":"int32","type":"integer"},"sign_off_id":{"format":"uuid","type":"string"}},"required":["id","org_id","sign_off_id","seq","reason","created_at"],"type":"object"},"SignOffSignerKind":{"description":"Who drew the mark. `PortalClient` is RESERVED: the API rejects it today so\na future portal-signing lane needs no schema change, mirroring the reserved\n`Attachment::uploaded_by_portal_user_id` column.","enum":["customer","portal_client"],"type":"string"},"SignOffView":{"allOf":[{"$ref":"#/components/schemas/SignOff"},{"properties":{"attachment_content_type":{"type":["string","null"]},"attachment_filename":{"description":"Payload facts (filename/content type/size/sha256), never the bytes.\nAll `None` on a typed agreement, which has no mark to describe.","type":["string","null"]},"attachment_sha256":{"type":["string","null"]},"attachment_size_bytes":{"format":"int64","type":["integer","null"]},"revocation":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SignOffRevocation","description":"The most recent revocation, when the sign-off has been revoked."}]},"superseded_by":{"description":"The id of a correction that replaced this one, when one exists.","format":"uuid","type":["string","null"]}},"type":"object"}],"description":"A sign-off plus the facts a client renders on the timeline: the payload's\nstorage facts, the current revocation state, and the correction relation."},"SignoffRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"contract_id":{"format":"uuid","type":"string"},"entries_dispositioned":{"type":"boolean"},"from":{"format":"date","type":"string"},"preview_seen":{"type":"boolean"},"reviewer_note":{"type":["string","null"]},"to":{"format":"date","type":"string"},"zero_unclassified":{"type":"boolean"}},"required":["contract_id","from","to","entries_dispositioned","zero_unclassified","preview_seen"],"type":"object"}],"description":"The close sign-off body."},"SignoffResponse":{"description":"The sign-off response.","properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"overlapping_runs":{"description":"Issued runs overlapping this window — a WARNING, not a block:\nre-running an overlapping window is a real operator need, but\nit must never be silent.","items":{"$ref":"#/components/schemas/RunWindowView"},"type":"array"},"signoff":{"$ref":"#/components/schemas/SignoffView"}},"required":["signoff","overlapping_runs","mutation"],"type":"object"},"SignoffView":{"description":"The close checklist as recorded.","properties":{"entries_dispositioned":{"type":"boolean"},"preview_seen":{"type":"boolean"},"reviewer_id":{"format":"uuid","type":["string","null"]},"reviewer_note":{"type":["string","null"]},"run_id":{"description":"The run that claimed this sign-off, when one has.","format":"uuid","type":["string","null"]},"signed_at":{"type":"string"},"zero_unclassified":{"type":"boolean"}},"required":["entries_dispositioned","zero_unclassified","preview_seen","signed_at"],"type":"object"},"SimilarTicketCandidate":{"properties":{"mode":{"description":"`shadow` rows are recorded but drive nothing (no rerank applied).","type":["string","null"]},"relates":{"description":"The `relates` Noul probability, when the decision edge answered.\n`None` = the edge went quiet (unconfigured/downed) for that\ncandidate.","format":"double","type":["number","null"]},"retrieval_score":{"description":"Blended lexical relevance from the Postgres index (higher is\nbetter) — the retrieval rank that fed the reranker.","format":"double","type":"number"},"snippet":{"description":"Raw retrieval excerpt (client-side snippet rendering).","type":"string"},"state":{"description":"Threshold classification of the live answer: \"act\" | \"pending\" |\n\"ignore\". `None` when no answer, shadow mode, or no thresholds.","type":["string","null"]},"ticket_id":{"format":"uuid","type":"string"},"title":{"type":"string"}},"required":["ticket_id","title","snippet","retrieval_score"],"type":"object"},"SimilarTicketsQuery":{"properties":{"q":{"description":"Override the retrieval text (defaults to the ticket's own title\nplus a body excerpt). Clamped like the search surface.","type":["string","null"]}},"type":"object"},"SimilarTicketsResponse":{"properties":{"candidates":{"items":{"$ref":"#/components/schemas/SimilarTicketCandidate"},"type":"array"},"query":{"description":"The retrieval query executed (the ticket's own title/body text).","type":"string"},"ticket_id":{"format":"uuid","type":"string"}},"required":["ticket_id","query","candidates"],"type":"object"},"Site":{"properties":{"address":{"type":["string","null"]},"city":{"type":["string","null"]},"client_id":{"format":"uuid","type":"string"},"country":{"type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"latitude":{"description":"lat/lon in decimal degrees for routing/travel estimates.","format":"double","type":["number","null"]},"longitude":{"format":"double","type":["number","null"]},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"postal_code":{"type":["string","null"]},"region":{"type":["string","null"]},"timezone":{"type":["string","null"]}},"required":["id","org_id","client_id","name","created_at"],"type":"object"},"SkillCatalogEntry":{"description":"GET /api/dispatch/skills envelope entry: the org skill catalog with\nholder counts (`active` = non-expired as of now).","properties":{"active_holders":{"format":"int64","type":"integer"},"holders":{"format":"int64","type":"integer"},"skill":{"type":"string"}},"required":["skill","holders","active_holders"],"type":"object"},"SkillInput":{"description":"One entry of a skill replacement set.","properties":{"expires_at":{"description":"Certification expiry; must be in the future when present.","format":"date-time","type":["string","null"]},"skill":{"description":"Org skill vocabulary, 1..=80 chars after trimming.","type":"string"}},"required":["skill"],"type":"object"},"SkillListQuery":{"properties":{"slug":{"description":"When present, every version of this slug (newest first) instead of\nthe latest version of each slug.","type":["string","null"]}},"type":"object"},"SkillManifest":{"description":"The declarative skill manifest (docs/10-agent-platform.md §4:\n`AgentSkill` — version, capabilities, prompts/rules). Content is\nUNTRUSTED RAG-class data: consumption-time layering quotes and scopes\nit into rules layers; nothing here is ever executed as instructions.","properties":{"capabilities":{"description":"Agent capability scopes the skill REQUESTS (data for the consent\nsurface; enforcement stays in the agent platform's rule layers).","items":{"type":"string"},"type":"array"},"description":{"type":"string"},"name":{"type":"string"},"prompts":{"description":"Prompt snippets — quoted verbatim at consumption time.","items":{"type":"string"},"type":"array"},"rules":{"description":"Rules snippets — quoted verbatim at consumption time, deny wins.","items":{"type":"string"},"type":"array"},"version":{"type":"string"}},"type":"object"},"SkillVersionView":{"properties":{"artifact_digest":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"enabled":{"type":"boolean"},"fetched_ref":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"manifest":{},"slug":{"type":"string"},"source_kind":{"type":"string"},"source_url":{"type":"string"},"version":{"format":"int32","type":"integer"}},"required":["id","slug","version","manifest","source_kind","source_url","artifact_digest","enabled","created_at"],"type":"object"},"SkillView":{"description":"One skill on one technician.","properties":{"expires_at":{"description":"Certification expiry; absent = does not expire.","format":"date-time","type":["string","null"]},"skill":{"type":"string"}},"required":["skill"],"type":"object"},"SkillsCatalogView":{"properties":{"skills":{"items":{"$ref":"#/components/schemas/SkillCatalogEntry"},"type":"array"}},"required":["skills"],"type":"object"},"SlaCalendar":{"description":"An org-level business-hours calendar row (`sla_calendars`). The math\ntype is [`BusinessCalendar`]; this carries its identity.","properties":{"calendar":{"description":"Flattened calendar body (timezone/windows/holidays/maintenance);\n`calendar.name` mirrors `name` on write.","type":"object"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"}},"required":["id","org_id","name","calendar"],"type":"object"},"SlaVerdictRequest":{"properties":{"body":{"type":["string","null"]},"notify_user_ids":{"description":"Extra recipients from the ladder step; the assignee is added\nserver-side.","items":{"format":"uuid","type":"string"},"type":"array"},"org_id":{"format":"uuid","type":"string"},"step_id":{"description":"Ladder step that fired (ledger key).","format":"uuid","type":"string"},"target":{"description":"'first_response' | 'resolution'","type":"string"},"ticket_id":{"format":"uuid","type":"string"},"title":{"description":"Human-facing headline, e.g. \"[MAR-12] SLA risk on resolution\".","type":"string"},"trigger":{"description":"'risk' | 'breach' | 'escalate'","type":"string"}},"required":["org_id","ticket_id","target","step_id","trigger","notify_user_ids","title"],"type":"object"},"SlotInterval":{"properties":{"ends_at":{"format":"date-time","type":"string"},"starts_at":{"format":"date-time","type":"string"}},"required":["starts_at","ends_at"],"type":"object"},"SpecFilter":{"description":"One spec filter: catalog column + operator + typed operand.","properties":{"column":{"description":"Column name from the view's FILTERABLE inventory (the narrower v1\nallowlist — filtered-but-unselected columns surprise consumers).","type":"string"},"op":{"$ref":"#/components/schemas/FilterOp"},"value":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FilterValue","description":"Required exactly when the operator takes a value."}]}},"required":["column","op"],"type":"object"},"SsoExchangeRequest":{"properties":{"code":{"format":"uuid","type":"string"}},"required":["code"],"type":"object"},"SsoExchangeResponse":{"oneOf":[{"$ref":"#/components/schemas/TokenPair"},{"description":"Org MFA policy applies to federated logins (Wave 13 lane 7):\ncomplete via POST /api/auth/mfa/verify with the bridge token.","properties":{"methods":{"items":{"type":"string"},"type":"array"},"mfa_required":{"type":"boolean"},"mfa_token":{"type":"string"}},"required":["mfa_required","mfa_token","methods"],"type":"object"},{"properties":{"linked":{"type":"boolean"}},"required":["linked"],"type":"object"}]},"StageChunkRequest":{"allOf":[{"$ref":"#/components/schemas/RowsPayload"},{"properties":{"template_id":{"description":"Optional saved field-mapping template applied at STAGE time, so the\nstored chunk is already in canonical columns.","format":"uuid","type":["string","null"]}},"type":"object"}],"description":"POST /imports/batches/{id}/stage request: uploads ONE chunk of rows to\nthe ObjectStore under a deterministic key without executing anything."},"StageChunkResponse":{"properties":{"batch_id":{"format":"uuid","type":"string"},"chunk_index":{"description":"Index of the chunk just staged (its deterministic key suffix).","format":"int32","type":"integer"},"staged_chunks":{"description":"Total staged chunks after this upload — the worker's runbook length.","format":"int32","type":"integer"}},"required":["batch_id","chunk_index","staged_chunks"],"type":"object"},"StageFromGithubRequest":{"properties":{"artifact_url":{"description":"Optional release-asset URL for B2 cross-digest verification of\nthe actual artifact bytes (same posture as the registry source).","type":["string","null"]},"commit":{"description":"Pinned 40-hex commit the tag resolves to.","type":"string"},"owner":{"type":"string"},"repo":{"type":"string"},"tag":{"description":"Pinned git tag — HEAD is refused, never accepted.","type":"string"}},"required":["owner","repo","tag","commit"],"type":"object"},"StageFromRegistryRequest":{"properties":{"artifact_url":{"description":"Optional direct artifact URL (marketplace CDN / release asset).\nWhen present the server RE-FETCHES the bytes and verifies their\nsha256 against the catalog-pinned digest BEFORE the staged row\nexists (B2 cross-digest verification; typed\n`distribution_digest_invalid` on mismatch).","type":["string","null"]},"url":{"type":"string"}},"required":["url"],"type":"object"},"StageListQuery":{"properties":{"include_inactive":{"description":"`None` = active stages only (default); `true` includes inactive.","type":["boolean","null"]}},"type":"object"},"StageMutationResponse":{"allOf":[{"$ref":"#/components/schemas/PipelineStage"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation responses (IF2)."},"StageSkillRequest":{"properties":{"artifact_digest":{"description":"The claimed sha256 for community pulls — reverified against the\nre-fetched bytes before the row exists.","type":["string","null"]},"fetched_ref":{"description":"The pinned reference (a version tag / commit for community pulls).","type":["string","null"]},"manifest":{"description":"The inline manifest for `manual_upload` (community origins are\nre-fetched server-side instead)."},"source_kind":{"description":"`skill_community` | `manual_upload`.","type":"string"},"source_url":{"type":"string"}},"required":["source_kind","source_url"],"type":"object"},"StageType":{"description":"The kind of a pipeline stage. `won` / `lost` are terminal outcomes;\ndeals on such a stage are closed.","enum":["open","won","lost"],"type":"string"},"StagedItemView":{"properties":{"adopted_install_id":{"format":"uuid","type":["string","null"]},"adopted_skill_id":{"format":"uuid","type":["string","null"]},"adopted_theme_pack_id":{"format":"uuid","type":["string","null"]},"artifact_digest":{"type":"string"},"artifact_kind":{"type":"string"},"declared_capabilities":{"items":{"type":"string"},"type":"array"},"fetched_ref":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"manifest":{},"manifest_authenticated":{"description":"Whether a signature the operator's trust set verified covers the\nmanifest on this row, not just the artifact bytes. `false` is the\nweaker claim: a legacy digest-only registry entry, a zip, a skill, a\nGitHub pull, or any ingest run with no trusted key configured.","type":"boolean"},"module_digest":{"description":"The digest of the executable module this import carries, when it carries\none. This is the value the install will pin and the executor will load;\nthe consent screen shows it so an operator approves bytes, not a name.","type":"string"},"review_note":{"type":["string","null"]},"source_kind":{"type":"string"},"source_url":{"type":"string"},"status":{"type":"string"},"uploaded_by":{"format":"uuid","type":"string"}},"required":["id","artifact_kind","source_kind","source_url","artifact_digest","module_digest","manifest_authenticated","manifest","declared_capabilities","status","uploaded_by"],"type":"object"},"StartPointView":{"properties":{"kind":{"type":"string"},"label":{"type":["string","null"]},"latitude":{"format":"double","type":["number","null"]},"longitude":{"format":"double","type":["number","null"]},"site_id":{"format":"uuid","type":["string","null"]},"source":{"description":"`explicit` | `previous_day_site`.","type":"string"}},"required":["kind","source"],"type":"object"},"StartTimerRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key. Absent in\nlegacy clients — purely additive. (Creates carry no version.)"},{"properties":{"ticket_id":{"format":"uuid","type":"string"}},"required":["ticket_id"],"type":"object"}]},"StatementLine":{"description":"One open invoice on the statement.","properties":{"due_on":{"type":"string"},"number":{"type":"string"},"outstanding_minor":{"format":"int64","type":"integer"},"total_minor":{"format":"int64","type":"integer"}},"required":["number","due_on","total_minor","outstanding_minor"],"type":"object"},"StockItemListQuery":{"properties":{"active":{"description":"`None` = all items (active + archived).","type":["boolean","null"]},"limit":{"format":"int64","type":["integer","null"]},"search":{"type":["string","null"]}},"type":"object"},"StockItemMutationResponse":{"allOf":[{"$ref":"#/components/schemas/StockItemView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2)."},"StockItemView":{"description":"The stock item as the CALLER may see it. `last_unit_cost_minor` is\nfinancial data — `null` without `financial:read` (RB2, ADR 0004).","properties":{"active":{"type":"boolean"},"catalog_item_id":{"format":"uuid","type":["string","null"]},"created_at":{"type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"currency":{"type":"string"},"id":{"format":"uuid","type":"string"},"is_tool":{"type":"boolean"},"kind":{"type":"string"},"last_unit_cost_minor":{"description":"`null` without `financial:read` (RB2).","format":"int64","type":["integer","null"]},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"part_number":{"type":["string","null"]},"sku":{"type":["string","null"]},"unit":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","org_id","name","kind","is_tool","unit","currency","active","created_at","updated_at"],"type":"object"},"StockLevelView":{"description":"POST thresholds response: the fresh threshold row.","properties":{"max_qty_milli":{"format":"int64","type":["integer","null"]},"min_qty_milli":{"format":"int64","type":["integer","null"]},"qty_milli":{"format":"int64","type":"integer"},"reorder_point_milli":{"format":"int64","type":["integer","null"]},"stock_item_id":{"format":"uuid","type":"string"},"stock_location_id":{"format":"uuid","type":"string"},"updated_at":{"type":"string"}},"required":["stock_item_id","stock_location_id","qty_milli","updated_at"],"type":"object"},"StockLocationMutationResponse":{"allOf":[{"$ref":"#/components/schemas/StockLocationView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}]},"StockLocationView":{"description":"Location view.","properties":{"active":{"type":"boolean"},"assigned_user_id":{"format":"uuid","type":["string","null"]},"code":{"type":["string","null"]},"created_at":{"type":"string"},"id":{"format":"uuid","type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"parent_id":{"format":"uuid","type":["string","null"]},"updated_at":{"type":"string"}},"required":["id","org_id","name","kind","active","created_at","updated_at"],"type":"object"},"StockMovementView":{"description":"Movement view (the traceability row the reads render).","properties":{"base_cost_minor":{"format":"int64","type":["integer","null"]},"carrier":{"type":["string","null"]},"client_id":{"format":"uuid","type":["string","null"]},"cost_minor":{"description":"`null` without `financial:read` (RB2).","format":"int64","type":["integer","null"]},"created_at":{"type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"currency":{"type":["string","null"]},"from_location_id":{"format":"uuid","type":["string","null"]},"fx_rate_date":{"type":["string","null"]},"fx_rate_used":{"description":"ADR 0002 receipt FX lock fields (`null` for in-base movements).","type":["string","null"]},"fx_source":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"kind":{"type":"string"},"note":{"type":["string","null"]},"qty_milli":{"format":"int64","type":"integer"},"serial_id":{"format":"uuid","type":["string","null"]},"stock_item_id":{"format":"uuid","type":"string"},"ticket_id":{"format":"uuid","type":["string","null"]},"to_location_id":{"format":"uuid","type":["string","null"]},"tracking_ref":{"type":["string","null"]}},"required":["id","stock_item_id","kind","qty_milli","created_at"],"type":"object"},"StockSerialView":{"properties":{"client_id":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"location_id":{"format":"uuid","type":["string","null"]},"serial_number":{"type":"string"},"status":{"type":"string"},"stock_item_id":{"format":"uuid","type":"string"},"ticket_id":{"format":"uuid","type":["string","null"]},"updated_at":{"type":"string"}},"required":["id","stock_item_id","serial_number","status","updated_at"],"type":"object"},"StopTimerRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + entity\nversion. Absent in legacy clients — purely additive. A retried stop\nreplays the original time entry instead of failing \"no active timer\"."},{"properties":{"billable":{"type":["boolean","null"]},"notes":{"type":["string","null"]},"reason":{"type":["string","null"]},"session_id":{"description":"Omit to stop the caller's active timer.","format":"uuid","type":["string","null"]},"unbillable_reason":{"description":"Typed unbillable reason (Wave 39 V3) — required by validation\nwhen `billable` is false.","type":["string","null"]},"work_type":{"description":"Category slug (Wave 39 V5): keys onto the org's time_categories\ntaxonomy; additive — legacy clients omit it and the entry stays\nhonestly unclassified.","type":["string","null"]}},"type":"object"}]},"SubmitEntryRequest":{"properties":{"note":{"description":"Optional free-text context recorded on the decision trail.","type":["string","null"]}},"type":"object"},"SubmitResponse":{"allOf":[{"$ref":"#/components/schemas/TimeEntry"}],"description":"Instant-first mutation response (IF2): the full entry snapshot\n(including its new approval state) plus the decision trail entry."},"SubmitTravelDraftRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"}],"description":"POST /api/dispatch/travel-drafts/{id}/submit body."},"SuggestionAnswer":{"properties":{"act":{"format":"double","type":["number","null"]},"answer":{"description":"Raw answer entry: kind + picked/level/probability + the FULL\nnormalized distribution + confidence (no prose — none exists)."},"question_id":{"type":"string"},"review":{"format":"double","type":["number","null"]},"state":{"description":"\"act\" | \"pending\" | \"ignore\" — the threshold gate at read time.\n`None` when no thresholds resolve (honest unconfigured state).","type":["string","null"]}},"required":["question_id","answer"],"type":"object"},"SuggestionSetView":{"properties":{"available":{"description":"False in NONE mode — suggestion-grade features require a measured\nprovider (ADR 0010 visibly-absent posture).","type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/TravelSuggestionView"},"type":"array"},"reason":{"type":["string","null"]}},"required":["available","items"],"type":"object"},"SuggestionSurface":{"properties":{"answers":{"items":{"$ref":"#/components/schemas/SuggestionAnswer"},"type":"array"},"mode":{"description":"`shadow` rows drive nothing and are labeled as such.","type":"string"},"model":{"type":"string"},"provider":{"type":"string"},"recorded_at":{"format":"date-time","type":"string"},"surface":{"type":"string"}},"required":["surface","mode","provider","model","recorded_at","answers"],"type":"object"},"SurfaceAction":{"description":"One declared automation action kind.","properties":{"agent_description":{"description":"A2: model-facing prose beside the human label. What makes LLM\nauthoring cheap enough to be the default.","type":["string","null"]},"audience":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/NodeAudience","description":"A2: who sees this entry. Undeclared stays `human`, so a plugin that\ndeclares nothing never joins the LLM tool catalogue."}]},"description":{"description":"One-line description shown in the editor and on consent.","type":"string"},"effect":{"$ref":"#/components/schemas/SurfaceActionEffect","description":"The consented effect the engine executes."},"emits":{"description":"A2: the event types this action can produce. Load-bearing: the\nstatic loop refusal reads them, and a plugin that emits\n`ticket.status_changed` then waits for it would otherwise deadlock\na workflow. Validated against the event-type registry at grant\ntime.","items":{"type":"string"},"type":"array"},"id":{"description":"Plugin-local id, `[a-z][a-z0-9_]{0,63}`. Registry key:\n`plugin.<install-slug>.<id>`.","type":"string"},"idempotent":{"description":"A2: repeating-safety. Defaults to false, because an undeclared\nplugin action's retry-safety is unknown — `flow.retry` and the\noperator retry both refuse a false until a plugin says otherwise.","type":"boolean"},"label":{"description":"Label shown in the automations editor.","type":"string"},"output_schema":{"description":"A2 (Wave 43 lane 13, `p43-node-registry`): what the action\nRETURNS. Before this field a workflow could call a plugin action\nbut could not use what came back, which made a plugin node a\ndead end for every downstream condition and reference."},"params_schema":{"description":"Optional JSON-Schema subset for the action's params."}},"required":["id","label","description","effect"],"type":"object"},"SurfaceActionEffect":{"description":"The built-in EFFECT a declared automation action maps onto. Closed\nvocabulary: the rule engine executes the effect with the action's\nvalidated params — the plugin contributes the kind, never custom\nexecution.","enum":["add_internal_comment"],"type":"string"},"SurfaceEntryList":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/SurfaceEntryView"},"type":"array"}},"required":["entries"],"type":"object"},"SurfaceEntryView":{"description":"One registered entry as the web consumes it.","properties":{"definition":{},"entry_key":{"type":"string"},"install_id":{"format":"uuid","type":"string"},"label":{"type":"string"}},"required":["install_id","entry_key","label","definition"],"type":"object"},"SurfaceFieldValidator":{"description":"One declared custom-field validator.","properties":{"id":{"description":"Plugin-local id, `[a-z][a-z0-9_]{0,63}`. Registry key:\n`plugin.<install-slug>.<id>`.","type":"string"},"label":{"description":"Label shown in the board field editor.","type":"string"},"schema":{"description":"The JSON-Schema subset applied to the field's value on write."}},"required":["id","label","schema"],"type":"object"},"SurfaceRead":{"description":"One declared READ node (Wave 43 lane 4, ADR 0017 §5).\n\n`automation_action` is write-shaped, so before this kind a workflow\ncould not FETCH from an external system at all — which is exactly the\ngap that stopped a workflow assembling Decision Engine context from an\nRMM. A read is therefore the one kind whose guardrails are STRICTER\nthan a write's, and they are declared per entry rather than defaulted:\na loop over devices is a denial of service aimed at the customer's own\nexternal system, and a device list of ten thousand must not grow the\nrun ledger by megabytes.","properties":{"agent_description":{"description":"A2: model-facing prose beside the human label.","type":["string","null"]},"audience":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/NodeAudience","description":"A2: who sees this entry. Undeclared stays `human`."}]},"description":{"description":"One-line description shown in the editor and on consent.","type":"string"},"emits":{"description":"A2: the event types this read can produce. A read produces none of\nits own, so this is normally empty; declaring one makes the loop\nrefusal able to see it.","items":{"type":"string"},"type":"array"},"guardrails":{"$ref":"#/components/schemas/ReadGuardrails","description":"The caps this read runs under. Required — a read without declared\nguardrails refuses, so there is no silent unbounded default."},"id":{"description":"Plugin-local id, `[a-z][a-z0-9_]{0,63}`. Registry key:\n`plugin.<install-slug>.<id>`.","type":"string"},"idempotent":{"description":"A2 (Wave 43 lane 13): repeating-safety. False by default — a read\nis rate-capped precisely because a loop over devices is cheap to\nwrite, and retrying one is the same loop.","type":"boolean"},"label":{"description":"Label shown in the automations editor's read picker.","type":"string"},"output_schema":{"description":"Optional JSON-Schema subset describing what comes back, so a\ndownstream node can use it."},"params_schema":{"description":"Optional JSON-Schema subset for the read's parameters (what the\ncaller supplies — never credentials, which the host resolves)."}},"required":["id","label","description","guardrails"],"type":"object"},"SurfaceRenderKind":{"description":"One declared report render kind.","properties":{"description":{"description":"One-line description.","type":"string"},"id":{"description":"Plugin-local id, `[a-z][a-z0-9_]{0,63}`. Registry key:\n`plugin.<install-slug>.<id>`.","type":"string"},"label":{"description":"Label shown in the report builder.","type":"string"}},"required":["id","label","description"],"type":"object"},"SurfaceSlot":{"description":"Where a declared widget may render. Closed vocabulary.","enum":["dashboard","ticket_pane"],"type":"string"},"SurfaceTrigger":{"description":"One declared TRIGGER source (Wave 43 lane 4, ADR 0017 §3): the event\ntype a plugin emits, which a rule can then fire on.\n\nThe trigger is a DECLARATION, not a dispatcher. It names the type it\nemits and the schema the emitted payload satisfies; the engine's\nmatching decides what fires. `emitted_event_type` is validated against\nthe event-type registry (migration 0124) at grant time, so a plugin\ncannot declare a type the platform does not know and the org has not\nadmitted.","properties":{"description":{"description":"One-line description shown in the editor and on consent.","type":"string"},"emitted_event_type":{"description":"The event type this source publishes (`segment.segment`, the same\ngrammar the platform's own types use, e.g. `rmm.device_offline`).\nChecked against the event-type registry at grant time.","type":"string"},"id":{"description":"Plugin-local id, `[a-z][a-z0-9_]{0,63}`. Registry key:\n`plugin.<install-slug>.<id>`.","type":"string"},"label":{"description":"Label shown in the automations editor's trigger picker.","type":"string"},"payload_schema":{"description":"Optional JSON-Schema subset the emitted envelope's payload\nsatisfies, so a rule author knows what they can read."}},"required":["id","label","description","emitted_event_type"],"type":"object"},"SurfaceWidget":{"description":"One declared widget.","properties":{"default_props":{"description":"Optional defaults applied under props_schema before render."},"id":{"description":"Plugin-local id, `[a-z][a-z0-9_]{0,63}`. The registry key is\n`plugin.<install-slug>.<id>` — collision-free by construction.","type":"string"},"kind":{"$ref":"#/components/schemas/SurfaceWidgetKind","description":"The trusted render kind."},"props_schema":{"description":"Optional JSON-Schema subset the widget's per-board props must\nsatisfy (same subset as settings_schema)."},"slot":{"$ref":"#/components/schemas/SurfaceSlot","description":"Where the widget renders."},"title":{"description":"Human label shown on the widget chrome and the consent screen.","type":"string"}},"required":["id","title","slot","kind"],"type":"object"},"SurfaceWidgetKind":{"description":"What a declared widget RENDERS AS. Closed vocabulary — each kind maps\nonto one trusted core component; a plugin cannot introduce a new\nrender shape.","enum":["stat","metric","list"],"type":"string"},"SuspensionStatusView":{"description":"The notice state plus the two links a surface renders beside it.","properties":{"data_export_url":{"description":"Where to pull the org's data out (the existing export surface).","type":"string"},"docs_url":{"description":"The guide that explains that export.","type":"string"},"notify_after":{"description":"Whole seconds after `suspended_at` before the notice starts showing.\n`null` = the notice is due immediately.","format":"int64","type":["integer","null"]},"org_id":{"format":"uuid","type":"string"},"reason":{"description":"`billing_lapsed` | `abuse` | `other`. `null` = not flagged.","type":["string","null"]},"suspended_at":{"description":"When the org was flagged. `null` = not flagged.","format":"date-time","type":["string","null"]}},"required":["org_id","data_export_url","docs_url"],"type":"object"},"SyncForceQuery":{"properties":{"force":{"description":"Force a poll even when the connection is disabled (admin repair).","type":"boolean"}},"type":"object"},"SyncOutcomeResponse":{"properties":{"connection_id":{"format":"uuid","type":"string"},"outcome":{"$ref":"#/components/schemas/SyncOutcomeWire","description":"The apply counters, wire-shaped (SyncOutcome is an internal struct)."}},"required":["connection_id","outcome"],"type":"object"},"SyncOutcomeWire":{"properties":{"duplicates":{"format":"int64","type":"integer"},"imported":{"format":"int64","type":"integer"},"overlaps":{"format":"int64","type":"integer"},"released":{"format":"int64","type":"integer"},"updated":{"format":"int64","type":"integer"}},"required":["imported","updated","released","overlaps","duplicates"],"type":"object"},"Tag":{"description":"An org-wide tag: a flat named label with an optional display color.","properties":{"color":{"type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","name","created_at","updated_at"],"type":"object"},"TagMutationResponse":{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"tag":{"$ref":"#/components/schemas/TagView"}},"required":["tag","mutation"],"type":"object"},"TagView":{"description":"One tag + how many tickets currently carry it.","properties":{"applied_count":{"format":"int64","type":"integer"},"tag":{"$ref":"#/components/schemas/Tag"}},"required":["tag","applied_count"],"type":"object"},"TargetStatusOption":{"description":"One target-board status option inside a `board_move_status_conflict`\n409 — everything the client needs to render the pick list.","properties":{"category":{"$ref":"#/components/schemas/TicketStatusCategory"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"}},"required":["id","name","category"],"type":"object"},"TaskGateItem":{"description":"One unassigned task on the close gate's typed 409 (§4.1): the row the\nUI must resolve (Option A batch-close / Option B manual work) — decision\ntext, and whether it is a REQUIRED template item (enforcement pill).","properties":{"item_id":{"format":"uuid","type":"string"},"required":{"type":"boolean"},"text":{"type":"string"}},"required":["item_id","text","required"],"type":"object"},"TaskListQuery":{"properties":{"assignee_id":{"format":"uuid","type":["string","null"]},"limit":{"format":"int64","type":"integer"},"milestone":{"type":["boolean","null"]},"offset":{"format":"int64","type":"integer"},"phase_id":{"format":"uuid","type":["string","null"]},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TaskStatus"}]}},"type":"object"},"TaskMutationResponse":{"allOf":[{"$ref":"#/components/schemas/ProjectTask"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}]},"TaskStatus":{"description":"Lifecycle of a task. `completed` is reached ONLY through the dedicated\ncomplete endpoint, which enforces dependency readiness and emits the\ncompletion (+ milestone billing seam) events.","enum":["pending","in_progress","blocked","completed","cancelled"],"type":"string"},"TaxonomyMutationResponse":{"allOf":[{"$ref":"#/components/schemas/TaxonomyNodeView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"ticket_references":{"description":"How many tickets still reference the node (informational —\nretirement does not require zero references).","format":"int64","type":"integer"}},"required":["ticket_references","mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): the node plus read-your-write\nmetadata."},"TaxonomyNodeView":{"description":"One taxonomy node (type or category). `parent_id` is null for types and\nroot categories; subcategories carry their parent.","properties":{"created_at":{"type":"string"},"description":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_retired":{"type":"boolean"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"parent_id":{"format":"uuid","type":["string","null"]},"sort_order":{"format":"int32","type":"integer"},"updated_at":{"type":"string"}},"required":["id","org_id","name","is_retired","sort_order","created_at","updated_at"],"type":"object"},"TeamRangeQuery":{"properties":{"from":{"description":"Inclusive window start (default: today's UTC midnight minus 1 day).","format":"date-time","type":["string","null"]},"to":{"description":"Exclusive window end (default: `from` + 8 days).","format":"date-time","type":["string","null"]}},"type":"object"},"TeamScorecardsResponse":{"description":"The team view: one scorecard per staff member, owner/admin only.","properties":{"scorecards":{"items":{"$ref":"#/components/schemas/Scorecard"},"type":"array"},"window":{"$ref":"#/components/schemas/ScorecardWindow"}},"required":["window","scorecards"],"type":"object"},"TechnicianCapacity":{"description":"One technician across the whole horizon.","properties":{"days":{"items":{"$ref":"#/components/schemas/DayCapacity"},"type":"array"},"display_name":{"type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","display_name","days"],"type":"object"},"TechnicianJourneyView":{"properties":{"display_name":{"type":"string"},"start_point":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/StartPointView"}]},"stops":{"items":{"$ref":"#/components/schemas/JourneyStopView"},"type":"array"},"timezone":{"type":"string"},"total_travel_minutes":{"description":"Sum of leg minutes actually estimated for this day.","format":"int64","type":"integer"},"user_id":{"format":"uuid","type":"string"},"warnings":{"items":{"$ref":"#/components/schemas/TravelWarningView"},"type":"array"}},"required":["user_id","display_name","timezone","total_travel_minutes","stops","warnings"],"type":"object"},"TechnicianView":{"description":"One technician column: capacity stats plus everything the board draws\nin the column (bookings, on-call, exclusions, availability windows).","properties":{"appointments":{"items":{"$ref":"#/components/schemas/DispatchAppointment"},"type":"array"},"availability_windows":{"items":{"$ref":"#/components/schemas/AvailabilityWindow"},"type":"array"},"available_minutes":{"description":"Working minutes covered by availability windows minus exclusions\nover the day window (`core_domain::scheduling::covered_minutes`).","format":"int64","type":"integer"},"booked_minutes":{"description":"Union of live booking minutes clipped to the day window.","format":"int64","type":"integer"},"display_name":{"type":"string"},"exclusions":{"items":{"$ref":"#/components/schemas/CalendarExclusion"},"type":"array"},"on_call":{"items":{"$ref":"#/components/schemas/OnCallBlock"},"type":"array"},"timezone":{"type":"string"},"user_id":{"format":"uuid","type":"string"},"utilization_percent":{"description":"0–100+, where >100 is over-capacity (the Ember warning state).","format":"int64","type":"integer"}},"required":["user_id","display_name","timezone","available_minutes","booked_minutes","utilization_percent","appointments","on_call","exclusions","availability_windows"],"type":"object"},"TemplateKindVocabulary":{"description":"The closed variable vocabulary for one kind (editor UX + validation\nsource of truth: `core_domain::templates::variables_for_kind`).","properties":{"kind":{"type":"string"},"variables":{"items":{"type":"string"},"type":"array"}},"required":["kind","variables"],"type":"object"},"TemplateListQuery":{"properties":{"active_only":{"description":"Defaults to true: the pickers only ever want live templates.","type":["boolean","null"]},"purpose":{"type":["string","null"]}},"type":"object"},"TemplateMutationResponse":{"allOf":[{"$ref":"#/components/schemas/ProjectTemplate"}]},"TemplateVariableView":{"properties":{"key":{"type":"string"},"label":{"type":"string"}},"required":["key","label"],"type":"object"},"TemplateView":{"description":"One template row: a shipped default (`is_system` + `builtin`), a fork\n(`forked_from`), or an org-created template.","properties":{"body":{"type":"string"},"builtin":{"description":"Shipped preset tag, `null` for org-created rows.","type":["string","null"]},"created_at":{"type":"string"},"description":{"type":"string"},"forked_from":{"description":"For a forked copy: the origin's builtin tag.","type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_system":{"description":"Shipped defaults are system data: immutable, restorable, never\neditable or deletable in place — orgs fork.","type":"boolean"},"kind":{"type":"string"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"slug":{"type":"string"},"subject":{"type":["string","null"]},"updated_at":{"type":"string"},"variables":{"items":{"$ref":"#/components/schemas/TemplateVariableView"},"type":"array"}},"required":["id","org_id","kind","slug","name","description","body","variables","is_system","created_at","updated_at"],"type":"object"},"TemplateViewResponse":{"properties":{"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":"string"},"created_by_kind":{"type":"string"},"entity_kind":{"$ref":"#/components/schemas/EntityKind"},"id":{"format":"uuid","type":"string"},"mapping":{"type":"object"},"name":{"type":"string"},"source_system":{"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","name","source_system","entity_kind","mapping","created_by","created_by_kind","created_at","updated_at"],"type":"object"},"TemplatesListResponse":{"properties":{"templates":{"items":{"$ref":"#/components/schemas/TemplateView"},"type":"array"},"vocabulary":{"description":"The closed per-kind variable vocabulary, for editors: kind → keys.","items":{"$ref":"#/components/schemas/TemplateKindVocabulary"},"type":"array"}},"required":["templates","vocabulary"],"type":"object"},"TestConnectionRequest":{"description":"Request to test the connection to an AI provider.","properties":{"provider":{"description":"Provider to test. If omitted, tests the currently active provider in settings.","type":["string","null"]},"test_prompt":{"description":"Optional prompt to test with.","type":["string","null"]}},"type":"object"},"TestConnectionResponse":{"description":"Result of testing an AI provider connection.","properties":{"latency_ms":{"format":"int64","minimum":0,"type":["integer","null"]},"message":{"description":"Descriptive status message or reason on failure.","type":"string"},"ok":{"type":"boolean"},"provider":{"type":"string"}},"required":["provider","ok","message"],"type":"object"},"TestFireResponse":{"properties":{"event_id":{"description":"The envelope id to look for in the delivery log.","format":"uuid","type":"string"}},"required":["event_id"],"type":"object"},"TestResult":{"properties":{"detail":{"type":["string","null"]},"kind":{"type":"string"},"outcome":{"description":"`delivered` | `refused` | `bad_config` | `unreachable`.","type":"string"}},"required":["kind","outcome"],"type":"object"},"TestSendRequest":{"properties":{"to":{"description":"Where the probe goes; absent sends to the caller's own address.","type":["string","null"]}},"type":"object"},"TestSendResponse":{"properties":{"message_id":{"format":"uuid","type":"string"},"outcome":{"description":"`queued` — the worker performs the actual send; the delivery ledger\ncarries the outcome (never a fake synchronous \"sent\" here).","type":"string"},"to":{"type":"string"},"transport":{"type":"string"}},"required":["message_id","to","transport","outcome"],"type":"object"},"ThemePackList":{"properties":{"packs":{"items":{"$ref":"#/components/schemas/ThemePackView"},"type":"array"}},"required":["packs"],"type":"object"},"ThemePackView":{"description":"One installed theme pack for the org. The `manifest` carries the FULL\nvalidated manifest snapshot (token overrides only); the ACTIVE pack\nis identified through the typed `appearance.theme` setting value\n`pack:<slug>`, not this table.","properties":{"active":{"description":"Present when this pack is the org's active theme (i.e. the\ntyped `appearance.theme` setting names `pack:<slug>`).","type":"boolean"},"artifact_digest":{"type":["string","null"]},"description":{"type":"string"},"fetched_ref":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"manifest":{},"name":{"type":"string"},"schema_version":{"format":"int32","type":"integer"},"slug":{"type":"string"},"source_kind":{"type":"string"},"source_url":{"type":"string"},"version":{"type":"string"}},"required":["id","slug","name","version","description","schema_version","manifest","source_kind","source_url","active"],"type":"object"},"ThresholdsRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"item_id":{"format":"uuid","type":"string"},"location_id":{"format":"uuid","type":"string"},"max_qty_milli":{"format":"int64","type":["integer","null"]},"min_qty_milli":{"format":"int64","type":["integer","null"]},"reorder_point_milli":{"format":"int64","type":["integer","null"]}},"required":["item_id","location_id"],"type":"object"}]},"TickMutationResponse":{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"tick":{"$ref":"#/components/schemas/DayPackTickView"}},"required":["tick","mutation"],"type":"object"},"Ticket":{"properties":{"assignee_id":{"format":"uuid","type":["string","null"]},"board_id":{"format":"uuid","type":"string"},"body":{"type":["string","null"]},"category":{"$ref":"#/components/schemas/TicketStatusCategory","description":"Denormalized from the board status for cheap filtering/sorting."},"category_id":{"format":"uuid","type":["string","null"]},"client_id":{"format":"uuid","type":["string","null"]},"contact_id":{"format":"uuid","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"due_at":{"format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"merged_into_ticket_id":{"description":"Merge marker (Wave 11 lane 9): the survivor this ticket folded\ninto, when it was absorbed by a merge. The row stays queryable\n(doctrine #9) but is closed and never re-opened.","format":"uuid","type":["string","null"]},"number":{"$ref":"#/components/schemas/TicketNumber"},"org_id":{"format":"uuid","type":"string"},"priority":{"$ref":"#/components/schemas/Priority"},"requester_id":{"format":"uuid","type":["string","null"]},"resolved_at":{"format":"date-time","type":["string","null"]},"site_id":{"format":"uuid","type":["string","null"]},"status_id":{"format":"uuid","type":"string"},"title":{"type":"string"},"type_id":{"description":"Classification (Wave 11 lane 3; read-view exposure Wave 12 lane 6):\nthe org taxonomy nodes carried on the ticket. PATCH accepts them;\nthe read view now returns them so ticket detail can display the\ncurrent classification. Purely descriptive — classification-only\ndoctrine (§5) binds behavior to semantic categories, never these.","format":"uuid","type":["string","null"]},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","number","board_id","status_id","title","priority","category","created_at","updated_at"],"type":"object"},"TicketBoardMoveResponse":{"allOf":[{"$ref":"#/components/schemas/Ticket"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Same additive envelope as the status endpoint (plain ticket body)."},"TicketChecklistBatchResponse":{"allOf":[{"$ref":"#/components/schemas/TicketChecklistView"},{"properties":{"appended_notes":{"items":{"$ref":"#/components/schemas/TicketComment"},"type":"array"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["appended_notes","mutation"],"type":"object"}],"description":"Batch response: full updated checklist view + the appended notes in\nitem order so the client reconciles in one round trip."},"TicketChecklistMutationResponse":{"allOf":[{"$ref":"#/components/schemas/TicketChecklistView"},{"properties":{"appended_note":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TicketComment"}]},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Same additive envelope for checklist writes: the full checklist view at\nthe top level (unchanged shape) plus the read-your-write metadata for\nthe affected item. `appended_note` carries the server-composed ledger\nnote when the transition wrote one (§3.3/§3.4) — absent otherwise."},"TicketChecklistView":{"description":"GET response for a ticket's checklist: instances, derived progress,\nand the board's live enforcement posture. `blocking` lists the unchecked\nREQUIRED items when enforcement is active (empty otherwise).","properties":{"blocking":{"items":{"type":"string"},"type":"array"},"enforcement_active":{"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/ChecklistItem"},"type":"array"},"progress":{"$ref":"#/components/schemas/ChecklistProgress"}},"required":["items","progress","enforcement_active","blocking"],"type":"object"},"TicketComment":{"description":"A structured ticket note. Taxonomy: internal / client-visible /\nin-house update. `kind` gates portal visibility and email fan-out.","properties":{"author_id":{"format":"uuid","type":["string","null"]},"body":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"kind":{"$ref":"#/components/schemas/NoteKind"},"org_id":{"format":"uuid","type":"string"},"ticket_id":{"format":"uuid","type":"string"},"updated_at":{"format":"date-time","type":["string","null"]},"workflow_meta":{"description":"Server-composed workflow-note provenance (Wave 17 lane 11,\nmigration 0080): `{kind, auto, item_ids}` on notes the server\nappended for task-state transitions; NULL/unsent for every\nuser-written note. Machine marking lives HERE, never in body text\n(spoofable, unqueryable). Additive-only contract: absent keys\ndeserialize to None."}},"required":["id","org_id","ticket_id","kind","body","created_at"],"type":"object"},"TicketCommentMutationResponse":{"allOf":[{"$ref":"#/components/schemas/TicketComment"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Same additive envelope for comments (plain comment body)."},"TicketCostLine":{"properties":{"base_amount_minor":{"format":"int64","type":["integer","null"]},"billable":{"type":"boolean"},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"currency":{"type":"string"},"description":{"type":"string"},"fx_rate_date":{"type":["string","null"]},"fx_rate_used":{"description":"FX conversion snapshot (ADR 0002 lock moment: purchase entry) —\nset once at entry when currency ≠ org base; immutable afterwards.","type":["string","null"]},"fx_source":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"invoice_line_id":{"description":"Set once this line became an invoice line (\"billed once, ever\").","format":"uuid","type":["string","null"]},"kind":{"$ref":"#/components/schemas/CostLineKind"},"org_id":{"format":"uuid","type":"string"},"quantity":{"format":"double","type":"number"},"ticket_id":{"format":"uuid","type":"string"},"unit_cost_minor":{"format":"int64","type":"integer"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","ticket_id","kind","description","quantity","unit_cost_minor","currency","billable","created_at","updated_at"],"type":"object"},"TicketCustomFieldValue":{"description":"One stored ticket value with its definition context.","properties":{"field_id":{"format":"uuid","type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"required":{"type":"boolean"},"sort_order":{"format":"int32","type":"integer"},"value":{}},"required":["field_id","name","kind","required","sort_order","value"],"type":"object"},"TicketEmailRow":{"allOf":[{"$ref":"#/components/schemas/EmailMessage"},{"properties":{"delivery":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/EmailDeliveryView","description":"Outbound send lifecycle; `null` for inbound rows."}]},"reply_to_message_id":{"description":"The id of the message INSIDE THIS TICKET this row replies to,\nresolved case-insensitively against the ledger's Message-IDs.\n`null` when the parent is unknown or lives on another ticket.","format":"uuid","type":["string","null"]}},"type":"object"}],"description":"One ledger message as the timeline renders it: the full `EmailMessage`\nshape (unchanged, additive only) plus the delivery projection and the\nresolved in-ticket reply parent."},"TicketEmailsView":{"properties":{"messages":{"description":"Newest-first, mirroring the ledger's thread view.","items":{"$ref":"#/components/schemas/TicketEmailRow"},"type":"array"}},"required":["messages"],"type":"object"},"TicketExpense":{"description":"One expense entry on a ticket.","properties":{"amount_minor":{"format":"int64","type":"integer"},"base_amount_minor":{"format":"int64","type":["integer","null"]},"billable":{"type":"boolean"},"created_at":{"format":"date-time","type":"string"},"currency":{"type":"string"},"description":{"type":"string"},"fx_rate_date":{"type":["string","null"]},"fx_rate_used":{"description":"FX conversion snapshot (ADR 0002 lock moment: expense entry) —\nset once at entry when currency ≠ org base; immutable afterwards.","type":["string","null"]},"fx_source":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"incurred_at":{"format":"date-time","type":"string"},"incurred_by":{"format":"uuid","type":["string","null"]},"invoice_line_id":{"format":"uuid","type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"ticket_id":{"format":"uuid","type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","ticket_id","description","amount_minor","currency","billable","incurred_at","created_at","updated_at"],"type":"object"},"TicketFieldInput":{"description":"One value assignment on a ticket create/patch: raw JSON validated against\nits definition server-side.","properties":{"field_id":{"format":"uuid","type":"string"},"value":{}},"required":["field_id","value"],"type":"object"},"TicketLinkType":{"description":"A typed edge between two tickets.\n\n- `ParentChild`: `from_ticket_id` is the CHILD, `to_ticket_id` the\n  parent — child work rolls up to the parent.\n- `MajorIncident`: `from_ticket_id` is a MEMBER, `to_ticket_id` the\n  major incident it folded into.","enum":["parent_child","major_incident"],"type":"string"},"TicketListQuery":{"properties":{"assignee_id":{"format":"uuid","type":["string","null"]},"board_id":{"format":"uuid","type":["string","null"]},"category":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TicketStatusCategory"}]},"client_id":{"format":"uuid","type":["string","null"]},"cursor":{"description":"Keyset cursor (Wave 15 lane 3): the opaque `x-next-cursor` value\nfrom the previous page's last row. Resumes the ranked list\nstrictly after that row — deep pages stop paying O(offset) scan\ncost (docs/perf.md F2). When present, `offset` is ignored.","type":["string","null"]},"limit":{"format":"int64","type":"integer"},"offset":{"format":"int64","type":"integer"},"priority":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Priority"}]},"tags":{"description":"Comma-separated tag ids (Wave 11 lane 9): AND semantics — tickets\nmust carry every listed tag.","type":["string","null"]}},"type":"object"},"TicketMatch":{"allOf":[{"$ref":"#/components/schemas/MyWorkItem"},{"properties":{"candidates":{"description":"Active technicians sorted best-first (fewest missing, then name).","items":{"$ref":"#/components/schemas/MatchCandidate"},"type":"array"},"required_skills":{"items":{"type":"string"},"type":"array"}},"required":["required_skills","candidates"],"type":"object"}],"description":"Skills-matched staffing for one unassigned ticket. Tickets with no\nrequired skills are not listed (the plain queue ordering covers them)."},"TicketMutationResponse":{"allOf":[{"$ref":"#/components/schemas/TicketWithFields"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): the enriched ticket fields at the\ntop level (unchanged shape) plus the additive `mutation` metadata — the\napplied snapshot, monotonic per-entity `seq`/`version`, and the prior\nand new values that make undo a core capability."},"TicketNumber":{"description":"Human-facing ticket number, e.g. `MAR-2841`. Globally unique per org,\nassigned from a per-org sequence.","type":"string"},"TicketPickRowView":{"allOf":[{"$ref":"#/components/schemas/TicketPickView"},{"properties":{"is_tool":{"type":"boolean"},"item_name":{"type":["string","null"]}},"required":["is_tool"],"type":"object"}]},"TicketPickView":{"properties":{"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"note":{"type":["string","null"]},"qty_milli":{"format":"int64","type":"integer"},"stock_item_id":{"format":"uuid","type":"string"},"ticket_id":{"format":"uuid","type":"string"}},"required":["id","ticket_id","stock_item_id","qty_milli","created_at"],"type":"object"},"TicketPicksQuery":{"properties":{"ticket_id":{"format":"uuid","type":"string"}},"required":["ticket_id"],"type":"object"},"TicketRevertResponse":{"allOf":[{"$ref":"#/components/schemas/TicketWithFields"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"},"revert":{"$ref":"#/components/schemas/RevertDetail"}},"required":["mutation","revert"],"type":"object"}],"description":"The reverted ticket with the mutation envelope, plus what was reverted.\nDeserializable so an IF1 idempotent replay can reconstruct the exact\nstored response."},"TicketRollup":{"description":"Rollup over the children/members pointing at one ticket, plus the\nticket's own billable money lines.","properties":{"billable_cost_minor":{"description":"Summed minor-unit total of billable cost lines + expenses.","format":"int64","type":"integer"},"child_count":{"format":"int64","type":"integer"},"child_open_count":{"description":"Open (not closed, not merged) children still carrying work.","format":"int64","type":"integer"},"major_incident_member_count":{"format":"int64","type":"integer"},"ticket_id":{"format":"uuid","type":"string"},"time_minutes":{"description":"Summed minutes of folded-in time (own + children/members).","format":"int64","type":"integer"}},"required":["ticket_id","child_count","child_open_count","major_incident_member_count","time_minutes","billable_cost_minor"],"type":"object"},"TicketSkillsMutationResponse":{"allOf":[{"$ref":"#/components/schemas/TicketSkillsView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"The full-replace mutation envelope (IF2)."},"TicketSkillsView":{"properties":{"skills":{"items":{"type":"string"},"type":"array"},"ticket_id":{"format":"uuid","type":"string"}},"required":["ticket_id","skills"],"type":"object"},"TicketSlaState":{"description":"Per-ticket SLA clock state. Accumulates paused business seconds so a\nWaiting stretch does not consume the resolution budget.","properties":{"breached":{"type":"boolean"},"first_responded_at":{"format":"date-time","type":["string","null"]},"first_response_due":{"format":"date-time","type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"paused_business_secs":{"description":"Business seconds accumulated while the clock was paused.","format":"int64","type":"integer"},"paused_since":{"description":"When the current pause began (None while running).","format":"date-time","type":["string","null"]},"policy_id":{"format":"uuid","type":"string"},"resolution_due":{"format":"date-time","type":["string","null"]},"ticket_id":{"format":"uuid","type":"string"}},"required":["org_id","ticket_id","policy_id","paused_business_secs","breached"],"type":"object"},"TicketSlaStatus":{"description":"Live SLA clock readout for one ticket (badge + countdown surfaces).","properties":{"applied":{"description":"False when no SLA policy applies to the ticket — a typed empty\nstate, never a 404 (the endpoint is polled by the ticket header).","type":"boolean"},"breached":{"type":"boolean"},"minutes_remaining":{"description":"Business minutes left on the resolution budget (negative = over).","format":"int64","type":["integer","null"]},"paused":{"description":"True while a Waiting-category pause is open.","type":"boolean"},"state":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TicketSlaState"}]}},"required":["applied","paused","breached"],"type":"object"},"TicketStatusCategory":{"description":"The four semantic categories every ticket status maps to (§5).\nCustomization (labels, ordering, per-board sets) can never add, remove, or\nre-bind these: SLA timers, pause logic, automations, billing, and reports\nbind to the category, not the label.","enum":["open","in_progress","waiting","closed"],"type":"string"},"TicketStatusMutationResponse":{"allOf":[{"$ref":"#/components/schemas/Ticket"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Same additive envelope for status transitions (plain ticket body)."},"TicketWithFields":{"allOf":[{"$ref":"#/components/schemas/Ticket"},{"properties":{"custom_fields":{"items":{"$ref":"#/components/schemas/TicketCustomFieldValue"},"type":"array"},"site_timezone":{"description":"The ticket's site zone, when the site names one (Wave 50 lane T2).\n\nThe record-zone chain is site → org default → UTC, and the server ships\nthe site's raw value only: the org default is the registry setting\n`general.default_timezone`, which every member may already read, so the\nfallback is resolved once in the client instead of being frozen into\nevery row that leaves this endpoint.","type":["string","null"]}},"required":["custom_fields"],"type":"object"}],"description":"A ticket payload enriched with its custom-field values (detail + list)."},"TicketsByCategory":{"description":"Tickets by workflow category. The status NAME is per-board and repeats\nacross boards, so a name-keyed map would merge different statuses;\n`category` is the closed vocabulary the workflow layer owns.","properties":{"closed":{"format":"int64","type":"integer"},"in_progress":{"format":"int64","type":"integer"},"open":{"format":"int64","type":"integer"},"waiting":{"format":"int64","type":"integer"}},"required":["open","in_progress","waiting","closed"],"type":"object"},"TimeApprovalAction":{"description":"The audit-trail action of a [`TimeEntryApprovalRecord`] (append-only\ndecision trail; differs from [`ApprovalAction`] in that it names the\nrecorded past, not the requested transition).","enum":["submitted","approved","rejected","reopened","fixed"],"type":"string"},"TimeCategory":{"description":"An org's semantic time category (Wave 39 V4). Entries key their\n`work_type` column onto `slug`; the T&M rate card keys\n`contract_rates.work_type` onto the same slugs. Renames change the\nlabel only — slugs are stable forever.","properties":{"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"default_billable":{"description":"Billing default applied at capture time.","type":"boolean"},"description":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"label":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"position":{"description":"Admin-managed display order; capture surfaces sort by it.","format":"int32","type":"integer"},"rate_card_affinity":{"description":"Hint that T&M card rows key onto this slug (admin documentation;\nthe card itself stays contract-owned).","type":"boolean"},"requires_unbillable_reason":{"description":"When true, flipping billable off demands a typed\n[`UnbillableReason`] first (validated in the domain + API).","type":"boolean"},"retired":{"description":"Soft retire: pickers drop retired categories, entries keep them.","type":"boolean"},"slug":{"description":"Lowercase snake_case semantic key. `work_type` values are these.","type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","org_id","slug","label","default_billable","requires_unbillable_reason","rate_card_affinity","position","retired","created_at","updated_at"],"type":"object"},"TimeCategoryCreateRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"default_billable":{"type":"boolean"},"description":{"type":["string","null"]},"label":{"type":"string"},"position":{"format":"int32","type":["integer","null"]},"rate_card_affinity":{"type":"boolean"},"requires_unbillable_reason":{"type":"boolean"},"slug":{"type":"string"}},"required":["slug","label"],"type":"object"}]},"TimeCategoryListResponse":{"properties":{"categories":{"items":{"$ref":"#/components/schemas/TimeCategoryView"},"type":"array"}},"required":["categories"],"type":"object"},"TimeCategoryMutationResponse":{"properties":{"category":{"$ref":"#/components/schemas/TimeCategoryView"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["category","mutation"],"type":"object"},"TimeCategoryUpdateRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"default_billable":{"type":["boolean","null"]},"description":{"description":"Some → set (null clears); absent → no change.","type":["string","null"]},"label":{"type":["string","null"]},"rate_card_affinity":{"type":["boolean","null"]},"requires_unbillable_reason":{"type":["boolean","null"]},"retired":{"description":"Un-retire a soft-retired category. `Some(false)` restores it to\nthe pickers; retirement is reversible because entries and rate\ncards keep the slug.","type":["boolean","null"]}},"type":"object"}]},"TimeCategoryView":{"allOf":[{"$ref":"#/components/schemas/TimeCategory"},{"properties":{"entry_count":{"description":"Live count of entries carrying this slug (settings usage read).","format":"int64","type":"integer"}},"required":["entry_count"],"type":"object"}]},"TimeEntry":{"description":"A durable, reviewable record of worked time. Created from a timer session\nor entered manually; subject to bulk review and approval workflows.","properties":{"approval_reason_code":{"description":"Typed rejection reason (the [`TimeRejectionReason`] wire code) when\nthe entry was rejected; cleared on resubmit.","type":["string","null"]},"approval_status":{"description":"Approval lifecycle state. `None` on legacy snapshots (pre-approval\nrows and old event payloads) and treated as [`ApprovalStatus::Draft`]\nby the workflow; new writes always carry the status.","type":["string","null"]},"billable":{"type":"boolean"},"created_at":{"format":"date-time","type":"string"},"ended_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"minutes":{"description":"Worked seconds — authoritative, derived on write, never recomputed.","format":"int64","type":"integer"},"notes":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"phase_id":{"description":"The delivery phase this entry scopes to (project-scoped entries\nonly; implies the project). `None` on ticket-scoped entries.","format":"uuid","type":["string","null"]},"started_at":{"format":"date-time","type":"string"},"ticket_id":{"description":"The ticket this entry bills through. `None` ONLY on project-scoped\nentries (Wave 39 lane 4, report P5): the entry scopes to\n[`phase_id`](Self::phase_id) instead and bills through the phase\nproject's linked contract — project work pays without a ticket\ndetour. Exactly one of (ticket_id, phase_id) is present; the DB\nCHECK mirrors it.","format":"uuid","type":["string","null"]},"timer_session_id":{"description":"Origin: timer-derived entries keep their session for audit.","format":"uuid","type":["string","null"]},"unbillable_reason":{"description":"Typed unbillable reason (the [`UnbillableReason`] wire code) —\nrequired when `billable` is false; null on billable entries.","type":["string","null"]},"user_id":{"format":"uuid","type":"string"},"work_type":{"type":["string","null"]}},"required":["id","org_id","user_id","started_at","ended_at","minutes","billable","created_at"],"type":"object"},"TimeEntryAdjustment":{"description":"One verification edit on one entry's billed minutes (Wave 39 V2).\nAdditive-ledger: the original entry row is never rewritten for\nbilling — the run sums `(entry.minutes + Σ delta_minutes)`. The\nsigned delta is ≤ 0 (verification can only reduce or zero); the\nreason is the evidence (V9) and is NOT NULL at the DB level.","properties":{"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"delta_minutes":{"description":"Signed; always ≤ 0. `original_minutes + delta >= 0` holds.","format":"int32","type":"integer"},"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"original_minutes":{"description":"Denormalized evidence: minutes BEFORE the adjustment.","format":"int32","type":"integer"},"reason":{"type":"string"},"run_id":{"description":"The run this adjustment rides, when it was made during a run's\nclose flow; `None` when made from the standalone workspace.","format":"uuid","type":["string","null"]},"time_entry_id":{"format":"uuid","type":"string"}},"required":["id","org_id","time_entry_id","original_minutes","delta_minutes","reason","created_at"],"type":"object"},"TimeEntryApprovalRecord":{"description":"One append-only approval decision (the audit row behind the queue's\nhistory view). `seq` is monotonic per entry — the client's ordering key.","properties":{"action":{"$ref":"#/components/schemas/TimeApprovalAction"},"actor_id":{"format":"uuid","type":"string"},"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"note":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"reason_code":{"description":"Typed [`TimeRejectionReason`] wire code on rejections, else None.","type":["string","null"]},"seq":{"format":"int32","type":"integer"},"time_entry_id":{"format":"uuid","type":"string"}},"required":["id","org_id","time_entry_id","seq","action","actor_id","created_at"],"type":"object"},"TimeEntryMutationResponse":{"allOf":[{"$ref":"#/components/schemas/TimeEntry"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Same additive envelope for the stop flow (time-entry body)."},"TimeEntryQueueItem":{"description":"A queue/history projection: the entry plus its approval metadata\n(who/when submitted, who/when decided).","properties":{"decided_at":{"format":"date-time","type":["string","null"]},"decided_by":{"format":"uuid","type":["string","null"]},"entry":{"$ref":"#/components/schemas/TimeEntry"},"locked":{"description":"True once the entry is consumed by an invoice — frozen forever.","type":"boolean"},"submitted_at":{"format":"date-time","type":["string","null"]},"submitted_by":{"format":"uuid","type":["string","null"]},"ticket":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TimeEntryTicketRef","description":"The ticket's human number (`18366`) and title, so the queue can lead\nwith \"#18366 Disable leaver accounts\" instead of a uuid prefix. A\nperson reads a ticket number; they do not read an id. Absent for\nproject-scoped entries, which have no ticket at all."}]},"user_timezone":{"description":"The zone the person who did the work was in when they did it — the\nrecord's clock for this entry (Wave 50 lane T2). The approver reads\ntheir own clock in the row; this is the one the member logged against,\nand it is what the daily totals are grouped by. Absent when the member\nrow is gone.","type":["string","null"]}},"required":["entry","locked"],"type":"object"},"TimeEntryTicketRef":{"description":"The ticket an entry bills through, named for a human reader.","properties":{"client_name":{"description":"The client the ticket belongs to, when it is known. `None` for a\nticket with no client yet — the row then shows no client rather than\nan empty one.","type":["string","null"]},"id":{"format":"uuid","type":"string"},"number":{"type":"string"},"title":{"type":"string"}},"required":["id","number","title"],"type":"object"},"TimeRejectionReason":{"description":"Closed rejection vocabulary (Wave 14 lane 1): approvers reject with a\nmachine-readable reason code, never prose-only. `other` may carry the\nfree-text explanation on the decision record.","enum":["missing_notes","wrong_ticket","incorrect_duration","duplicate_entry","non_billable_work","policy_violation","other"],"type":"string"},"TimeSettingsResponse":{"properties":{"eod_nudge_hour":{"description":"Local hour (member timezones) the EOD audit fires at.","format":"int32","type":"integer"},"eod_nudges_enabled":{"type":"boolean"},"time_gap_min_minutes":{"description":"Active days logging under this many minutes are gap days.","format":"int32","type":"integer"}},"required":["eod_nudges_enabled","eod_nudge_hour","time_gap_min_minutes"],"type":"object"},"TimeVerdict":{"description":"One verdict: what is odd, said in words, plus the fix beside it.","properties":{"fix":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TimeVerdictFix"}]},"kind":{"$ref":"#/components/schemas/TimeVerdictKind"},"label":{"description":"The mark's words, typed and short: \"Vague description\",\n\"Overlaps #18355 by 30 min\", \"Longer than usual, 2:00 vs 0:45\".","type":"string"}},"required":["kind","label"],"type":"object"},"TimeVerdictFix":{"description":"A remedy, carrying the value it applies so the client never has to\nrecompute it (and cannot apply a different one than the server meant).","properties":{"kind":{"$ref":"#/components/schemas/TimeVerdictFixKind"},"label":{"description":"Button text, in the pack's voice: \"Use suggestion\", \"Trim 30m\",\n\"Ask the tech\".","type":"string"},"suggested_description":{"description":"Set for [`TimeVerdictFixKind::UseSuggestion`]: the exact description\nto write. Assembled from the ticket title and the work type.","type":["string","null"]},"trim_minutes":{"description":"Set for [`TimeVerdictFixKind::TrimMinutes`]: whole minutes to cut,\nalways positive and never more than the entry holds.","format":"int32","type":["integer","null"]}},"required":["kind","label"],"type":"object"},"TimeVerdictFixKind":{"description":"The one action that clears a verdict.","enum":["use_suggestion","trim_minutes","ask"],"type":"string"},"TimeVerdictKind":{"description":"What is wrong with an entry. A closed set: an open string here would put\nmodel prose on a money surface, which is exactly what the pack forbids.","enum":["vague_description","overlaps","longer_than_usual"],"type":"string"},"TimerMutationResponse":{"allOf":[{"$ref":"#/components/schemas/TimerSession"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Instant-first mutation response (IF2): session fields at the top level\n(unchanged shape) plus the additive `mutation` metadata."},"TimerSegment":{"description":"A contiguous running interval within a timer session.","properties":{"end_kind":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SegmentEnd"}]},"ended_at":{"description":"None while this segment is running.","format":"date-time","type":["string","null"]},"reason":{"description":"Free-text reason recorded at pause time (\"on call with vendor\").","type":["string","null"]},"started_at":{"format":"date-time","type":"string"}},"required":["started_at"],"type":"object"},"TimerSession":{"description":"One user's timer against one ticket. At most one session per user is\never running — enforced by [`TimerSession::start_switch`].","properties":{"id":{"format":"uuid","type":"string"},"org_id":{"format":"uuid","type":"string"},"segments":{"items":{"$ref":"#/components/schemas/TimerSegment"},"type":"array"},"started_at":{"format":"date-time","type":"string"},"stopped_at":{"format":"date-time","type":["string","null"]},"ticket_id":{"format":"uuid","type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["id","org_id","user_id","ticket_id","started_at","segments"],"type":"object"},"TokenPair":{"description":"Token pair returned by all auth flows.","properties":{"access_token":{"type":"string"},"expires_in":{"description":"Access token TTL in seconds.","format":"int64","type":"integer"},"refresh_token":{"type":"string"},"session_id":{"format":"uuid","type":"string"}},"required":["access_token","refresh_token","session_id","expires_in"],"type":"object"},"TokenRequest":{"properties":{"token":{"type":"string"}},"required":["token"],"type":"object"},"ToolAvailabilityQuery":{"properties":{"item_id":{"format":"uuid","type":["string","null"]},"limit":{"format":"int64","type":["integer","null"]},"location_id":{"format":"uuid","type":["string","null"]}},"type":"object"},"ToolAvailabilityView":{"properties":{"available_units":{"format":"int64","type":"integer"},"checked_out_units":{"format":"int64","type":"integer"},"item_name":{"type":"string"},"loaned_units":{"format":"int64","type":"integer"},"location_id":{"format":"uuid","type":["string","null"]},"location_name":{"type":["string","null"]},"out_units":{"format":"int64","type":"integer"},"stock_item_id":{"format":"uuid","type":"string"}},"required":["stock_item_id","item_name","available_units","checked_out_units","loaned_units","out_units"],"type":"object"},"ToolCheckoutDetailResponse":{"properties":{"checkout":{"$ref":"#/components/schemas/ToolCheckoutView"},"lines":{"items":{"$ref":"#/components/schemas/ToolCheckoutLineView"},"type":"array"}},"required":["checkout","lines"],"type":"object"},"ToolCheckoutLineView":{"properties":{"id":{"format":"uuid","type":"string"},"movement_in_id":{"format":"uuid","type":["string","null"]},"movement_out_id":{"format":"uuid","type":"string"},"return_condition":{"type":["string","null"]},"return_notes":{"type":["string","null"]},"returned_at":{"type":["string","null"]},"serial_id":{"format":"uuid","type":"string"},"stock_item_id":{"format":"uuid","type":"string"}},"required":["id","stock_item_id","serial_id","movement_out_id"],"type":"object"},"ToolCheckoutMutationResponse":{"properties":{"checkout":{"$ref":"#/components/schemas/ToolCheckoutView"},"lines":{"items":{"$ref":"#/components/schemas/ToolCheckoutLineView"},"type":"array"},"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["checkout","lines","mutation"],"type":"object"},"ToolCheckoutView":{"properties":{"checked_out_by":{"format":"uuid","type":["string","null"]},"created_at":{"type":"string"},"due_at":{"type":"string"},"from_location_id":{"format":"uuid","type":"string"},"holder_user_id":{"format":"uuid","type":"string"},"id":{"format":"uuid","type":"string"},"kit_id":{"format":"uuid","type":["string","null"]},"notes":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"overdue":{"description":"Derived: open past the due date.","type":"boolean"},"return_notes":{"type":["string","null"]},"returned_at":{"type":["string","null"]},"status":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","org_id","holder_user_id","from_location_id","due_at","status","overdue","created_at","updated_at"],"type":"object"},"ToolKitMutationResponse":{"allOf":[{"$ref":"#/components/schemas/ToolKitView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}]},"ToolKitView":{"properties":{"active":{"type":"boolean"},"code":{"type":["string","null"]},"created_at":{"type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"note":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"updated_at":{"type":"string"}},"required":["id","org_id","name","active","created_at","updated_at"],"type":"object"},"ToolSerialWhereaboutsView":{"properties":{"checkout_id":{"format":"uuid","type":["string","null"]},"client_id":{"description":"Open loan client, when the unit is on a client loan.","format":"uuid","type":["string","null"]},"created_at":{"type":"string"},"due_at":{"type":["string","null"]},"expected_return_at":{"type":["string","null"]},"holder_user_id":{"description":"Open checkout holder, when the unit is checked out.","format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_tool":{"description":"True when the item is a flagged tool (checkout-eligible).","type":"boolean"},"item_name":{"type":"string"},"loan_id":{"format":"uuid","type":["string","null"]},"location_id":{"format":"uuid","type":["string","null"]},"location_name":{"type":["string","null"]},"overdue":{"description":"Derived: checked-out past due, or loaned past its expected return.","type":"boolean"},"serial_number":{"type":"string"},"status":{"type":"string"},"stock_item_id":{"format":"uuid","type":"string"},"updated_at":{"type":"string"}},"required":["id","stock_item_id","item_name","is_tool","serial_number","status","overdue","created_at","updated_at"],"type":"object"},"ToolSerialsQuery":{"properties":{"holder_user_id":{"format":"uuid","type":["string","null"]},"item_id":{"format":"uuid","type":["string","null"]},"limit":{"format":"int64","type":["integer","null"]},"location_id":{"format":"uuid","type":["string","null"]},"overdue_only":{"description":"Only units past their checkout due date or loan expected return.","type":["boolean","null"]},"status":{"description":"in_stock | out | checked_out | loaned.","type":["string","null"]},"tool_only":{"description":"`true` = tools only; `false` = devices only; absent = both.","type":["boolean","null"]}},"type":"object"},"TotpDisableRequest":{"properties":{"code":{"description":"Current TOTP code or a recovery code.","type":"string"},"password":{"type":"string"}},"required":["password","code"],"type":"object"},"TotpEnrollBeginResponse":{"properties":{"otpauth_url":{"description":"Standard otpauth:// URI (renderable as QR by clients).","type":"string"},"secret_base32":{"description":"Base32 secret for manual authenticator entry. Shown exactly once.","type":"string"}},"required":["secret_base32","otpauth_url"],"type":"object"},"TotpEnrollVerifyRequest":{"properties":{"code":{"type":"string"}},"required":["code"],"type":"object"},"TranscriptionResponse":{"properties":{"duration":{"description":"The audio duration the provider reports, when it sends one.","type":["string","null"]},"model":{"type":"string"},"provider":{"type":"string"},"surface":{"description":"The typed surface the call rode (always `transcription` here).","type":"string"},"text":{"type":"string"}},"required":["surface","provider","model","text"],"type":"object"},"TransferRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"from_location_id":{"format":"uuid","type":"string"},"item_id":{"format":"uuid","type":"string"},"note":{"type":["string","null"]},"qty_milli":{"format":"int64","type":"integer"},"serial_id":{"description":"Serialized transfer moves ONE unit.","format":"uuid","type":["string","null"]},"to_location_id":{"format":"uuid","type":"string"}},"required":["item_id","from_location_id","to_location_id","qty_milli"],"type":"object"}]},"TransitionChangeOrderRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key +\nIf-Match `version`."},{"properties":{"action":{"description":"`approve|reject|cancel`. `billed` is run-owned only (never a\nmanual write — the invoice run flips it with the invoice link).","type":"string"}},"required":["action"],"type":"object"}]},"TransitionEdge":{"description":"One directed edge in a board's explicit transition matrix. Both ids must\nbelong to statuses of the SAME board (enforced here, and by composite FKs\nin storage).","properties":{"from_status_id":{"format":"uuid","type":"string"},"to_status_id":{"format":"uuid","type":"string"}},"required":["from_status_id","to_status_id"],"type":"object"},"TravelDraftMutationResponse":{"allOf":[{"$ref":"#/components/schemas/TravelDraftView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"Mutation envelope: draft fields at the top level plus the additive\n`mutation` metadata (IF2)."},"TravelDraftView":{"description":"One travel/mileage draft.","properties":{"appointment_id":{"description":"Wave 32 lane 7: the planned appointment this trip reconciles\nagainst, when linked.","format":"uuid","type":["string","null"]},"billable":{"type":"boolean"},"created_at":{"format":"date-time","type":"string"},"distance":{"format":"double","type":"number"},"distance_unit":{"description":"`km` | `mi`.","type":"string"},"ended_at":{"format":"date-time","type":"string"},"expense_id":{"description":"Wave 32 lane 7: the expense this draft's mileage produced, once\nreimbursed.","format":"uuid","type":["string","null"]},"id":{"format":"uuid","type":"string"},"notes":{"type":["string","null"]},"started_at":{"format":"date-time","type":"string"},"status":{"description":"`draft` | `submitted`.","type":"string"},"submitted_time_entry_id":{"format":"uuid","type":["string","null"]},"ticket_id":{"format":"uuid","type":"string"},"updated_at":{"format":"date-time","type":"string"},"user_id":{"format":"uuid","type":"string"},"vehicle":{"type":"string"}},"required":["id","user_id","ticket_id","started_at","ended_at","distance","distance_unit","vehicle","billable","status","created_at","updated_at"],"type":"object"},"TravelOrigin":{"description":"The resolved journey start point a travel estimate ran from (Wave 32\nlane 7). Snapshotted onto the appointment so the estimate stays\nexplainable after the start point or the site changes.","properties":{"kind":{"description":"`home` | `office` | `site` | `address`.","type":"string"},"label":{"type":["string","null"]},"latitude":{"format":"double","type":["number","null"]},"longitude":{"format":"double","type":["number","null"]},"site_id":{"format":"uuid","type":["string","null"]},"source":{"description":"How it was resolved: `explicit` (dispatcher-set day start) or\n`previous_day_site` (auto-suggested continuity).","type":["string","null"]}},"required":["kind"],"type":"object"},"TravelPlaceView":{"properties":{"kind":{"type":"string"},"label":{"type":["string","null"]},"latitude":{"format":"double","type":["number","null"]},"longitude":{"format":"double","type":["number","null"]},"site_id":{"format":"uuid","type":["string","null"]}},"required":["kind"],"type":"object"},"TravelSuggestionView":{"properties":{"appointment_id":{"format":"uuid","type":"string"},"kind":{"description":"`swap_adjacent` | `reassign`.","type":"string"},"label":{"type":"string"},"minutes_saved":{"format":"int64","type":"integer"},"swap_with_appointment_id":{"format":"uuid","type":["string","null"]},"target_user_id":{"format":"uuid","type":["string","null"]}},"required":["kind","appointment_id","minutes_saved","label"],"type":"object"},"TravelWarningView":{"properties":{"appointment_id":{"format":"uuid","type":["string","null"]},"gap_minutes":{"format":"int64","type":["integer","null"]},"kind":{"type":"string"},"message":{"type":"string"},"provider_reason":{"type":["string","null"]},"required_minutes":{"format":"int64","type":["integer","null"]},"short_minutes":{"format":"int64","type":["integer","null"]}},"required":["kind","message"],"type":"object"},"TxtCheck":{"properties":{"error":{"description":"Set when the lookup itself failed (no resolver, transport error).","type":["string","null"]},"matched":{"description":"Whether an observed TXT string equalled the issued token (the\nauthority for verification).","type":"boolean"},"observed":{"description":"Every TXT string observed at the record name.","items":{"type":"string"},"type":"array"}},"required":["matched","observed"],"type":"object"},"UnavailableMetric":{"description":"A metric the core does not report, and why. The reason is prose for a\nHUMAN operator reading the surface; `metric` is the stable token a\ndashboard can group on.","properties":{"metric":{"type":"string"},"reason":{"type":"string"}},"required":["metric","reason"],"type":"object"},"UnbillableReason":{"description":"Why an entry is not billable (report V3). Typed values only — the\nverification run and scoring reports aggregate on these codes.","enum":["warranty","courtesy","in_retainer_block","sla_covered","internal","mistake"],"type":"string"},"UndoFieldStep":{"description":"One field of one applied write — per-field undo granularity.","properties":{"actor_id":{"format":"uuid","type":"string"},"actor_kind":{"type":"string"},"changed":{"type":"boolean"},"created_at":{"type":"string"},"field":{"type":"string"},"new_value":{"description":"Value after the write (absent keys read as `null`).","type":["object","null"]},"op":{"type":"string"},"prior":{"description":"Value before the write (absent keys read as `null`).","type":["object","null"]},"seq":{"format":"int64","type":"integer"},"write_id":{"format":"uuid","type":"string"}},"required":["write_id","seq","op","field","prior","new_value","changed","actor_id","actor_kind","created_at"],"type":"object"},"UndoMode":{"description":"The record's undo posture: `open` = near-total undo; `reverts_only` =\nexplicit actioned per-field reverts only (record is closed).","enum":["open","reverts_only"],"type":"string"},"UndoStackQuery":{"properties":{"limit":{"description":"Maximum steps / field rows returned (1–200, default 50). Newest\nfirst; the first step's `seq` is the record's current version.","format":"int64","type":["integer","null"]}},"type":"object"},"UndoStackResponse":{"properties":{"current_version":{"description":"The record's current version — echo it as `version` on a revert.","format":"int64","type":"integer"},"entity":{"type":"string"},"entity_id":{"format":"uuid","type":"string"},"fields":{"description":"Per-field history, newest write first.","items":{"$ref":"#/components/schemas/UndoFieldStep"},"type":"array"},"mode":{"$ref":"#/components/schemas/UndoMode","description":"Near-total (`open`) vs actioned-reverts-only (`reverts_only`)."},"steps":{"description":"Applied writes, newest first.","items":{"$ref":"#/components/schemas/UndoStep"},"type":"array"}},"required":["entity","entity_id","mode","current_version","steps","fields"],"type":"object"},"UndoStep":{"description":"One applied write on the record — one step of the undo stack.","properties":{"actor_id":{"format":"uuid","type":"string"},"actor_kind":{"description":"`human` | `agent` | ... (the audit actor vocabulary).","type":"string"},"after":{"description":"Applied entity snapshot.","type":"object"},"before":{"description":"Prior entity snapshot; `null` on creates.","type":["object","null"]},"created_at":{"type":"string"},"idempotency_key":{"type":["string","null"]},"op":{"description":"Machine op name (`create`, `update`, `status_change`, `revert`, ...).","type":"string"},"restorable":{"description":"The revert path can restore this write's prior values (`create`\nsteps are visible history but not value-restorable).","type":"boolean"},"seq":{"description":"Per-entity monotonic write number (the version this write produced).","format":"int64","type":"integer"},"write_id":{"description":"Trail row id (stable reference for actioned reverts).","format":"uuid","type":"string"}},"required":["write_id","seq","op","actor_id","actor_kind","before","after","restorable","created_at"],"type":"object"},"UniformOk":{"description":"The uniform shape every request surface returns.","properties":{"ok":{"type":"boolean"}},"required":["ok"],"type":"object"},"UnitBasis":{"description":"Which org inventory counts a per-unit contract's quantity at\nevaluation time (C: quantity × unit price per period, unit counts\ndrawn from the org's asset/user inventories at evaluation).","enum":["devices","users"],"type":"string"},"UnreadCount":{"properties":{"count":{"format":"int64","type":"integer"}},"required":["count"],"type":"object"},"UpdateArticleRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"body":{"type":["string","null"]},"category_id":{"description":"`null` clears the category; omit to leave it untouched.","format":"uuid","type":["string","null"]},"client_visible":{"type":["boolean","null"]},"content_jsonb":{"description":"Canonical rich content (ADR 0007); validated on every write. The\neditor's debounced autosave rides THIS field over the instant-first\nenvelope (IF1–IF3 unchanged: idempotency key + version/If-Match)."},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/KbArticleStatus"}]},"title":{"type":["string","null"]}},"type":"object"}]},"UpdateAssetRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first mutation parameters (IF1)."},{"properties":{"fields":{},"layout_id":{"format":"uuid","type":["string","null"]},"lifecycle":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AssetLifecycleInput","description":"Inventory lifecycle inputs (rental tracking + depreciation);\nabsent = keep (patch semantics)."}]},"name":{"type":["string","null"]},"site_id":{"format":"uuid","type":["string","null"]},"status":{"type":["string","null"]}},"type":"object"}]},"UpdateCalendarRequest":{"properties":{"holidays":{},"maintenance":{},"name":{"type":["string","null"]},"timezone":{"type":["string","null"]},"windows":{}},"type":"object"},"UpdateCatalogItemRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"active":{"description":"Reactivating an archived item is a plain update.","type":["boolean","null"]},"category":{"type":["string","null"]},"cost_minor":{"format":"int64","type":["integer","null"]},"currency":{"type":["string","null"]},"description":{"type":["string","null"]},"group_name":{"type":["string","null"]},"name":{"type":["string","null"]},"sku":{"description":"Some → set (null clears); absent → no change.","type":["string","null"]},"taxable":{"type":["boolean","null"]},"unit":{"type":["string","null"]},"unit_price_minor":{"format":"int64","type":["integer","null"]}},"type":"object"}],"description":"PATCH /api/catalog-items/{id} body. Absent fields keep their value;\n`Some(None)` clears an optional field."},"UpdateCategoryRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"client_visible":{"type":["boolean","null"]},"description":{"type":["string","null"]},"name":{"type":["string","null"]},"position":{"format":"int32","type":["integer","null"]}},"type":"object"}]},"UpdateConnectionRequest":{"properties":{"direction":{"description":"`outbound` | `inbound` | `two_way`.","type":["string","null"]},"feed_url":{"type":["string","null"]},"name":{"type":["string","null"]},"status":{"description":"`active` | `disabled` | `error`.","type":["string","null"]}},"type":"object"},"UpdateCostLineRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"billable":{"type":["boolean","null"]},"description":{"type":["string","null"]},"quantity":{"format":"double","type":["number","null"]},"unit_cost_minor":{"format":"int64","type":["integer","null"]}},"type":"object"}]},"UpdateDealRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"amount_minor":{"format":"int64","type":["integer","null"]},"client_id":{"format":"uuid","type":["string","null"]},"currency":{"type":["string","null"]},"expected_close_on":{"format":"date","type":["string","null"]},"name":{"type":["string","null"]},"notes":{"type":["string","null"]},"owner_user_id":{"format":"uuid","type":["string","null"]},"quote_id":{"format":"uuid","type":["string","null"]}},"type":"object"}],"description":"PATCH /api/pipeline/deals/{id} body — editable fields only; the stage\nmoves via the /stage leg."},"UpdateDirectoryGroupRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + version."},{"properties":{"description":{"description":"New description. Absent keeps the current one; an explicit `null`\nclears it.","type":["string","null"]},"external_id":{"description":"New external id. Absent keeps it; an explicit `null` clears it (the row\nis then locally owned again).","type":["string","null"]},"name":{"description":"New name. Absent keeps the current one.","type":["string","null"]}},"type":"object"}]},"UpdateDocumentRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"body":{"type":["string","null"]},"change_summary":{"type":["string","null"]},"client_id":{"description":"Absent = keep the current scope; explicit null moves to global.","format":"uuid","type":["string","null"]},"fields":{"description":"Absent = keep; present = validated against the (current) layout."},"layout_id":{"description":"Absent = keep; explicit null = untyped.","format":"uuid","type":["string","null"]},"title":{"type":["string","null"]}},"type":"object"}]},"UpdateExpenseRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"amount_minor":{"format":"int64","type":["integer","null"]},"billable":{"type":["boolean","null"]},"description":{"type":["string","null"]}},"type":"object"}]},"UpdateExternalDocumentRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"article_id":{"description":"`null` detaches the article; omit to leave it untouched.","format":"uuid","type":["string","null"]},"permission_note":{"type":["string","null"]},"title":{"type":["string","null"]},"url":{"type":["string","null"]}},"type":"object"}]},"UpdateFieldDefRequest":{"properties":{"name":{"type":["string","null"]},"options":{"description":"Replaces the option set for choice kinds.","items":{"type":"string"},"type":["array","null"]},"required":{"type":["boolean","null"]},"sort_order":{"format":"int32","type":["integer","null"]},"validator_entry_key":{"description":"§4.6: `Some(None)` clears the plugin validator; `Some(Some(key))`\nsets it; absent leaves it untouched.","type":["string","null"]}},"type":"object"},"UpdateGroupRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1/IF2)."},{"properties":{"description":{"type":["string","null"]},"name":{"type":["string","null"]}},"type":"object"}]},"UpdateInstallRequest":{"properties":{"credential_bindings":{"description":"The install's credential bindings, as a REPLACE-SET: what this list\nnames is what the install holds afterwards, so an omitted profile is a\nwithdrawn consent. `None` leaves the set untouched — the same\ndistinction `granted_capabilities` uses, so a PATCH that only flips\n`status` cannot silently drop a credential.\n\nEach entry names a profile from the pinned manifest's `outbound_scopes`\nand a vault record in this org. The vault's category grant decides\nwhether the acting operator may bind it; the value never travels\nthrough this API and never leaves the server.","items":{"$ref":"#/components/schemas/CredentialBindingInput"},"type":["array","null"]},"granted_capabilities":{"items":{"type":"string"},"type":["array","null"]},"status":{"description":"`enabled` | `disabled` (the admin-enabled gate). `revoked` is\nNOT settable here — revocation rides the dedicated revoke\nsurface so the audit trail names it.","type":["string","null"]},"version":{"description":"Upgrade the pinned version (explicit, never automatic).","type":["string","null"]}},"type":"object"},"UpdateKitRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"active":{"type":["boolean","null"]},"code":{"type":["string","null"]},"name":{"type":["string","null"]},"note":{"type":["string","null"]}},"type":"object"}],"description":"PATCH /api/tool-kits/{id} — absent fields keep their value."},"UpdateLabelTemplateRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"active":{"type":["boolean","null"]},"barcode_token":{"description":"Absent keeps the current token; `null` clears it (no barcode).","type":["string","null"]},"dpi":{"format":"int32","type":["integer","null"]},"fields":{"items":{"type":"string"},"type":["array","null"]},"height_mm":{"format":"double","type":["number","null"]},"include_qr":{"type":["boolean","null"]},"is_default":{"type":["boolean","null"]},"name":{"type":["string","null"]},"purpose":{"type":["string","null"]},"size_preset":{"type":["string","null"]},"width_mm":{"format":"double","type":["number","null"]}},"type":"object"}]},"UpdateLayoutRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"description":{"type":"string"},"fields":{"items":{"$ref":"#/components/schemas/LayoutField"},"type":"array"},"icon":{"type":"string"},"name":{"type":"string"}},"required":["name"],"type":"object"}]},"UpdateOrderRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"expected_at":{"format":"date","type":["string","null"]},"notes":{"type":["string","null"]},"project_id":{"format":"uuid","type":["string","null"]},"vendor_reference":{"type":["string","null"]}},"type":"object"}]},"UpdateOrgDefaultRequest":{"description":"PUT /api/notification-org-defaults — replaces the org default ROW for\none kind; the chain is user override → org default → built-in, so an\nuntouched user's next email follows this policy. Quiet hours validate as\na paired `HH:MM` window in a real IANA timezone (422 otherwise).","properties":{"email":{"type":"boolean"},"in_app":{"type":"boolean"},"kind":{"$ref":"#/components/schemas/NotificationKind"},"quiet_from":{"description":"`HH:MM` pair; both bounds together or neither.","type":["string","null"]},"quiet_until":{"type":["string","null"]},"timezone":{"type":["string","null"]}},"required":["kind","in_app","email"],"type":"object"},"UpdatePasswordRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"expires_at":{"description":"absent = keep; null = clear; value = set.","format":"date-time","type":["string","null"]},"notes":{"description":"absent = keep; null = clear; string = replace (encrypted at rest).","type":["string","null"]},"rotate_at":{"description":"Rotation schedule (Wave 19 lane 3): absent = keep; null = clear;\ninstant = set (stamps last_rotated_at in the same write).","format":"date-time","type":["string","null"]},"secret":{"description":"Absent = keep the current secret; present (even empty string is\nrejected) = rotate and archive the previous ciphertext to history.","type":["string","null"]},"title":{"type":"string"},"url":{"type":"string"},"username":{"type":"string"}},"required":["title"],"type":"object"}]},"UpdatePreferenceRequest":{"properties":{"email":{"type":"boolean"},"in_app":{"type":"boolean"},"kind":{"$ref":"#/components/schemas/NotificationKind"},"quiet_from":{"description":"`HH:MM` pair; both bounds together or neither.","type":["string","null"]},"quiet_until":{"type":["string","null"]},"timezone":{"description":"IANA timezone for the window; defaults to UTC.","type":["string","null"]}},"required":["kind","in_app","email"],"type":"object"},"UpdateProposalView":{"properties":{"consent_diff":{"$ref":"#/components/schemas/ConsentDiffView"},"credential_bindings_cleared":{"description":"The credential bindings this consent would CLEAR, by profile name\n(Wave 53 lane 12).\n\n`outbound_scopes` is manifest-declared, so the same profile name in the\nnext version can allowlist a different host. A binding that outlived\nthe pin move would be consent to a name, quietly extended to whatever\nthe new publisher pointed it at, with the org's credential attached —\nso a pin move clears them. This field is how the screen says so before\nthe operator clicks, rather than leaving them to notice afterwards.\nEmpty when nothing is bound or nothing is pending.","items":{"type":"string"},"type":"array"},"current_granted_capabilities":{"items":{"type":"string"},"type":"array"},"current_version":{"description":"Still running — the OLD version is pinned until consented.","type":"string"},"install_id":{"format":"uuid","type":"string"},"pending_digest":{"type":["string","null"]},"pending_version":{"type":["string","null"]},"slug":{"type":"string"}},"required":["install_id","slug","current_version","current_granted_capabilities","consent_diff","credential_bindings_cleared"],"type":"object"},"UpdateProviderRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"enabled":{"type":["boolean","null"]},"host_patterns":{"items":{"type":"string"},"type":["array","null"]},"name":{"type":["string","null"]},"oembed_endpoint":{"type":["string","null"]},"url_regex":{"type":["string","null"]}},"type":"object"}]},"UpdateQuoteRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"lines":{"items":{"$ref":"#/components/schemas/QuoteLineInput"},"type":["array","null"]},"notes":{"type":["string","null"]},"tax_rate_bps":{"format":"int32","type":["integer","null"]},"terms":{"type":["string","null"]},"valid_until":{"format":"date","type":["string","null"]}},"type":"object"}],"description":"PATCH /api/quotes/{id} body — draft-only. Absent fields keep their\nvalue; `lines` present replaces the whole line list."},"UpdateReplySnippetRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"board_ids":{"items":{"format":"uuid","type":"string"},"type":["array","null"]},"body":{"type":["string","null"]},"name":{"type":["string","null"]}},"type":"object"}]},"UpdateReportDefinitionRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"description":{"type":["string","null"]},"name":{"type":"string"},"scope":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/DefinitionScope","description":"New visibility; `org` requires admin and non-admins keep `self`."}]},"spec":{"$ref":"#/components/schemas/ReportSpec"}},"required":["name","spec"],"type":"object"}],"description":"PUT /api/report-definitions/{id} body — full-replace semantics: every\nmember is applied; `description: null` clears it."},"UpdateReportScheduleRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"cadence":{"$ref":"#/components/schemas/ScheduleCadence"},"day_of_month":{"format":"int32","type":["integer","null"]},"day_of_week":{"format":"int32","type":["integer","null"]},"enabled":{"type":["boolean","null"]},"name":{"type":"string"},"recipients":{"items":{"format":"uuid","type":"string"},"type":"array"},"report_definition_id":{"format":"uuid","type":"string"},"time_of_day":{"type":"string"},"timezone":{"type":"string"},"window_days":{"format":"int32","type":["integer","null"]}},"required":["name","report_definition_id","recipients","cadence","time_of_day","timezone"],"type":"object"}],"description":"PUT /api/report-schedules/{id} body — full-replace semantics; every\nmember is applied and the next-run preview recomputes."},"UpdateRoleRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"name":{"type":["string","null"]},"permissions":{"items":{"type":"string"},"maxItems":64,"type":["array","null"]}},"type":"object"}]},"UpdateRuleRequest":{"properties":{"actions":{},"board_id":{"description":"`Some(Some(id))` scopes; `Some(None)` clears to all boards.","format":"uuid","type":["string","null"]},"conditions":{},"enabled":{"type":["boolean","null"]},"name":{"type":["string","null"]},"subject_kind":{"description":"Replaces the subject declaration (Wave 43 lane 3).","type":["string","null"]},"trigger_event":{"type":["string","null"]}},"type":"object"},"UpdateScheduleRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"assignee_id":{"format":"uuid","type":["string","null"]},"body":{"description":"Some → set (null clears); absent → no change.","type":["string","null"]},"cadence":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CadenceSpec"}]},"client_id":{"format":"uuid","type":["string","null"]},"enabled":{"type":["boolean","null"]},"name":{"type":["string","null"]},"priority":{"type":["string","null"]},"site_id":{"format":"uuid","type":["string","null"]},"title":{"type":["string","null"]}},"type":"object"}]},"UpdateScorecardSettingsRequest":{"properties":{"gamification_opt_in":{"description":"R5's opt-in gamification toggle (streaks / leaderboards). Default\nand privacy-preserving value: false.","type":"boolean"}},"required":["gamification_opt_in"],"type":"object"},"UpdateSecretFolderRequest":{"properties":{"label":{"type":["string","null"]},"restricted":{"type":["boolean","null"]},"slug":{"type":["string","null"]}},"type":"object"},"UpdateSecretRecordRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1)."},{"properties":{"expires_at":{"description":"absent = keep; null = clear; value = set.","format":"date-time","type":["string","null"]},"fields":{"description":"Matching keys rotate (sealed); `value: null` clears (A5).","items":{"$ref":"#/components/schemas/SecretFieldValuePatch"},"type":"array"},"rotate_at":{"description":"Rotation schedule (Wave 19 lane 3): absent = keep; null = clear;\ninstant = set (stamps last_rotated_at in the same write).","format":"date-time","type":["string","null"]},"title":{"type":"string"}},"required":["title"],"type":"object"}]},"UpdateServiceAccountRequest":{"properties":{"expires_at":{"description":"Replacement expiry (null ⇒ never expires). Omitted ⇒ keep current.","format":"date-time","type":["string","null"]},"scopes":{"description":"Replacement grant set. Omitted ⇒ keep current grants.","items":{"type":"string"},"type":["array","null"]},"status":{"description":"Replacement status. Omitted ⇒ keep current.","type":["string","null"]}},"type":"object"},"UpdateSettingRequest":{"properties":{"key":{"type":"string"},"value":{"description":"Must satisfy the declaration's kind or the request is rejected 422."}},"required":["key","value"],"type":"object"},"UpdateSettingsRequest":{"properties":{"auto_merge_threshold":{"description":"Auto-merge only at/above this confidence (0..1).","format":"double","type":"number"}},"required":["auto_merge_threshold"],"type":"object"},"UpdateStageRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"active":{"type":["boolean","null"]},"name":{"type":["string","null"]},"position":{"format":"int32","type":["integer","null"]},"stage_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/StageType"}]}},"type":"object"}],"description":"PATCH /api/pipeline/stages/{id} body."},"UpdateStatusRequest":{"properties":{"category":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TicketStatusCategory","description":"Re-binds the semantic category; behavior follows immediately (§5)."}]},"client_visible":{"description":"Wave 41 lane 12: whether the portal shows this status's own label to a\nclient. Absent = unchanged.","type":["boolean","null"]},"is_entry":{"type":["boolean","null"]},"name":{"type":["string","null"]},"sort_order":{"format":"int32","type":["integer","null"]},"wip_limit":{"$ref":"#/components/schemas/WipLimitUpdate","description":"WIP cap (0089): absent = unchanged; JSON `null` = CLEAR the cap; a\nnumber = set it (1..=1000 — the DB CHECK is the backstop)."}},"type":"object"},"UpdateStockItemRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"active":{"description":"Reactivating an archived item is a plain update.","type":["boolean","null"]},"catalog_item_id":{"format":"uuid","type":["string","null"]},"is_tool":{"description":"Flag/unflag the item as a tool (it must be serialized to flag).","type":["boolean","null"]},"kind":{"type":["string","null"]},"name":{"type":["string","null"]},"part_number":{"type":["string","null"]},"sku":{"type":["string","null"]},"unit":{"type":["string","null"]}},"type":"object"}],"description":"PATCH /api/stock-items/{id}. Absent fields keep their value;\n`Some(None)` clears an optional field."},"UpdateStockLocationRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"active":{"type":["boolean","null"]},"assigned_user_id":{"format":"uuid","type":["string","null"]},"code":{"type":["string","null"]},"kind":{"type":["string","null"]},"name":{"type":["string","null"]}},"type":"object"}]},"UpdateTagRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"color":{"description":"Some → set (null clears); absent → no change.","type":["string","null"]},"name":{"type":["string","null"]}},"type":"object"}]},"UpdateTaxonomyNodeRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key + version."},{"properties":{"description":{"type":["string","null"]},"name":{"type":"string"},"sort_order":{"format":"int32","type":"integer"}},"required":["name"],"type":"object"}]},"UpdateTemplateRequest":{"description":"PUT /api/imports/templates/{id} request — all fields optional.","properties":{"entity_kind":{"$ref":"#/components/schemas/EntityKind"},"mapping":{"type":"object"},"name":{"type":["string","null"]},"source_system":{"type":["string","null"]}},"required":["entity_kind","mapping"],"type":"object"},"UpdateTimeSettingsRequest":{"properties":{"eod_nudge_hour":{"format":"int32","type":"integer"},"eod_nudges_enabled":{"type":"boolean"},"time_gap_min_minutes":{"format":"int32","type":"integer"}},"required":["eod_nudges_enabled","eod_nudge_hour","time_gap_min_minutes"],"type":"object"},"UpdateVendorRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"account_number":{"type":["string","null"]},"active":{"type":["boolean","null"]},"code":{"type":["string","null"]},"default_currency":{"type":["string","null"]},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"notes":{"type":["string","null"]},"phone":{"type":["string","null"]},"website":{"type":["string","null"]}},"type":"object"}]},"UpdateWebhookRequest":{"properties":{"enabled":{"type":["boolean","null"]},"event_types":{"items":{"type":"string"},"type":["array","null"]},"url":{"type":["string","null"]}},"type":"object"},"UpdateWorkflowDocumentRequest":{"properties":{"definition":{"description":"Replacing the document advances the revision by one."},"enabled":{"type":["boolean","null"]},"name":{"type":["string","null"]},"subject_kind":{"type":["string","null"]},"trigger_event":{"type":["string","null"]}},"type":"object"},"UpsertEdgeSourceRequest":{"properties":{"audience":{"description":"PINNED audience — the assertion's `aud` must equal this exactly\n(the Access application's AUD tag).","type":"string"},"display_name":{"type":"string"},"enabled":{"description":"Deny-by-default: omitted or false creates/keeps the source\nDISABLED. Only an explicit admin `true` enables authentication.","type":"boolean"},"issuer":{"description":"PINNED issuer — the assertion's `iss` must equal this exactly.\nFor Cloudflare Access: `https://<team>.cloudflareaccess.com`.","type":"string"},"jwks_url":{"description":"Team JWKS URL the assertion signature is verified against.","type":"string"}},"required":["display_name","issuer","audience","jwks_url"],"type":"object"},"UpsertMemberRequest":{"properties":{"role":{"$ref":"#/components/schemas/BoardRole"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","role"],"type":"object"},"UpsertPrecedenceRequest":{"properties":{"field_key":{"type":"string"},"layout_id":{"format":"uuid","type":["string","null"]},"priority":{"format":"int32","type":"integer"},"semantic_class":{"type":["string","null"]},"source_system":{"type":"string"}},"required":["field_key","source_system","priority"],"type":"object"},"UpsertProviderRequest":{"properties":{"client_id":{"type":"string"},"client_secret":{"description":"Write-only: stored encrypted at rest, never returned.","type":"string"},"discovery_url":{"description":"Typically `{issuer}/.well-known/openid-configuration`.","type":"string"},"display_name":{"type":"string"},"enabled":{"type":"boolean"},"issuer":{"type":"string"},"jit_provisioning":{"type":"boolean"},"preset":{"description":"Declarative first-party preset (`google` | `entra` | `okta`).\nWhen set, issuer + discovery_url are DERIVED from the preset\ntemplate + [`Self::preset_params`]; request values, when also\npresent, must match the derivation exactly (mix-up defense).","type":["string","null"]},"preset_params":{"additionalProperties":{"type":"string"},"description":"Template parameters for the preset (e.g. `tenant` for Entra,\n`domain` for Okta).","propertyNames":{"type":"string"},"type":["object","null"]}},"required":["display_name","issuer","discovery_url","client_id","client_secret"],"type":"object"},"UpsertWorkTypeMapRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"},{"properties":{"category_slug":{"type":"string"},"legacy_value":{"type":"string"}},"required":["legacy_value","category_slug"],"type":"object"}]},"UserSkillsMutationResponse":{"allOf":[{"$ref":"#/components/schemas/UserSkillsView"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}],"description":"The full-replace mutation envelope (IF2)."},"UserSkillsView":{"properties":{"skills":{"items":{"$ref":"#/components/schemas/SkillView"},"type":"array"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","skills"],"type":"object"},"ValueView":{"description":"One effective value: registry default overlaid by any stored override.","properties":{"key":{"type":"string"},"value":{"description":"JSON payload typed by the declaration's kind."}},"required":["key","value"],"type":"object"},"VaultEntryView":{"description":"A vault entry as the asset detail page sees it: metadata only —\nreveal rides the audited `/passwords/{id}/reveal` endpoint.","properties":{"expires_at":{"format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"title":{"type":"string"},"url":{"type":["string","null"]},"username":{"type":"string"}},"required":["id","title","username"],"type":"object"},"VelocityMutationless":{"properties":{"avg_daily_milli":{"description":"Average daily outbound rate in milli over the window.","format":"int64","type":"integer"},"consumed_milli":{"format":"int64","type":"integer"},"item_name":{"type":"string"},"stock_item_id":{"format":"uuid","type":"string"},"unit":{"type":"string"}},"required":["stock_item_id","item_name","unit","consumed_milli","avg_daily_milli"],"type":"object"},"VelocityQuery":{"properties":{"item_id":{"format":"uuid","type":["string","null"]},"limit":{"format":"int64","type":["integer","null"]},"window_days":{"description":"Lookback window in days (default 30; outbound legs summed).","format":"int64","type":["integer","null"]}},"type":"object"},"Vendor":{"description":"One org-owned supplier/reseller.","properties":{"account_number":{"description":"The vendor's account number for THIS org.","type":["string","null"]},"active":{"type":"boolean"},"code":{"type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"default_currency":{"description":"ISO 4217 default for POs to this vendor.","type":"string"},"email":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"notes":{"type":["string","null"]},"org_id":{"format":"uuid","type":"string"},"phone":{"type":["string","null"]},"updated_at":{"format":"date-time","type":"string"},"website":{"type":["string","null"]}},"required":["id","org_id","name","default_currency","active","created_at","updated_at"],"type":"object"},"VendorContact":{"description":"A typed contact on a vendor.","properties":{"created_at":{"format":"date-time","type":"string"},"email":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_primary":{"type":"boolean"},"name":{"type":"string"},"org_id":{"format":"uuid","type":"string"},"phone":{"type":["string","null"]},"role":{"type":["string","null"]},"updated_at":{"format":"date-time","type":"string"},"vendor_id":{"format":"uuid","type":"string"}},"required":["id","org_id","vendor_id","name","is_primary","created_at","updated_at"],"type":"object"},"VendorContactInput":{"properties":{"email":{"type":["string","null"]},"is_primary":{"type":"boolean"},"name":{"type":"string"},"phone":{"type":["string","null"]},"role":{"type":["string","null"]}},"type":"object"},"VendorDetail":{"description":"Vendor detail: the entity plus its typed contacts and external links.","properties":{"contacts":{"items":{"$ref":"#/components/schemas/VendorContact"},"type":"array"},"links":{"items":{"$ref":"#/components/schemas/ExternalLink"},"type":"array"},"vendor":{"$ref":"#/components/schemas/Vendor"}},"required":["vendor","contacts","links"],"type":"object"},"VendorMutationResponse":{"allOf":[{"$ref":"#/components/schemas/Vendor"},{"properties":{"mutation":{"$ref":"#/components/schemas/MutationMeta"}},"required":["mutation"],"type":"object"}]},"VerdictResponse":{"properties":{"applied":{"description":"False when the ledger had already recorded this fire (idempotent\nreplay after a worker retry) — nothing was written or notified.","type":"boolean"},"reason":{"type":["string","null"]}},"required":["applied"],"type":"object"},"VerificationCounters":{"description":"The workspace counters that gate the close checklist.","properties":{"adjusted":{"format":"int64","type":"integer"},"billable":{"format":"int64","type":"integer"},"draft":{"format":"int64","type":"integer"},"entries":{"format":"int64","type":"integer"},"invoiced":{"format":"int64","type":"integer"},"non_billable":{"format":"int64","type":"integer"},"rejected":{"format":"int64","type":"integer"},"unclassified":{"description":"Approved + billable + no category slug: these price at the\ncontract default rate, which is exactly the silent billing the\nclose ritual exists to catch.","format":"int64","type":"integer"},"undispositioned":{"description":"Entries nobody has dispositioned yet.","format":"int64","type":"integer"}},"required":["entries","billable","non_billable","unclassified","undispositioned","draft","rejected","invoiced","adjusted"],"type":"object"},"VerificationEntryView":{"description":"One entry inside a ticket group: identity, cost cell, badge.","properties":{"adjustment_minutes":{"description":"Signed adjustment total (negative or zero; 0 when untouched).","format":"int64","type":"integer"},"approval_status":{"description":"Approval state (draft/submitted/approved/rejected).","type":"string"},"cost_minor":{"description":"Priced cost in contract minor units at the resolved card rate.","format":"int64","type":"integer"},"disposition":{"description":"The current human disposition, when one was recorded.","type":["string","null"]},"effective_minutes":{"description":"Minutes after verification adjustments — what the run will price.","format":"int64","type":"integer"},"id":{"format":"uuid","type":"string"},"invoiced":{"description":"True when an invoice already consumed this entry (locked).","type":"boolean"},"minutes":{"description":"Raw logged minutes.","format":"int64","type":"integer"},"notes":{"description":"Free-text note on the entry.","type":["string","null"]},"phase_id":{"description":"Project phase this entry scopes to, when project-scoped.","format":"uuid","type":["string","null"]},"rate_minor":{"description":"The card rate this entry priced at, per hour.","format":"int64","type":"integer"},"started_at":{"type":"string"},"unbillable_reason":{"description":"Typed unbillable reason when the entry is non-billable.","type":["string","null"]},"user_id":{"format":"uuid","type":"string"},"user_name":{"type":"string"},"work_type":{"description":"The category slug, or null (unclassified — a blocker, not a blank).","type":["string","null"]}},"required":["id","user_id","user_name","started_at","minutes","effective_minutes","adjustment_minutes","cost_minor","rate_minor","approval_status","invoiced"],"type":"object"},"VerificationGroupView":{"description":"One ticket (or project phase) group with its running totals.","properties":{"entries":{"items":{"$ref":"#/components/schemas/VerificationEntryView"},"type":"array"},"group_id":{"description":"Stable grouping key: the ticket id, or the phase id for\nproject-scoped entries (Wave 39 lane 4 gave entries a second\nparent, so a phase group has no ticket number).","format":"uuid","type":"string"},"label":{"description":"Human label: ticket number, else \"Project / phase\".","type":"string"},"project_name":{"type":["string","null"]},"total_cost_minor":{"description":"Group cost in contract minor units.","format":"int64","type":"integer"},"total_minutes":{"description":"Group minutes after adjustments.","format":"int64","type":"integer"}},"required":["group_id","label","entries","total_minutes","total_cost_minor"],"type":"object"},"VerificationWorkspace":{"description":"The full workspace response.","properties":{"blocks":{"description":"The block-consumption panel (empty when the contract has no\nblocks — retainer overage panels render nothing, not a zero).","items":{"$ref":"#/components/schemas/BlockPanelView"},"type":"array"},"client_id":{"format":"uuid","type":"string"},"client_name":{"type":["string","null"]},"contract_id":{"format":"uuid","type":"string"},"contract_name":{"type":"string"},"contract_number":{"type":"string"},"contract_type":{"type":"string"},"counters":{"$ref":"#/components/schemas/VerificationCounters"},"currency":{"type":"string"},"from":{"format":"date","type":"string"},"groups":{"items":{"$ref":"#/components/schemas/VerificationGroupView"},"type":"array"},"overlapping_runs":{"description":"Issued runs whose window overlaps this one (report V8).","items":{"$ref":"#/components/schemas/RunWindowView"},"type":"array"},"preview_total_minor":{"description":"What the engine says this window would bill. The workspace's\nown entry sum and this are computed independently on purpose:\nthey must agree, and a disagreement is the signal that a\ndisposition was missed.","format":"int64","type":"integer"},"signoff":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SignoffView","description":"The sign-off recorded for this window, if any."}]},"to":{"format":"date","type":"string"},"total_minor":{"description":"Running total across the whole window, in contract minor units.","format":"int64","type":"integer"},"total_minutes":{"description":"Running total minutes across the window (effective).","format":"int64","type":"integer"}},"required":["contract_id","contract_number","contract_name","contract_type","client_id","currency","from","to","groups","counters","total_minor","total_minutes","blocks","preview_total_minor","overlapping_runs"],"type":"object"},"VerifyCustomDomainResponse":{"description":"POST `/verify` response: the raw evidence plus the resulting status.","properties":{"domain":{"type":"string"},"edge":{"$ref":"#/components/schemas/EdgeCheck"},"org_id":{"format":"uuid","type":"string"},"record_name":{"description":"The `_marula-verify.<domain>` name that was queried.","type":"string"},"status":{"description":"`pending` or `verified` (never `none` — verify requires a domain).","type":"string"},"txt":{"$ref":"#/components/schemas/TxtCheck"},"verified":{"type":"boolean"}},"required":["org_id","domain","verified","status","record_name","txt","edge"],"type":"object"},"VerifyPublisherRequest":{"properties":{"display_name":{"type":"string"},"note":{"type":["string","null"]},"publisher":{"description":"GitHub `owner/repo` URL or bare path, `registry://slug`, or\na host (zips) — normalized server-side to the publisher identity.","type":"string"}},"required":["publisher","display_name"],"type":"object"},"VerifyStatus":{"properties":{"email_verified":{"type":"boolean"}},"required":["email_verified"],"type":"object"},"ViewId":{"description":"Versioned read-model views. Variant ↔ physical view name is frozen\nalongside the schema-version suffix; adding a view is additive.\n\nThe serde encoding is the view SLUG (`snake_case`) — the same string the\nstored-spec `kind` column carries (R3 registry backing) — so a definition\nround-trips through JSON without a parallel name table.","enum":["tickets","time_entries","sla_outcomes","gap_days","first_response"],"type":"string"},"ViewListQuery":{"properties":{"project_id":{"description":"Scope to one project's views; omit for the org's ENTIRE view set.","format":"uuid","type":["string","null"]}},"type":"object"},"ViewMutationResponse":{"allOf":[{"$ref":"#/components/schemas/ProjectView"}]},"VizHint":{"description":"Visualization hint the report builder renders with (ADR 0001: Recharts\nbar/line/pie/table on the frontend). Pure presentation metadata — it\nnever influences the compiled SQL.","enum":["bar","line","pie","table"],"type":"string"},"VoidCreditNoteRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams"}],"description":"POST /api/credit-notes/{id}/void body."},"VoidInvoiceRequest":{"allOf":[{"$ref":"#/components/schemas/MutationParams","description":"Instant-first autosave parameters (IF1): idempotency key +\nIf-Match `version`."}]},"VoipCallLogRequest":{"properties":{"call_id":{"description":"Provider call id (idempotency — required).","type":"string"},"direction":{"description":"`in` | `out`.","type":"string"},"duration_seconds":{"format":"int32","type":"integer"},"external_number":{"type":"string"},"occurred_at":{"format":"date-time","type":"string"},"summary":{"type":["string","null"]},"ticket_id":{"format":"uuid","type":["string","null"]}},"required":["call_id","direction","external_number","duration_seconds","occurred_at"],"type":"object"},"VoipCallLogResponse":{"properties":{"id":{"format":"uuid","type":"string"}},"required":["id"],"type":"object"},"WarrantyState":{"description":"Which side of the warranty horizon an asset sits on.","enum":["active","ending","ended"],"type":"string"},"WarrantyWindow":{"description":"The warranty horizon for one device asset: the end date, which side of it\ntoday falls, and how many whole days are left (negative once ended).","properties":{"days_remaining":{"format":"int64","type":"integer"},"ends_on":{"format":"date","type":"string"},"state":{"$ref":"#/components/schemas/WarrantyState"}},"required":["state","ends_on","days_remaining"],"type":"object"},"WebauthnBeginResponse":{"properties":{"challenge_token":{"description":"Opaque ceremony binding — pass back to finish.","type":"string"},"public_key":{"description":"CreationOptions for navigator.credentials.create()/get().","type":"object"}},"required":["public_key","challenge_token"],"type":"object"},"WebauthnCredentialInfo":{"properties":{"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"label":{"type":"string"},"last_used_at":{"format":"date-time","type":["string","null"]}},"required":["id","label","created_at"],"type":"object"},"WebauthnLoginFinishRequest":{"properties":{"challenge_token":{"type":"string"},"credential":{"description":"The PublicKeyCredential from navigator.credentials.get().","type":"object"}},"required":["challenge_token","credential"],"type":"object"},"WebauthnRegisterFinishRequest":{"properties":{"challenge_token":{"type":"string"},"credential":{"description":"The PublicKeyCredential from navigator.credentials.create().","type":"object"},"label":{"description":"Label shown on the security settings page.","type":["string","null"]}},"required":["challenge_token","credential"],"type":"object"},"WebauthnRemoveRequest":{"properties":{"password":{"type":"string"}},"required":["password"],"type":"object"},"WebhookList":{"properties":{"webhooks":{"items":{"$ref":"#/components/schemas/WebhookView"},"type":"array"}},"required":["webhooks"],"type":"object"},"WebhookSecretResponse":{"description":"Returned by create ONLY — carries the signing secret in plaintext one\nfinal time. There is deliberately no read-back path.","properties":{"secret":{"type":"string"},"webhook":{"$ref":"#/components/schemas/WebhookView"}},"required":["webhook","secret"],"type":"object"},"WebhookView":{"properties":{"consecutive_failures":{"description":"Retryable delivery failures since the last delivery this destination\naccepted. Visible here because it is the number that predicts a pause:\nthe operator fixing a receiver wants to see it climbing before the\ndestination disappears from the delivery set.","format":"int32","type":"integer"},"created_at":{"format":"date-time","type":"string"},"created_by":{"format":"uuid","type":["string","null"]},"enabled":{"type":"boolean"},"event_types":{"description":"Empty array = wildcard (all event types).","items":{"type":"string"},"type":"array"},"id":{"format":"uuid","type":"string"},"paused_at":{"description":"Set while the destination is paused: new events are not matched for it,\nso nothing new is queued until it is resumed (or its URL changes, which\nis a different destination).","format":"date-time","type":["string","null"]},"paused_reason":{"type":["string","null"]},"url":{"type":"string"}},"required":["id","url","event_types","enabled","created_at","consecutive_failures"],"type":"object"},"WipLimitUpdate":{"description":"WIP-cap patch input (0089): `Default` (field absent) = unchanged;\nJSON null = clear; a number = set. serde's `Option<Option>` mapping\nmakes a present-but-null field UNREPRESENTABLE from JSON (null folds\nto the outer None), so the three states ride an explicit enum.","oneOf":[{"enum":["Unchanged"],"type":"string"},{"enum":["Clear"],"type":"string"},{"properties":{"Set":{"format":"int32","type":"integer"}},"required":["Set"],"type":"object"}]},"WorkNextEntryView":{"description":"One row of a technician's curated queue, as the board renders it.","properties":{"client_id":{"format":"uuid","type":["string","null"]},"client_name":{"type":["string","null"]},"created_at":{"type":"string"},"id":{"format":"uuid","type":"string"},"position":{"format":"int32","type":"integer"},"priority":{"type":"string"},"status":{"type":"string"},"ticket_id":{"format":"uuid","type":"string"},"ticket_number":{"type":"string"},"title":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","ticket_id","position","ticket_number","title","priority","status","created_at","updated_at"],"type":"object"},"WorkNextMutation":{"properties":{"position":{"description":"Target rank. Clamped server-side to the queue's bounds: dragging past\nthe end is a gesture, not a client bug.","format":"int32","type":"integer"},"ticket_id":{"format":"uuid","type":"string"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","ticket_id","position"],"type":"object"},"WorkNextQueueQuery":{"properties":{"user_id":{"description":"Whose queue to read. Admins may read another technician's (a\ndispatcher setting up a shift); anyone may read their own, and a\nnon-admin asking for someone else is refused rather than silently\nserved their own.","format":"uuid","type":["string","null"]}},"type":"object"},"WorkNextQueueView":{"description":"The whole queue for one technician, in curated order.","properties":{"display_name":{"description":"The technician whose queue this is, resolved server-side from the\nauthenticated principal unless an admin asks for someone else's.","type":"string"},"entries":{"items":{"$ref":"#/components/schemas/WorkNextEntryView"},"type":"array"},"user_id":{"format":"uuid","type":"string"}},"required":["user_id","display_name","entries"],"type":"object"},"WorkNextRemovalQuery":{"properties":{"ticket_id":{"format":"uuid","type":"string"},"user_id":{"description":"Absent means \"every technician's queue\", which is what a placed or\nclosed ticket needs: its queue entries are stale everywhere at once.","format":"uuid","type":["string","null"]}},"required":["ticket_id"],"type":"object"},"WorkTypeMapRowWire":{"properties":{"category_slug":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"legacy_value":{"type":"string"},"source":{"description":"`backfill` (the one-time migration mapping) or `manual` (an\nadmin alias added later).","type":"string"}},"required":["legacy_value","category_slug","source","created_at"],"type":"object"},"WorkTypeSlice":{"properties":{"minutes":{"format":"int64","type":"integer"},"work_type":{"description":"`null` = unclassified entries.","type":["string","null"]}},"required":["minutes"],"type":"object"},"WorkbenchDecision":{"description":"The suggestion-grade `decision_record_dedupe` judgment for this\ncandidate, read from the append-only decision store. `None` (and\ntherefore silent in the UI) whenever the decision model is\nunconfigured — ADR 0010's degrade-quiet posture, never a 5xx.","properties":{"answers":{},"mode":{"type":"string"},"model":{"type":"string"},"provider":{"type":"string"},"recorded_at":{"format":"date-time","type":"string"},"surface":{"type":"string"}},"required":["surface","mode","provider","model","recorded_at","answers"],"type":"object"},"WorkbenchFieldRow":{"properties":{"candidate_curated":{},"candidate_observed":{},"candidate_source":{},"key":{"type":"string"},"primary_curated":{},"primary_observed":{},"primary_source":{}},"required":["key"],"type":"object"},"WorkbenchSide":{"allOf":[{"$ref":"#/components/schemas/ConfigItemView"},{"properties":{"curated_fields":{},"field_sources":{},"identity_keys":{},"observed_fields":{}},"required":["observed_fields","curated_fields","field_sources","identity_keys"],"type":"object"}],"description":"One side of the workbench comparison: the item view plus its FULL tier\nstate (observed/curated/field_sources) and identity keys."},"WorkflowDocumentListView":{"properties":{"documents":{"items":{"$ref":"#/components/schemas/WorkflowDocumentView"},"type":"array"}},"required":["documents"],"type":"object"},"WorkflowDocumentView":{"description":"A workflow document as the canvas reads it. `revision` is the document\nversion the executor records on each run.","properties":{"created_by":{"format":"uuid","type":["string","null"]},"definition":{"type":"object"},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"revision":{"format":"int32","type":"integer"},"subject_kind":{"description":"`\"ticket\"` or `\"board\"` — the tier of subject the document runs against.","type":"string"},"trigger_event":{"type":"string"}},"required":["id","name","enabled","trigger_event","revision","subject_kind","definition"],"type":"object"},"WorkflowRetryMode":{"description":"How far back a retry re-executes.","enum":["resume","from_start"],"type":"string"},"WorkflowRunStatus":{"description":"What one execution attempt is doing. `Waiting` is the parked state and\nis as terminal-as-nothing: it is waiting for a clock or an event, not\nfor a worker.","enum":["running","waiting","succeeded","failed","abandoned"],"type":"string"},"WorkflowRunTrigger":{"description":"Why a run row exists. The two chain kinds are pinned to their pointer\nin the database (`workflow_runs_trigger_shape`), so a row can never\nclaim to be a resume without naming the run it continues.","enum":["event","resume","operator_retry","manual"],"type":"string"},"WorkflowStepStatus":{"description":"What one step attempt is doing. Wider than the run's because a step can\nbe SKIPPED: a branch not taken is a recorded fact, not a step that\nnever happened.","enum":["running","waiting","succeeded","failed","abandoned","skipped"],"type":"string"}}},"info":{"description":"The single write surface for Project Marula. All mutations flow through this API and emit domain events.","license":{"identifier":"AGPL-3.0-or-later","name":"AGPL-3.0-or-later"},"title":"Marula Core API","version":"0.3.1"},"openapi":"3.1.0","paths":{"/api/v1/admin/dead-work":{"get":{"operationId":"dead_work_list","parameters":[{"description":"Restrict to one ledger: `job`, `email`, `webhook` or `event`.","in":"query","name":"source","required":false,"schema":{"type":["string","null"]}},{"description":"Include rows an operator already cancelled. Off by default: the list\nexists to show what still needs a decision.","in":"query","name":"include_cancelled","required":false,"schema":{"type":["boolean","null"]}},{"description":"Page size, clamped to 1..=200.","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}},{"description":"Rows to skip, from a previous page's `next_offset`.","in":"query","name":"offset","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeadWorkPage"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/admin/dead-work — every ledger's abandoned work, newest first.","tags":["dead_work"]}},"/api/v1/admin/dead-work/destinations":{"get":{"description":"Paused first, then the ones closest to pausing: the operator question is\n\"what is suppressed, and what is about to be\".","operationId":"dead_work_destinations","parameters":[{"description":"Only destinations that are currently paused.","in":"query","name":"paused_only","required":false,"schema":{"type":["boolean","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/DestinationView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/admin/dead-work/destinations — webhook destinations and their\nfailure counters.","tags":["dead_work"]}},"/api/v1/admin/dead-work/destinations/{id}/resume":{"post":{"description":"Also resets the counter, so the destination gets a full budget of failures\nbefore it pauses again rather than pausing on the next one. The operator\nfixed something; the evidence that it was broken is history, not a debt.","operationId":"dead_work_resume_destination","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/admin/dead-work/destinations/{id}/resume — clear a pause.","tags":["dead_work"]}},"/api/v1/admin/dead-work/{source}/{id}/cancel":{"post":{"description":"For a job (including a webhook delivery job) the row also moves to `failed`,\nso the dead depth an alert reads actually falls. For email and dead events\nthe ledger state stays and the decision is this table's row: see\n`core_db::dead_work::cancel` for why that asymmetry is deliberate.","operationId":"dead_work_cancel","parameters":[{"in":"path","name":"source","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelDeadWorkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/admin/dead-work/{source}/{id}/cancel — record that this work is\nnot wanted.","tags":["dead_work"]}},"/api/v1/admin/dead-work/{source}/{id}/redrive":{"post":{"description":"The row is requeued in place: a webhook job keeps its dedupe key, so the\nreplay cannot deliver the same `(webhook, event)` twice, and the attempt\nbudget restarts so the retry is real. A repeat call is `not_dead` and writes\nno second audit row.","operationId":"dead_work_redrive","parameters":[{"in":"path","name":"source","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedriveResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/admin/dead-work/{source}/{id}/redrive — requeue one item.","tags":["dead_work"]}},"/api/v1/admin/quarantine":{"get":{"description":"`pending` are still held; `released` were admitted by an operator and keep\ntheir row so the override stays visible.","operationId":"quarantine_admin_list","parameters":[{"description":"Page size. Clamped to 1..=200.","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}},{"description":"Rows to skip, from a previous page's `next_offset`.","in":"query","name":"offset","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuarantinePage"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/admin/quarantine — the org's quarantined objects, newest first.","tags":["quarantine"]}},"/api/v1/admin/quarantine/{sha256}":{"delete":{"description":"The row goes with them and the fact stays in the audit trail. A released\nobject is refused (409): its bytes are admitted and its trail is evidence.","operationId":"quarantine_admin_delete","parameters":[{"in":"path","name":"sha256","required":true,"schema":{"type":"string"}},{"description":"Optional note for the audit trail. The default sentence names the\naction when the operator does not add one.","in":"query","name":"reason","required":false,"schema":{"type":["string","null"]}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/admin/quarantine/{sha256} — remove the held bytes.","tags":["quarantine"]},"get":{"operationId":"quarantine_admin_get","parameters":[{"in":"path","name":"sha256","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuarantineItemView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/admin/quarantine/{sha256} — one held or released object.","tags":["quarantine"]}},"/api/v1/admin/quarantine/{sha256}/release":{"post":{"description":"The bytes move from the quarantine key to the canonical attachment key,\nthe row records actor + reason + timestamp, and an `audit_events` row is\nwritten in the same transaction. Repeating the call returns the first\nrelease unchanged and writes no second audit row.","operationId":"quarantine_admin_release","parameters":[{"in":"path","name":"sha256","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseQuarantineRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuarantineItemView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/admin/quarantine/{sha256}/release — admit the object anyway.","tags":["quarantine"]}},"/api/v1/agent-proposals":{"get":{"operationId":"agent_proposals_list","parameters":[{"description":"Filter by lifecycle state.","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}},{"description":"Opaque keyset cursor from a previous page's `next_cursor`.","in":"query","name":"cursor","required":false,"schema":{"type":["string","null"]}},{"description":"Page size, clamped to [1, 200].","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentProposalPage"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/agent-proposals — cursor-paged listing, newest first.","tags":["agent-platform"]},"post":{"operationId":"agent_proposals_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProposalRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProposalResponse"}}},"description":""},"403":{"description":"capability not granted (deny-by-default)"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/agent-proposals — file a proposal (IF8). Accepts human-JWT\ncallers for administrative simulation, but the API-key/machine path is\nthe intended caller: the machine principal's key id becomes the\nagent_ref when none is supplied. The dial position is captured from\nthe capability's grant row at creation time.","tags":["agent-platform"]}},"/api/v1/agent-proposals/{id}":{"get":{"operationId":"agent_proposals_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentProposalView"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/agent-proposals/{id}.","tags":["agent-platform"]}},"/api/v1/agent-proposals/{id}/approve":{"post":{"operationId":"agent_proposals_approve","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecideProposalRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentProposalView"}}},"description":""},"403":{"description":"not a permitted approver"},"404":{"description":"not in scope"},"409":{"description":"proposal already decided"}},"security":[{"bearer":[]}],"summary":"POST /api/agent-proposals/{id}/approve — decide FOR. Approved ticket\nupdate proposals apply immediately through the IF1 instant-first path\nin the SAME transaction (idempotency-keyed, version-gated, write-trail\nrow — stays undoable); other entity/op combinations stay approved-\npending until the write-execution lanes widen application.","tags":["agent-platform"]}},"/api/v1/agent-proposals/{id}/decline":{"post":{"operationId":"agent_proposals_decline","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecideProposalRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentProposalView"}}},"description":""},"403":{"description":"not a permitted approver"},"404":{"description":"not in scope"},"409":{"description":"proposal already decided"}},"security":[{"bearer":[]}],"summary":"POST /api/agent-proposals/{id}/decline — decide AGAINST. A declined\nproposal produces NO write at all; the row is the audit record.","tags":["agent-platform"]}},"/api/v1/agent-proposals/{id}/withdraw":{"post":{"operationId":"agent_proposals_withdraw","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentProposalView"}}},"description":""},"403":{"description":"only the proposing agent or an admin may withdraw"},"404":{"description":"not in scope"},"409":{"description":"proposal already decided"}},"security":[{"bearer":[]}],"summary":"POST /api/agent-proposals/{id}/withdraw — the proposing agent (or an\nadmin) cancels its own open proposal. No write ever happened.","tags":["agent-platform"]}},"/api/v1/agent-skills":{"get":{"operationId":"agent_skills_list","parameters":[{"description":"When present, every version of this slug (newest first) instead of\nthe latest version of each slug.","in":"query","name":"slug","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSkillList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/v1/agent-skills — latest version per slug (or one slug's history).","tags":["agent-platform"]},"post":{"operationId":"agent_skills_publish","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishSkillRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSkillView"}}},"description":""},"403":{"description":"owner/admin surface"},"422":{"description":"invalid slug or manifest"}},"security":[{"bearer":[]}],"summary":"POST /api/v1/agent-skills — publish a NEW version of a skill.","tags":["agent-platform"]}},"/api/v1/agent-skills/{id}":{"get":{"operationId":"agent_skills_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSkillView"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/v1/agent-skills/{id} — one immutable version.","tags":["agent-platform"]}},"/api/v1/ai/chat/sessions/{session_id}/turns":{"post":{"operationId":"chat_runner_turn_post","parameters":[{"in":"path","name":"session_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatTurnRequest"}}},"required":true},"responses":{"200":{"description":"SSE stream of loop frames (text/event-stream)"},"400":{"description":"validation"},"403":{"description":"typed refusal: ai_surface_not_granted | ai_surface_disabled | ai_surface_internal | ai_model_not_allowed | ai_no_models_configured | ai_hard_cap_reached | ai_no_credential | ai_agent_capability_not_granted"},"429":{"description":"chat turn rate limit"}},"security":[{"bearer":[]}],"summary":"POST /ai/chat/sessions/{session_id}/turns — run ONE bounded model turn\non a granted surface, streaming loop frames over `text/event-stream`.\nRefusals (surface/model/cap/credential/agent capability), the hard cap\nand the rate limit answer typed BEFORE any stream begins — clients\nbranch on codes without parsing frames.","tags":["ai"]}},"/api/v1/ai/me/authorize":{"post":{"operationId":"ai_my_authorize_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiAuthorizeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiAuthorizeResponse"}}},"description":""},"403":{"description":"typed refusal: ai_surface_not_granted | ai_surface_disabled | ai_surface_internal | ai_model_not_allowed | ai_model_not_capable | ai_no_models_configured | ai_hard_cap_reached | ai_no_credential"},"404":{"description":"rls_sparse"}},"security":[{"bearer":[]}],"summary":"POST /ai/me/authorize — the completion-edge resolution (typed refusals;\ndegrade-never-5xx), delegating to the shared pipeline.","tags":["ai"]}},"/api/v1/ai/me/credentials":{"get":{"operationId":"ai_my_credentials_get","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AiCredentialDto"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /ai/me/credentials — the caller's personal credential summaries.","tags":["ai"]},"put":{"operationId":"ai_my_credential_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutAiCredentialRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiCredentialMutationResponse"}}},"description":""},"403":{"description":"personal credentials disabled by policy"},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"PUT /ai/me/credentials — seal a PERSONAL credential (flag-gated).","tags":["ai"]}},"/api/v1/ai/me/credentials/{credential_id}":{"delete":{"operationId":"ai_my_credential_delete","parameters":[{"in":"path","name":"credential_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiCredentialMutationResponse"}}},"description":""},"403":{"description":""},"404":{"description":"rls_sparse"}},"security":[{"bearer":[]}],"summary":"DELETE /ai/me/credentials/{credential_id} — revoke a PERSONAL credential.","tags":["ai"]}},"/api/v1/ai/me/model-picks/{surface_key}":{"delete":{"operationId":"ai_my_pick_delete","parameters":[{"in":"path","name":"surface_key","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiPickMutationResponse"}}},"description":""},"403":{"description":""},"404":{"description":"rls_sparse"}},"security":[{"bearer":[]}],"summary":"DELETE /ai/me/model-picks/{surface_key} — clears the sticky pick.","tags":["ai"]},"put":{"operationId":"ai_my_pick_put","parameters":[{"in":"path","name":"surface_key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutAiPickRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiPickMutationResponse"}}},"description":""},"403":{"description":""},"404":{"description":"rls_sparse"},"422":{"description":"model not in the admin allowlist"}},"security":[{"bearer":[]}],"summary":"PUT /ai/me/model-picks/{surface_key} — a sticky pick; the pair must sit\ninside the admin allowlist or the pick is typed-refused.","tags":["ai"]}},"/api/v1/ai/me/surfaces":{"get":{"operationId":"ai_my_surfaces_get","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiMySurfacesResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /ai/me/surfaces — which surfaces the CALLER may use\n(deny-by-default: only granted, enabled, non-internal rows surface).","tags":["ai"]}},"/api/v1/ai/me/usage":{"get":{"operationId":"ai_my_usage_get","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiMyUsageResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /ai/me/usage — personal progress against caps.","tags":["ai"]}},"/api/v1/alert-sources":{"get":{"operationId":"alert_sources_list","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertSourceList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/alert-sources — newest-first config list (no secrets).","tags":["alerts"]},"post":{"operationId":"alert_sources_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAlertSourceRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertSourceMutationResponse"}}},"description":""},"409":{"description":"source_ref collision or idempotency key reused"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/alert-sources — creates an ingest source, generates + seals\nits HMAC secret (returned ONCE), and writes the audit trail.\nInstant-first (IF1/IF2): idempotency-keyed, read-your-write response.","tags":["alerts"]}},"/api/v1/alert-sources/{id}":{"patch":{"operationId":"alert_sources_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchAlertSourceRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertSourceMutationResponse"}}},"description":""},"404":{"description":"source not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"PATCH /api/alert-sources/{id} — re-point the board, rename, resize the\nstorm window, or disable. Instant-first (IF1/IF2).","tags":["alerts"]}},"/api/v1/alerts":{"get":{"operationId":"alerts_list","parameters":[{"in":"query","name":"source_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"ticket_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Triage filter (0089): \"open\" | \"acknowledged\" | \"resolved\" —\nabsent returns all states.","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"in":"query","name":"offset","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertListResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/alerts — newest-first ledger browse, filterable by source and\nincident ticket (staff, ticket-view scoped).","tags":["alerts"]}},"/api/v1/alerts/ingest/{source_ref}":{"post":{"description":"Responses: 200 with a terminal `status` (incident_created / collapsed /\nduplicate); 401 missing/bad signature; 403 disabled source; 404 unknown\nsource_ref; 409 source is a generic M2M receiver (its profile emits\nenvelopes, so the answer names `/api/webhooks/ingest/{source_ref}`);\n422 malformed payload.","operationId":"alerts_ingest","parameters":[{"description":"Public ingest routing id of an alert source","in":"path","name":"source_ref","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngestAlertRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngestResponse"}}},"description":""},"401":{"description":"missing or invalid signature"},"403":{"description":"alert source disabled"},"404":{"description":"unknown source_ref"},"422":{"description":"validation failed"}},"summary":"POST /api/alerts/ingest/{source_ref} — HMAC-verified alert ingest.","tags":["alerts"]}},"/api/v1/alerts/{id}/acknowledge":{"post":{"operationId":"alerts_acknowledge","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MutationParams"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertMutationResponse"}}},"description":""},"404":{"description":"alert not visible in scope"},"409":{"description":"alert already acknowledged or resolved"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/alerts/{id}/acknowledge — triage: open → acknowledged.\nInstant-first (IF1/IF2): idempotency-keyed, read-your-write. Re-ack\n(or ack of an already-resolved alert) is a typed conflict — the state\nis honest, never silently rewritten.","tags":["alerts"]}},"/api/v1/alerts/{id}/resolve":{"post":{"operationId":"alerts_resolve","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MutationParams"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertMutationResponse"}}},"description":""},"404":{"description":"alert not visible in scope"},"409":{"description":"alert already resolved"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/alerts/{id}/resolve — triage: open/acknowledged → resolved.\nResolving straight from `open` leaves the ack fields NULL (no\nacknowledgement ever happened — the row renders that honestly).","tags":["alerts"]}},"/api/v1/api-keys":{"get":{"operationId":"api_keys_list","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/api-keys — listing without secrets.","tags":["integrations"]},"post":{"operationId":"api_keys_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreatedResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/api-keys — creates a key and returns the raw secret ONCE.","tags":["integrations"]}},"/api/v1/api-keys/{id}":{"delete":{"operationId":"api_keys_revoke","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/api-keys/{id} — immediate revocation (rotation story:\ncreate replacement → revoke old).","tags":["integrations"]}},"/api/v1/appointment-requests":{"get":{"operationId":"appointment_requests_list","parameters":[{"description":"Exact status: `pending | approved | declined | cancelled`.","in":"query","name":"status","required":false,"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AppointmentRequestStatus"}]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"in":"query","name":"offset","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AppointmentRequest"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/appointment-requests — the staff queue (optionally narrowed\nby status), newest first.","tags":["scheduling"]}},"/api/v1/appointment-requests/{id}/approve":{"post":{"operationId":"appointment_requests_approve","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecideAppointmentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecideResponse"}}},"description":""},"404":{"description":"request not found in scope"},"409":{"description":"not pending, slot no longer free, double-booking backstop, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/appointment-requests/{id}/approve — book the requested slot\non the staff member's calendar. Re-validates availability + conflicts\nat decision time (the client's listing may be stale); a slot that\nstopped being free is a typed 409, never a wedged booking. Emits BOTH\n`appointment.created` (the booking enters the world through the\nnormal creation event) and `appointment_request.approved`.","tags":["scheduling"]}},"/api/v1/appointment-requests/{id}/decline":{"post":{"operationId":"appointment_requests_decline","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecideAppointmentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecideResponse"}}},"description":""},"404":{"description":"request not found in scope"},"409":{"description":"not pending, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/appointment-requests/{id}/decline — refuse a pending\nrequest. No calendar effect; the note reaches the client.","tags":["scheduling"]}},"/api/v1/appointments":{"post":{"operationId":"appointment_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAppointmentRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}},"description":""},"409":{"description":"slot already booked"}},"security":[{"bearer":[]}],"summary":"POST /api/appointments — create a booking (409 on overlap).","tags":["scheduling"]}},"/api/v1/appointments/{id}":{"get":{"operationId":"appointment_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/appointments/{id} — fetch one booking in the caller's org.","tags":["scheduling"]},"patch":{"operationId":"appointment_patch","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchAppointmentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /api/appointments/{id} — edit fields/status (times unchanged;\nmoves use the dedicated endpoint so clients get rescheduled events).","tags":["scheduling"]}},"/api/v1/appointments/{id}/cancel":{"post":{"operationId":"appointment_cancel","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/appointments/{id}/cancel — frees the slot (status flip, never\ndeletion, so history survives).","tags":["scheduling"]}},"/api/v1/appointments/{id}/move":{"post":{"operationId":"appointment_move","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveAppointmentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}},"description":""},"404":{"description":""},"409":{"description":"target slot already booked"}},"security":[{"bearer":[]}],"summary":"POST /api/appointments/{id}/move — reschedule with re-validation\n(dry-run semantics inline: a conflicting move is rejected atomically\nand emits a `rescheduled` event only when it lands).","tags":["scheduling"]}},"/api/v1/assets":{"get":{"operationId":"assets_list","parameters":[{"in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"client | internal_org (migration 0103).","in":"query","name":"scope","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"layout_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Substring over tag, name, and custom fields.","in":"query","name":"q","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/DeviceAsset"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/assets — org-wide, filterable (resource REST for external-DMS\nparity; the client-scoped tab narrows with `?client_id=`).","tags":["assets"]},"post":{"operationId":"assets_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAssetRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetMutationResponse"}}},"description":""},"409":{"description":"idempotency key reused"},"422":{"description":"fields rejected by the layout schema"}},"security":[{"bearer":[]}],"summary":"POST /api/assets — instant-first (IF1/IF2).","tags":["assets"]}},"/api/v1/assets/integration-links":{"get":{"operationId":"assets_integration_links","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/IntegrationLinkStatus"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/assets/integration-links — per-connector link state as data\n(QBO/Xero and friends): linked/unlinked + last-touch timestamps. No\nlive sync runs here; the `ConfigSourceLink` seam (ADR 0003) attaches\nper-asset state when it lands.","tags":["assets"]}},"/api/v1/assets/{id}":{"delete":{"operationId":"assets_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"deleted"},"404":{"description":"asset not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/assets/{id} — the audit trail keeps the events; link rows\nsurvive and surface as broken targets (docs-platform semantics: never\ncascade-deleted). Mirrors the vault's delete shape (`deleted: true`),\nwhich intentionally rides no instant-first envelope.","tags":["assets"]},"get":{"operationId":"assets_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAsset"}}},"description":""},"404":{"description":"asset not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/assets/{id}","tags":["assets"]},"patch":{"operationId":"assets_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAssetRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetMutationResponse"}}},"description":""},"404":{"description":"asset not visible in scope"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"fields rejected by the layout schema"}},"security":[{"bearer":[]}],"summary":"PATCH /api/assets/{id} — instant-first (IF1/IF2); absent = keep. A\nlayout change re-validates the effective fields against the NEW schema.","tags":["assets"]}},"/api/v1/assets/{id}/links":{"get":{"operationId":"assets_list_links","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ResolvedLink"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/assets/{id}/links — the related-items panel.","tags":["assets"]},"post":{"operationId":"assets_add_link","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAssetLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ResolvedLink"},"type":"array"}}},"description":""},"404":{"description":"asset or link target not visible in scope"},"409":{"description":"link already exists"}},"security":[{"bearer":[]}],"summary":"POST /api/assets/{id}/links","tags":["assets"]}},"/api/v1/assets/{id}/vault":{"get":{"operationId":"assets_list_vault","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/VaultEntryView"},"type":"array"}}},"description":""},"404":{"description":"asset not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/assets/{id}/vault — the asset's vault entries: password rows\nLINKED to this asset (password→asset). Strict posture: metadata only\n(title/username/url/expiry) under `passwords:view`; secrets exist only\nbehind the audited `/passwords/{id}/reveal` endpoint, which requires\n`passwords:edit` — the UI must render role-gated reveal, never a dead\nbutton. No ciphertext ever appears in this response.","tags":["assets"]}},"/api/v1/attachments/{id}":{"delete":{"operationId":"attachments_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentDeleted"}}},"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/attachments/{id} — author-gated (the uploader, or an org\nowner/admin). The stored object is reclaimed only when no sibling row\nstill references the same content-addressed key.","tags":["attachments"]}},"/api/v1/attachments/{id}/download":{"get":{"description":"The ticket is the optimisation and the stream is the contract: without the\nflag the request gets the same bytes, headers and status it always did,\nand on a deployment that cannot presign the ticket request answers\n`mode: \"stream\"` rather than failing. [`AttachmentDownloadTicket`] carries\nthe filename the stream would have carried in `content-disposition`,\nbecause a presigned GET names the object key — a digest — instead.","operationId":"attachments_download","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"`true` asks for a download ticket instead of the bytes; the answer says whether a URL was minted","in":"query","name":"presign","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentDownloadTicket"}},"application/octet-stream":{"schema":{"items":{"format":"int32","minimum":0,"type":"integer"},"type":"array"}}},"description":""},"409":{"description":"the object is held by the scan gate: neither a URL nor the bytes are issued"}},"security":[{"bearer":[]}],"summary":"GET /api/attachments/{id}/download — streams the stored bytes through the\nAPI behind board-view authz, or answers with a presigned URL when the\ncaller asks for one (`?presign=true`) and the store can sign it.","tags":["attachments"]}},"/api/v1/attachments/{id}/transcribe":{"post":{"operationId":"attachments_transcribe","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptionResponse"}}},"description":""},"403":{"description":"typed AI refusals (surface/grant/credential/cap)"},"404":{"description":""},"422":{"description":"not_audio or size cap"}},"security":[{"bearer":[]}],"summary":"POST /api/v1/attachments/{id}/transcribe — transcribe one stored\naudio attachment through the org's granted `transcription` surface.","tags":["attachments"]}},"/api/v1/audit-events":{"get":{"operationId":"audit_list","parameters":[{"description":"Legacy cursor (kept for compatibility): strictly older than this.","in":"query","name":"before","required":false,"schema":{"format":"date-time","type":["string","null"]}},{"description":"Keyset cursor from a previous page's `next_cursor`; strictly older\nthan this position (newest-first mode).","in":"query","name":"cursor","required":false,"schema":{"type":["string","null"]}},{"description":"EXPORT cursor: a previous page's `next_cursor` in ascending mode.\nRows strictly AFTER this position, oldest first, so an external\nconsumer can resume exactly where it stopped. Mutually exclusive with\n`cursor`.","in":"query","name":"after","required":false,"schema":{"type":["string","null"]}},{"description":"`desc` (default) for a human list, `asc` for an export tail. `after`\nimplies `asc`.","in":"query","name":"order","required":false,"schema":{"type":["string","null"]}},{"description":"Actor filter: user uuid or \"system\".","in":"query","name":"actor","required":false,"schema":{"type":["string","null"]}},{"description":"Action prefix filter, e.g. \"auth\" matches every auth.* event.","in":"query","name":"action","required":false,"schema":{"type":["string","null"]}},{"description":"Inclusive lower bound on occurred_at.","in":"query","name":"from","required":false,"schema":{"format":"date-time","type":["string","null"]}},{"description":"Inclusive upper bound on occurred_at.","in":"query","name":"to","required":false,"schema":{"format":"date-time","type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditPage"}}},"description":""},"400":{"description":"cursor and after are mutually exclusive, order is not asc|desc, or a cursor does not decode"}},"security":[{"bearer":[]}],"summary":"GET /api/audit-events — append-only audit trail. Newest first by default;\n`order=asc` + `after` turns it into a forward-only export tail.","tags":["audit"]}},"/api/v1/auth/edge/sources":{"get":{"operationId":"edge_sources_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/EdgeSourceInfo"},"type":"array"}}},"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/auth/edge/sources — admin listing of edge-assertion sources.","tags":["auth"]}},"/api/v1/auth/edge/sources/{name}":{"delete":{"operationId":"edge_source_delete","parameters":[{"in":"path","name":"name","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":""},"403":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/auth/edge/sources/{name}.","tags":["auth"]},"put":{"operationId":"edge_source_upsert","parameters":[{"in":"path","name":"name","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertEdgeSourceRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EdgeSourceInfo"}}},"description":""},"403":{"description":""},"409":{"description":"this name is already claimed by another workspace"}},"security":[{"bearer":[]}],"summary":"PUT /api/auth/edge/sources/{name} — create or replace an edge\nsource. Pins are stored verbatim and enforced byte-for-byte at\nverification time; every write is audited.","tags":["auth"]}},"/api/v1/auth/edge/sources/{name}/links":{"get":{"operationId":"edge_links_list","parameters":[{"in":"path","name":"name","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/EdgeLinkInfo"},"type":"array"}}},"description":""},"403":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/auth/edge/sources/{name}/links — admin link listing.","tags":["auth"]},"post":{"operationId":"edge_link_create","parameters":[{"in":"path","name":"name","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEdgeLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EdgeLinkInfo"}}},"description":""},"403":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/auth/edge/sources/{name}/links — create an explicit link.\nAudited as `auth.federated_account_linked`.","tags":["auth"]}},"/api/v1/auth/edge/sources/{name}/links/{link_id}":{"delete":{"operationId":"edge_link_delete","parameters":[{"in":"path","name":"name","required":true,"schema":{"type":"string"}},{"in":"path","name":"link_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""},"403":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/auth/edge/sources/{name}/links/{link_id}. Audited as\n`auth.federated_account_unlinked`.","tags":["auth"]}},"/api/v1/auth/edge/{name}/exchange":{"post":{"operationId":"edge_exchange","parameters":[{"in":"path","name":"name","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EdgeExchangeResponse"}}},"description":""},"401":{"description":""}},"summary":"POST /api/auth/edge/{name}/exchange — swaps a valid\n`Cf-Access-Jwt-Assertion` (header) for Marula tokens. The\nassertion MUST verify against the source's pinned team JWKS with\nissuer AND audience pinned; failures are a uniform typed 401.","tags":["auth"]}},"/api/v1/auth/email-verification/confirm":{"post":{"operationId":"auth_email_verification_confirm","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyStatus"}}},"description":""},"401":{"description":"invalid or expired token"},"429":{"description":""}},"summary":"POST /api/auth/email-verification/confirm — marks the email verified\n(single-use, audited).","tags":["auth"]}},"/api/v1/auth/email-verification/request":{"post":{"operationId":"auth_email_verification_request","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailVerifyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UniformOk"}}},"description":""},"429":{"description":""}},"summary":"POST /api/auth/email-verification/request — resend (public, THROTTLED;\nadmin override rides POST /api/org/users/{id}/email-verified).","tags":["auth"]}},"/api/v1/auth/login":{"post":{"description":"Answers 503 with `Retry-After` when the process's password-hash slots are\nsaturated (Wave 56 lane `p56-auth-bounds`) — backpressure under a burst,\nnever a 500. Left out of the `utoipa` response list for the reason given\non [`register`].","operationId":"handlers_login","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}},"description":""},"401":{"description":"bad credentials"}},"summary":"POST /api/auth/login — resolves the org by email (SECURITY DEFINER\nbridge), verifies the argon2 hash under RLS scope. When any second\nfactor is enrolled, returns a short-lived `mfa_pending` bridge instead\nof tokens; POST /api/auth/mfa/verify completes it.","tags":["auth"]}},"/api/v1/auth/logout":{"post":{"operationId":"handlers_logout","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}},"required":true},"responses":{"200":{"description":""}},"summary":"POST /api/auth/logout — revokes the refresh session and audits the event.","tags":["auth"]}},"/api/v1/auth/magic-link/confirm":{"post":{"operationId":"auth_magic_link_confirm","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MagicLinkConfirm"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenPair"}}},"description":""},"401":{"description":"invalid or expired token"},"429":{"description":""}},"summary":"POST /api/auth/magic-link/confirm — consumes the link and issues a\nfull session through the SAME machinery as password login. STAFF\nfamily only (the portal is NOT included — see the threat model).\nAn account with an enrolled second factor answers a typed\n`mfa_required` refusal — the link proves the mailbox, not the factor.","tags":["auth"]}},"/api/v1/auth/magic-link/request":{"post":{"operationId":"auth_magic_link_request","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailVerifyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UniformOk"}}},"description":""},"429":{"description":""}},"summary":"POST /api/auth/magic-link/request — deny-by-default per org\n(`orgs.magic_link_sign_in`, 0088): unknown emails AND disabled orgs\ncollapse into the SAME uniform answer.","tags":["auth"]}},"/api/v1/auth/mfa/recovery-codes/regenerate":{"post":{"operationId":"mfa_recovery_regenerate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegenerateRecoveryRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecoveryCodesResponse"}}},"description":""},"401":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/auth/mfa/recovery-codes/regenerate — invalidates all previous\ncodes, issues a new batch (revealed once).","tags":["auth"]}},"/api/v1/auth/mfa/step-up":{"post":{"description":"This is the door the destructive factor changes point at. It is deliberately\nseparate from `mfa/verify`: that one completes a login, this one raises what\nan already-signed-in session may do, and the record it leaves expires.\n\nA passkey user does not call this — their step-up is the passkey login,\nwhich stamps the same record on the session it creates.","operationId":"mfa_step_up","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaStepUpRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaStepUpResponse"}}},"description":""},"401":{"description":""},"428":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/auth/mfa/step-up — proves password plus a live factor and records\nit against this sign-in lineage.","tags":["auth"]}},"/api/v1/auth/mfa/totp/disable":{"post":{"description":"This is the explicit \"turn MFA off\" action the last-factor rule points at.\nA successful call also stamps a step-up, because the caller just proved a\nfactor on this session; the next change within the window does not\nre-prompt.","operationId":"mfa_totp_disable","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpDisableRequest"}}},"required":true},"responses":{"204":{"description":""},"401":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/auth/mfa/totp/disable — requires password AND a live factor;\nwipes TOTP enrollment and all recovery codes.","tags":["auth"]}},"/api/v1/auth/mfa/totp/enroll-begin":{"post":{"description":"A FIRST enrolment (no confirmed factor) simply restarts the pending window.\nReplacing a CONFIRMED factor is a destructive act and needs a fresh step-up\nproved by a method OTHER than TOTP: a live code cannot authorize replacing\nthe secret it belongs to, so the caller uses a recovery code or a passkey.\nThe account keeps its recovery codes through the swap, which is what stops\nit holding zero working factors while the new secret is pending.\n\nAfter an admin reset (`users.mfa_reenroll_required`) enrolment is the\nsanctioned way out and the audited reset is its authorization: there is no\nfactor left to step up with, which is exactly why the reset travels in this\nlane.","operationId":"mfa_totp_enroll_begin","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpEnrollBeginResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/auth/mfa/totp/enroll-begin — generates a secret (returned ONCE)\nand stores it sealed+unconfirmed.","tags":["auth"]}},"/api/v1/auth/mfa/totp/enroll-verify":{"post":{"operationId":"mfa_totp_enroll_verify","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpEnrollVerifyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecoveryCodesResponse"}}},"description":""},"401":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/auth/mfa/totp/enroll-verify — confirms the enrollment with the\nfirst valid code and issues the one-time recovery-code batch.","tags":["auth"]}},"/api/v1/auth/mfa/verify":{"post":{"operationId":"mfa_verify","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaVerifyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenPair"}}},"description":""},"401":{"description":""}},"summary":"POST /api/auth/mfa/verify — completes an MFA-gated login with a TOTP or\nrecovery code. Tries TOTP first (advancing the replay watermark), then\nfalls back to single-use recovery codes.","tags":["auth"]}},"/api/v1/auth/mfa/webauthn/begin":{"post":{"operationId":"mfa_webauthn_login_begin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaTokenRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebauthnBeginResponse"}}},"description":""},"401":{"description":""}},"summary":"POST /api/auth/mfa/webauthn/begin — assertion request for the pending\nMFA login. Requires a valid mfa_pending token.","tags":["auth"]}},"/api/v1/auth/mfa/webauthn/credentials":{"get":{"operationId":"mfa_webauthn_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/WebauthnCredentialInfo"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/auth/mfa/webauthn/credentials — list registered passkeys.","tags":["auth"]}},"/api/v1/auth/mfa/webauthn/credentials/{id}/remove":{"post":{"description":"The password is not the authorization. A phished password must not be able\nto downgrade a passkey-only account to password-only sign-in (DECISIONS\nrecord 2), so removal needs a FRESH step-up on this sign-in lineage, proved\nby a factor that is not the credential being removed. The last factor of\nany kind cannot leave this way: that is the explicit \"turn MFA off\" action\n(`POST /api/auth/mfa/totp/disable`), and the refusal says so.","operationId":"mfa_webauthn_remove","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebauthnRemoveRequest"}}},"required":true},"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/auth/mfa/webauthn/credentials/{id}/remove — removes one passkey.","tags":["auth"]}},"/api/v1/auth/mfa/webauthn/finish":{"post":{"operationId":"mfa_webauthn_login_finish","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebauthnLoginFinishRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenPair"}}},"description":""},"401":{"description":""}},"summary":"POST /api/auth/mfa/webauthn/finish — verifies the assertion and issues\nfull tokens. Ceremony state is single-use; replay of a finish call fails.","tags":["auth"]}},"/api/v1/auth/mfa/webauthn/register/begin":{"post":{"operationId":"mfa_webauthn_register_begin","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebauthnBeginResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/auth/mfa/webauthn/register/begin — starts a passkey\nregistration for the authenticated user.","tags":["auth"]}},"/api/v1/auth/mfa/webauthn/register/finish":{"post":{"operationId":"mfa_webauthn_register_finish","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebauthnRegisterFinishRequest"}}},"required":true},"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/auth/mfa/webauthn/register/finish — verifies the attestation\nand stores the passkey. The public key is stored; no secret exists.","tags":["auth"]}},"/api/v1/auth/oidc/presets":{"get":{"operationId":"oidc_presets_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/OidcPresetInfo"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/auth/oidc/presets — the declarative preset catalog that\ndrives the admin \"add provider\" UI. Pure data; no configuration.","tags":["auth"]}},"/api/v1/auth/oidc/providers":{"get":{"operationId":"oidc_providers_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/OidcProviderInfo"},"type":"array"}}},"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/auth/oidc/providers — admin listing. Secrets excluded by type.","tags":["auth"]}},"/api/v1/auth/oidc/providers/public":{"get":{"operationId":"oidc_providers_public","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PublicProvider"},"type":"array"}}},"description":""}},"summary":"GET /api/auth/oidc/providers/public — drives config-driven SSO buttons\non the login screen. No configuration detail beyond names.","tags":["auth"]}},"/api/v1/auth/oidc/providers/{name}":{"delete":{"operationId":"oidc_provider_delete","parameters":[{"in":"path","name":"name","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":""},"403":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/auth/oidc/providers/{name}.","tags":["auth"]},"put":{"operationId":"oidc_provider_upsert","parameters":[{"in":"path","name":"name","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertProviderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OidcProviderInfo"}}},"description":""},"403":{"description":""},"409":{"description":"this name is already claimed by another workspace"}},"security":[{"bearer":[]}],"summary":"PUT /api/auth/oidc/providers/{name} — create or replace a provider.\nThe client secret is required on every write (rotate-friendly).","tags":["auth"]}},"/api/v1/auth/oidc/{name}/callback":{"get":{"operationId":"oidc_callback","parameters":[{"in":"path","name":"name","required":true,"schema":{"type":"string"}}],"responses":{"303":{"description":"redirect to web app"}},"summary":"GET /api/auth/oidc/{name}/callback — validates state signature/expiry/\nprovider binding and nonce echo, runs the conservative decision, then\nhands tokens (or link confirmation) to the web app via a single-use code.","tags":["auth"]}},"/api/v1/auth/oidc/{name}/link":{"post":{"operationId":"oidc_link_begin","parameters":[{"in":"path","name":"name","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkBeginResponse"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/auth/oidc/{name}/link — begins the EXPLICIT account-link flow\nfor the current user. Returns the authorize URL to navigate to.","tags":["auth"]}},"/api/v1/auth/oidc/{name}/login":{"get":{"operationId":"oidc_login_redirect","parameters":[{"in":"path","name":"name","required":true,"schema":{"type":"string"}}],"responses":{"303":{"description":"redirect to provider"},"404":{"description":""}},"summary":"GET /api/auth/oidc/{name}/login — 303 to the IdP with signed state,\nnonce, and PKCE S256 bound together.","tags":["auth"]}},"/api/v1/auth/password-reset/confirm":{"post":{"operationId":"auth_password_reset_confirm","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetConfirm"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UniformOk"}}},"description":""},"401":{"description":"invalid or expired token"},"422":{"description":"validation error"},"429":{"description":""}},"summary":"POST /api/auth/password-reset/confirm — consumes the token, sets the\nnew password, and revokes ALL of the user's live sessions (the\nreset's security payload: a stolen session cannot outlive it).","tags":["auth"]}},"/api/v1/auth/password-reset/request":{"post":{"operationId":"auth_password_reset_request","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UniformOk"}}},"description":""},"429":{"description":"flood throttle"}},"summary":"POST /api/auth/password-reset/request — issues a single-use token +\nmail when the account exists; ALWAYS the uniform answer.","tags":["auth"]}},"/api/v1/auth/refresh":{"post":{"operationId":"handlers_refresh","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenPair"}}},"description":""}},"summary":"POST /api/auth/refresh — rotates the refresh secret as a NEW chain node.\nPresenting an already-consumed (rotated/revoked/expired) token proves\nreplay: the WHOLE chain is revoked and the event is audited.","tags":["auth"]}},"/api/v1/auth/register":{"post":{"description":"Answers 503 with `Retry-After` when the process's password-hash slots are\nsaturated (Wave 56 lane `p56-auth-bounds`). The 503 is deliberately NOT in\nthe `utoipa` response list: every route can answer it from the middleware\nlayer (draining, database unreachable), the pinned OpenAPI document is\nregenerated only by `scripts/regen-openapi-pins.sh` against a live API, and\nadding one description here would leave both committed pins stale for a\nstatus the whole surface already shares.","operationId":"handlers_register","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenPair"}}},"description":""},"409":{"description":"slug or email already exists"},"422":{"description":"validation error"}},"summary":"POST /api/auth/register — creates the org, its default roles/board/SLA\npolicy, and the first user as Owner. One transaction: a half-created org\ncan never exist.","tags":["auth"]}},"/api/v1/auth/sessions":{"get":{"operationId":"auth_sessions_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SessionInfo"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/auth/sessions — the caller's live refresh sessions (security\nsettings page).","tags":["auth"]}},"/api/v1/auth/sessions/{id}/revoke":{"post":{"operationId":"auth_session_revoke","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/auth/sessions/{id}/revoke — kills one of the caller's own\nsessions (sign-out-everywhere-else primitive).","tags":["auth"]}},"/api/v1/auth/users/{id}/mfa-reset":{"post":{"description":"This exists because the strict rule needs an exit: a user who lost their only\nfactor cannot step up, and without a reset path the step-up requirement\nlocks them out of their own account changes (the ruling of 2026-10-25 puts\nthe two in one lane for exactly that reason).\n\nWhat makes it safe rather than a takeover tool:\n- `manage_users` is required, and an administrator cannot reset their own\n  factors — a stolen admin session must not be able to turn its owner's MFA\n  off with one call.\n- An Owner's or Admin's factors can only be reset by an Owner, so a lesser\n  administrator cannot strip a peer's second factor.\n- The account is left owing a factor (`users.mfa_reenroll_required`), not\n  merely factor-free: enrolment is the only factor change it may make until\n  one is confirmed, and the flag is audited with the counts.\n- Every live session is revoked, so the user re-authenticates with their\n  password and learns the factor is gone.\n- The audit row names the administrator, the target, what was removed and\n  how many sessions died.","operationId":"mfa_admin_factor_reset","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaResetResponse"}}},"description":""},"403":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/auth/users/{id}/mfa-reset — clears another user's factors.","tags":["auth"]}},"/api/v1/automation-rules":{"get":{"operationId":"automation_rules_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/automation-rules","tags":["automations"]},"post":{"operationId":"automation_rules_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRuleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/automation-rules — documents are validated through core-domain\nBEFORE storage, so stored rules are always well-formed (malformed rules\ncan only arrive out-of-band and are skipped at evaluation time).","tags":["automations"]}},"/api/v1/automation-rules/draft-summary":{"post":{"description":"Admin-gated (Edit). NOT a write: it returns an SSE stream of frames and\npersists nothing. Refusals answer typed BEFORE any stream begins, so the\nclient branches on the status code and renders a not-available state\ninstead of a dead spinner.","operationId":"automation_draft_summary","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationDraftSummaryRequest"}}},"required":true},"responses":{"200":{"description":"SSE stream of summary frames (text/event-stream)"},"400":{"description":"validation"},"403":{"description":"typed refusal: ai_surface_not_granted | ai_surface_disabled | ai_model_not_allowed | ai_no_models_configured | ai_hard_cap_reached | ai_no_credential (the org master switch among them)"}},"security":[{"bearer":[]}],"summary":"POST /api/v1/automation-rules/draft-summary — stream a one-sentence\nsummary of the rule draft.","tags":["automations"]}},"/api/v1/automation-rules/{id}":{"delete":{"operationId":"automation_rules_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/automation-rules/{id}","tags":["automations"]},"patch":{"operationId":"automation_rules_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRuleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /api/automation-rules/{id}","tags":["automations"]}},"/api/v1/automation-rules/{id}/test-run":{"post":{"operationId":"automation_rules_test_run","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationTestRunResponse"}}},"description":""},"404":{"description":"rule not visible in scope"},"422":{"description":"stored rule document is malformed"}},"security":[{"bearer":[]}],"tags":["automations"]}},"/api/v1/availability":{"get":{"operationId":"availability_list","parameters":[{"description":"Whose windows. Defaults to the caller; another user requires owner/admin.","in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailabilityListResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/availability — list one user's weekly templates.","tags":["scheduling"]},"post":{"operationId":"availability_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAvailabilityRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailabilityWindow"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/availability — add a weekly template for a user.","tags":["scheduling"]}},"/api/v1/availability/{id}":{"delete":{"operationId":"availability_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Owner of the row being deleted (must match self-or-admin rule).","in":"query","name":"user_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/availability/{id}?user_id= — remove one template.","tags":["scheduling"]}},"/api/v1/billing/invoice-runs":{"get":{"operationId":"billing_invoice_runs_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/InvoiceRun"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/billing/invoice-runs — most recent runs first.","tags":["billing"]},"post":{"operationId":"billing_invoice_runs_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvoiceRunRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceRunResponse"}}},"description":""},"409":{"description":"mixed-currency run or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/billing/invoice-runs — evaluate every billable contract over\nthe window (optionally one client's) and persist one invoice per\ncontract with at least one candidate line. Idempotency-keyed (IF1):\nthe same key with the same body replays the stored run.","tags":["billing"]}},"/api/v1/billing/invoice-runs/{id}":{"get":{"operationId":"billing_invoice_runs_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceRun"}}},"description":""},"404":{"description":"run not found"}},"security":[{"bearer":[]}],"summary":"GET /api/billing/invoice-runs/{id} — one run's roll-up.","tags":["billing"]}},"/api/v1/billing/preview":{"get":{"operationId":"billing_preview","parameters":[{"description":"Window start (inclusive), ISO date.","in":"query","name":"from","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Window end (inclusive), ISO date.","in":"query","name":"to","required":false,"schema":{"format":"date","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgBillingPreview"}}},"description":""},"422":{"description":"missing or inverted window"}},"security":[{"bearer":[]}],"summary":"GET /api/billing/preview — evaluate EVERY billable contract in the\norg over `[from, to]` (cancelled contracts excluded; effective windows\nmust intersect). READ-ONLY: the org-wide dry-run of a Wave-9 invoice\nrun.","tags":["billing"]}},"/api/v1/billing/verification":{"get":{"operationId":"billing_verification","parameters":[{"description":"The contract under review. Required: the workspace is\nper-contract because pricing, rate card, and blocks are.","in":"query","name":"contract_id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Inclusive period start (YYYY-MM-DD).","in":"query","name":"from","required":true,"schema":{"format":"date","type":"string"}},{"description":"Inclusive period end (YYYY-MM-DD).","in":"query","name":"to","required":true,"schema":{"format":"date","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationWorkspace"}}},"description":""},"404":{"description":"contract not visible in scope (rls_sparse)"},"422":{"description":"inverted or oversized window"}},"security":[{"bearer":[]}],"summary":"GET /api/billing/verification — the workspace for one contract over\none window. READ-ONLY: no entry changes, no side effects.","tags":["billing"]}},"/api/v1/billing/verification/entries/{id}/action":{"post":{"operationId":"billing_verification_entry_action","parameters":[{"description":"Time entry id","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntryActionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntryActionResponse"}}},"description":""},"403":{"description":"own_entry: an approver cannot decide their own entry"},"404":{"description":"entry not visible in scope (rls_sparse)"},"409":{"description":"entry already invoiced; verification is pre-run only"},"422":{"description":"missing reason, unknown action, or invalid value"}},"security":[{"bearer":[]}],"summary":"POST /api/billing/verification/entries/{id}/action — every in-context\nentry edit, one envelope. Gated on the instant-first mutation\ncontract: same key + same payload replays, different payload 409s.","tags":["billing"]}},"/api/v1/billing/verification/signoff":{"post":{"operationId":"billing_verification_signoff","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignoffRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignoffResponse"}}},"description":""},"404":{"description":"contract not visible in scope (rls_sparse)"},"422":{"description":"checklist claim does not match the live window"}},"security":[{"bearer":[]}],"summary":"POST /api/billing/verification/signoff — the close ritual (report\nV8). The checklist booleans are RE-VERIFIED against the live\nworkspace, never taken on trust: signing off \"every entry\ndispositioned\" while a draft entry sits in the window is a 422, not\na stored lie. The overlap warning rides the response.","tags":["billing"]}},"/api/v1/board-groups":{"get":{"operationId":"board_groups_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BoardGroup"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/board-groups — the org's groups in nav order (any member may\nread; rendering input for the tree).","tags":["boards"]},"post":{"operationId":"board_groups_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGroupRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardGroup"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/board-groups — creates a nav group (ManageBoards gated);\nemits `board.group_created`.","tags":["boards"]}},"/api/v1/board-groups/{id}":{"delete":{"operationId":"board_groups_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/board-groups/{id} — removes a group; member boards become\nungrouped, never deleted. ManageBoards OR the group's owner; emits\n`board.group_removed`.","tags":["boards"]},"patch":{"operationId":"board_groups_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGroupRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardGroup"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /api/board-groups/{id} — renames/reorders a group. ManageBoards\nOR the group's owner may do this; emits `board.group_updated`.","tags":["boards"]}},"/api/v1/boards":{"get":{"operationId":"boards_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Board"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/boards","tags":["boards"]},"post":{"operationId":"boards_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBoardRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Board"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/boards — creates a board seeded with the four default statuses.","tags":["boards"]}},"/api/v1/boards/favorite":{"post":{"operationId":"boards_toggle_favorite","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FavoriteRequest"}}},"required":true},"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/boards/favorite — toggles a board favorite for the current\nuser (user-scoped; any authenticated member may star boards they can\nsee). Emits `board.favorite_toggled` in-tx.","tags":["boards"]}},"/api/v1/boards/nav":{"get":{"operationId":"boards_nav","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BoardNavEntry"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/boards/nav — every org board annotated with the current\nuser's favorite mark, board role, and group assignment.","tags":["boards"]}},"/api/v1/boards/{id}/checklist-template":{"delete":{"operationId":"boards_delete_checklist_template","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/boards/{id}/checklist-template — removes the template.\nTicket instances demote to ad-hoc items; with no template left,\nclose-time enforcement is off by construction regardless of the toggle.","tags":["boards"]},"get":{"operationId":"boards_get_checklist_template","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChecklistTemplateView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/boards/{id}/checklist-template","tags":["boards"]},"put":{"operationId":"boards_put_checklist_template","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutChecklistTemplateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChecklistTemplateView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PUT /api/boards/{id}/checklist-template — creates or replaces the\nboard's template wholesale and sets the enforcement toggle; emits\n`board.checklist_template_updated` and audits the change.","tags":["boards"]}},"/api/v1/boards/{id}/email-identity":{"put":{"operationId":"email_board_identity_put","parameters":[{"description":"Board id","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutBoardIdentityRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardIdentityView"}}},"description":""},"404":{"description":"board not found"},"409":{"description":"from address already in use"}},"security":[{"bearer":[]}],"summary":"PUT /api/boards/{id}/email-identity — set/clear per-board outbound\nidentity fields (from address, display name, reply-to, signature).","tags":["email"]}},"/api/v1/boards/{id}/fields":{"get":{"operationId":"fields_list_for_board","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FieldDefView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/boards/{id}/fields — active ticket definitions governing this\nboard (org-wide plus board-owned), sort-order stable.","tags":["fields"]},"post":{"operationId":"fields_create_def","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFieldDefRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FieldDefView"}}},"description":""},"422":{"description":"invalid definition"}},"security":[{"bearer":[]}],"summary":"POST /api/boards/{id}/fields — creates a board-scoped definition\n(board-admin gated); emits `custom_field.def_created`.","tags":["fields"]}},"/api/v1/boards/{id}/fields/{field_id}":{"delete":{"operationId":"fields_delete_def","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"field_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/boards/{id}/fields/{field_id} — removes the definition and\ncascades its stored values; emits `custom_field.def_removed`.","tags":["fields"]},"patch":{"operationId":"fields_update_def","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"field_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFieldDefRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FieldDefView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /api/boards/{id}/fields/{field_id} — partial edit of a board-owned\ndefinition. Kind is immutable (it would orphan stored values); emit\n`custom_field.def_updated` on success.","tags":["fields"]}},"/api/v1/boards/{id}/group":{"put":{"operationId":"boards_assign_group","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignGroupRequest"}}},"required":true},"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"PUT /api/boards/{id}/group — assigns/unassigns the board's nav group.\nBoard-admin gated (ConfigureBoard); emits `board.group_updated`.","tags":["boards"]}},"/api/v1/boards/{id}/inbound-address":{"put":{"operationId":"email_board_inbound_address_put","parameters":[{"description":"Board id","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutBoardAddressRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardAddressResponse"}}},"description":""},"404":{"description":"board not found"},"409":{"description":"address already routed"}},"security":[{"bearer":[]}],"summary":"PUT /api/boards/{id}/inbound-address — set/clear a board-level address.","tags":["email"]}},"/api/v1/boards/{id}/members":{"get":{"operationId":"boards_list_members","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BoardMemberView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/boards/{id}/members — the roster (ConfigureBoard gated).","tags":["boards"]},"put":{"operationId":"boards_upsert_member","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertMemberRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardMemberView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PUT /api/boards/{id}/members — adds or re-roles one membership.\nManageMembers gated (board admin); emits `board.membership_updated`.","tags":["boards"]}},"/api/v1/boards/{id}/members/{user_id}":{"delete":{"operationId":"boards_remove_member","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"user_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/boards/{id}/members/{user_id} — removes one membership;\nthe user falls back to org-governed access. Emits\n`board.membership_removed`.","tags":["boards"]}},"/api/v1/boards/{id}/statuses":{"get":{"operationId":"boards_list_statuses","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BoardStatus"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/boards/{id}/statuses","tags":["boards"]},"post":{"operationId":"boards_add_status","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateStatusRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardStatus"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/boards/{id}/statuses — adds a custom status bound to a\nsemantic category; emits `board.status_added`.","tags":["boards"]}},"/api/v1/boards/{id}/statuses/usage":{"get":{"operationId":"boards_status_usage","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BoardStatusUsage"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"tags":["boards"]}},"/api/v1/boards/{id}/statuses/{status_id}":{"delete":{"description":"Without `reassign_to` a status holding tickets is refused (409), as before.\nWith it, every ticket moves first — set-based, mirroring the per-ticket\nstatus-change invariants exactly (denormalised category, one-shot\n`resolved_at`, SLA pause bookkeeping) — and each moved ticket gets its own\n`ticket.status_changed` outbox row, so a bulk move is as auditable as a\nsingle one. Automations naming the status are paused, never repointed.\nEmits `board.status_removed`.","operationId":"boards_delete_status","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"status_id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Move every ticket in this status to `reassign_to` and then delete it.\nMust be another status on the same board. Absent, a status holding\ntickets is refused with 409 and nothing changes.","in":"query","name":"reassign_to","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteStatusResult"}}},"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/boards/{id}/statuses/{status_id} — removes a custom status.","tags":["boards"]},"patch":{"operationId":"boards_update_status","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"status_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStatusRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardStatus"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /api/boards/{id}/statuses/{status_id} — rebinds/reorders/renames a\nstatus; emits `board.status_updated`.","tags":["boards"]}},"/api/v1/boards/{id}/transitions":{"get":{"operationId":"boards_list_transitions","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardTransitionsResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/boards/{id}/transitions — the board's effective workflow.","tags":["boards"]},"put":{"operationId":"boards_set_transitions","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTransitionsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardTransitionsResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PUT /api/boards/{id}/transitions — atomically replaces the explicit\ntransition matrix; emits `board.transitions_updated`. Endpoints must be\nstatuses of THIS board and self-transitions are rejected.","tags":["boards"]}},"/api/v1/calendar-exclusions":{"get":{"operationId":"calendar_exclusion_list","parameters":[{"in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"from","required":false,"schema":{"format":"date-time","type":["string","null"]}},{"in":"query","name":"to","required":false,"schema":{"format":"date-time","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CalendarExclusion"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/calendar-exclusions?user_id=&from=&to=","tags":["scheduling"]},"post":{"operationId":"calendar_exclusion_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExclusionRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarExclusion"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/calendar-exclusions — record time-off.","tags":["scheduling"]}},"/api/v1/calendar-exclusions/{id}":{"delete":{"operationId":"calendar_exclusion_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Owner of the row being deleted (must match self-or-admin rule).","in":"query","name":"user_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/calendar-exclusions/{id}?user_id=","tags":["scheduling"]}},"/api/v1/calendar-sync":{"get":{"operationId":"calendar_sync_list","parameters":[{"description":"Whose windows. Defaults to the caller; another user requires owner/admin.","in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CalendarSyncConnectionView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/calendar-sync — the caller's (or target user's) connections.","tags":["scheduling"]},"post":{"operationId":"calendar_sync_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConnectionRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/calendar-sync — create a connection. Outbound-capable\ndirections get a feed token generated immediately (returned once).","tags":["scheduling"]}},"/api/v1/calendar-sync/feed/{token}":{"get":{"operationId":"public_calendar_sync_feed","parameters":[{"in":"path","name":"token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"text/calendar":{"schema":{"type":"string"}}},"description":""},"404":{"description":"unknown token"},"429":{"description":""}},"summary":"GET /api/calendar-sync/feed/{token} — the OUTBOUND direction: the\nuser's live appointments as a subscribable iCalendar document.\nUnauthenticated but token-scoped and rate-limited (the scheduling-links\npattern); the token resolves org + user, then the read is org-scoped. The\ntoken-to-org resolution runs through the `app_resolve_calendar_feed_target`\nSECURITY DEFINER bridge (migration 0132) — the token is in the URL with no\nsession, so the row is default-deny off the bare pool under RLS.","tags":["scheduling"]}},"/api/v1/calendar-sync/{connection_id}/authorize":{"post":{"description":"Returns the vendor URL to send the operator to. The `state` is a\nserver-generated nonce, NOT a client-supplied value: a client that chose\nits own state would defeat the CSRF protection entirely.","operationId":"calendar_sync_authorize_start","parameters":[{"in":"path","name":"connection_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthStartResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/calendar-sync/{connection_id}/authorize — begin consent.","tags":["calendar-sync"]}},"/api/v1/calendar-sync/{id}":{"delete":{"operationId":"calendar_sync_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/calendar-sync/{id} — the connection, its mirror rows, and\nthe exclusion rows the sync owns all go away (external busy time\nfrees the calendar).","tags":["scheduling"]},"patch":{"operationId":"calendar_sync_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateConnectionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarSyncConnectionView"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /api/calendar-sync/{id} — edit name/direction/feed_url/status.","tags":["scheduling"]}},"/api/v1/calendar-sync/{id}/events":{"get":{"operationId":"calendar_sync_events","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventsListResponse"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/calendar-sync/{id}/events — the inbound mirror (the\nconflict/duplicate surface Wave 14 lane 7 renders).","tags":["scheduling"]}},"/api/v1/calendar-sync/{id}/rotate-token":{"post":{"operationId":"calendar_sync_rotate_token","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionView"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/calendar-sync/{id}/rotate-token — invalidates the old\noutbound feed URL and returns the new raw token once.","tags":["scheduling"]}},"/api/v1/calendar-sync/{id}/sync":{"post":{"operationId":"calendar_sync_run","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Force a poll even when the connection is disabled (admin repair).","in":"path","name":"force","required":true,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncOutcomeResponse"}}},"description":""},"404":{"description":""},"503":{"description":"external calendar unreachable/unreadable"}},"security":[{"bearer":[]}],"summary":"POST /api/calendar-sync/{id}/sync — poll now (manual trigger; the\nworker lane calls the same seam on its schedule). Network fetch\nhappens OUTSIDE the transaction; the apply + poll-state record + event\nemission run inside ONE org transaction.","tags":["scheduling"]}},"/api/v1/calendar-sync/{provider}/callback":{"get":{"description":"The `state` is matched against the stored nonce, which is what makes the\nflow CSRF-safe: a callback whose state does not match the connection this\nbrowser started is discarded, so an attacker cannot feed a victim's\nbrowser a code obtained under someone else's consent.","operationId":"calendar_sync_oauth_callback","parameters":[{"in":"path","name":"provider","required":true,"schema":{"type":"string"}},{"in":"query","name":"state","required":true,"schema":{"type":"string"}},{"in":"query","name":"code","required":false,"schema":{"type":["string","null"]}},{"description":"The operator may decline; the vendor then redirects with an error\nand no code. That is a user choice, not a failure, so it gets its own\nmessage instead of \"invalid code\".","in":"query","name":"error","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthCallbackResult"}}},"description":""}},"summary":"GET /api/calendar-sync/{provider}/callback — the vendor's redirect target.","tags":["calendar-sync"]}},"/api/v1/calendar/day":{"get":{"operationId":"calendar_day","parameters":[{"description":"Whose calendar. Defaults to the caller; another user requires\nowner/admin.","in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Local date in the TARGET user's timezone. Day view: the day itself.\nWeek view: any date inside the week (Monday-start). Defaults to\ntoday in that timezone.","in":"query","name":"date","required":false,"schema":{"format":"date","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarViewResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/calendar/day — one local day for one user.","tags":["scheduling"]}},"/api/v1/calendar/deadlines":{"get":{"operationId":"calendar_deadlines","parameters":[{"description":"Absolute UTC window [from, to); capped at 62 days like the team\nview (a deadline overlay never walks quarterly ranges).","in":"query","name":"from","required":false,"schema":{"format":"date-time","type":["string","null"]}},{"in":"query","name":"to","required":false,"schema":{"format":"date-time","type":["string","null"]}},{"description":"Optional per-person narrowing (personal mode rides the same model).","in":"query","name":"assignee_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarDeadlinesResponse"}}},"description":""},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"GET /api/calendar/deadlines — the ORG-WIDE ticket-deadline read model\n(Wave 17 lane 13): /calendar team mode overlays THIS instead of the\nbounded page-1 tickets fetch. Alive tickets only (category not closed)\nwhose due date falls in the window; hydrated in the row itself.","tags":["scheduling"]}},"/api/v1/calendar/team":{"get":{"operationId":"calendar_team","parameters":[{"description":"Inclusive window start (default: today's UTC midnight minus 1 day).","in":"query","name":"from","required":false,"schema":{"format":"date-time","type":["string","null"]}},{"description":"Exclusive window end (default: `from` + 8 days).","in":"query","name":"to","required":false,"schema":{"format":"date-time","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarViewResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/calendar/team — every org member over an absolute UTC range.","tags":["scheduling"]}},"/api/v1/calendar/week":{"get":{"operationId":"calendar_week","parameters":[{"description":"Whose calendar. Defaults to the caller; another user requires\nowner/admin.","in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Local date in the TARGET user's timezone. Day view: the day itself.\nWeek view: any date inside the week (Monday-start). Defaults to\ntoday in that timezone.","in":"query","name":"date","required":false,"schema":{"format":"date","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarViewResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/calendar/week — Monday-start local week for one user.","tags":["scheduling"]}},"/api/v1/catalog-items":{"get":{"operationId":"catalog_items_list","parameters":[{"description":"`None` = all items (active + archived).","in":"query","name":"active","required":false,"schema":{"type":["boolean","null"]}},{"in":"query","name":"category","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"group_name","required":false,"schema":{"type":["string","null"]}},{"description":"Substring search over name/sku/category/group.","in":"query","name":"search","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CatalogItemView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/catalog-items — newest/active first, filterable.","tags":["commercial"]},"post":{"operationId":"catalog_items_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCatalogItemRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogItemMutationResponse"}}},"description":""},"409":{"description":"SKU already exists or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/catalog-items — create through the instant-first write path.","tags":["commercial"]}},"/api/v1/catalog-items/{id}":{"get":{"operationId":"catalog_items_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogItemView"}}},"description":""},"404":{"description":"not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/catalog-items/{id}.","tags":["commercial"]},"patch":{"operationId":"catalog_items_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCatalogItemRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogItemMutationResponse"}}},"description":""},"404":{"description":"not visible in scope"},"409":{"description":"SKU already exists, version conflict, or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"PATCH /api/catalog-items/{id} — absent fields keep their value.","tags":["commercial"]}},"/api/v1/catalog-items/{id}/archive":{"post":{"operationId":"catalog_items_archive","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArchiveCatalogItemRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogItemMutationResponse"}}},"description":""},"404":{"description":"not visible in scope"},"409":{"description":"version conflict or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/catalog-items/{id}/archive — flip `active` off (data, never\na delete). Already-archived items archive as a no-op replay of state.","tags":["commercial"]}},"/api/v1/catalog/pricing-feeds":{"post":{"operationId":"pricing_feed_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PricingFeedBatchRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PricingFeedBatchResponse"}}},"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/v1/catalog/pricing-feeds — upload one immutable distributor\nfeed batch; admin/owner; then `/{batch_id}/apply` overlays pricing by\nlowercase SKU.","tags":["integrations"]}},"/api/v1/catalog/pricing-feeds/{batch_id}/apply":{"post":{"operationId":"pricing_feed_apply","parameters":[{"in":"path","name":"batch_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PricingFeedApplyResponse"}}},"description":""},"404":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/v1/catalog/pricing-feeds/{batch_id}/apply","tags":["integrations"]}},"/api/v1/client-loans":{"get":{"operationId":"client_loans_list","parameters":[{"description":"open | returned (absent = both).","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Only open loans past their expected return.","in":"query","name":"overdue_only","required":false,"schema":{"type":["boolean","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ClientLoanDetailResponse"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/client-loans.","tags":["commercial"]},"post":{"operationId":"client_loans_issue","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueLoanRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientLoanMutationResponse"}}},"description":""},"404":{"description":"client / contract / device not visible in scope"},"409":{"description":"device not in stock / already on an open loan / incomplete lease terms"}},"security":[{"bearer":[]}],"summary":"POST /api/client-loans — instant-first (IF1/IF2).","tags":["commercial"]}},"/api/v1/client-loans/{id}":{"get":{"operationId":"client_loans_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientLoanDetailResponse"}}},"description":""},"404":{"description":"not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/client-loans/{id}.","tags":["commercial"]}},"/api/v1/client-loans/{id}/return":{"post":{"operationId":"client_loans_return","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnLoanRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientLoanMutationResponse"}}},"description":""},"404":{"description":"not visible in scope"},"409":{"description":"already returned / missing return location for stock devices"}},"security":[{"bearer":[]}],"summary":"POST /api/client-loans/{id}/return — instant-first (IF1/IF2).","tags":["commercial"]}},"/api/v1/clients":{"get":{"operationId":"clients_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Client"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/clients","tags":["clients"]},"post":{"operationId":"clients_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateClientRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientMutationResponse"}}},"description":""},"409":{"description":"idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/clients — instant-first (IF1/IF2): idempotency-keyed and\nreturns the read-your-write `mutation` metadata.","tags":["clients"]}},"/api/v1/clients/org-map":{"get":{"operationId":"clients_org_map_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ClientOrgMap"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/clients/org-map — the durable operator mapping (settings view).","tags":["entity-dedupe"]}},"/api/v1/clients/org-reconcile":{"post":{"operationId":"clients_org_reconcile","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgReconcileRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgReconcileResponse"}}},"description":""},"409":{"description":"idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/clients/org-reconcile — the connectors' org_map seam (public\nAPI): report an unmapped vendor org; the durable map resolves it\ninstantly, otherwise deterministic label proposals queue reviewable\ncandidates. Never a guess, never a silent auto-map.","tags":["entity-dedupe"]}},"/api/v1/clients/{id}":{"get":{"operationId":"clients_get_one","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Client"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/clients/{id}","tags":["clients"]}},"/api/v1/clients/{id}/assets":{"get":{"operationId":"clients_list_assets","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/DeviceAsset"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/clients/{id}/assets","tags":["clients"]},"post":{"operationId":"clients_create_asset","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAssetRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAsset"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/clients/{id}/assets","tags":["clients"]}},"/api/v1/clients/{id}/board-exposures":{"get":{"operationId":"board_exposures_list","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BoardExposureView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/clients/{id}/board-exposures — every configured row for the\nclient (exposed or not), so admin UI can render toggles.","tags":["portal-admin"]},"put":{"operationId":"board_exposures_put","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutExposureRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardExposureView"}}},"description":""},"404":{"description":"board or client missing"}},"security":[{"bearer":[]}],"summary":"PUT /api/clients/{id}/board-exposures — upsert one board×client row.","tags":["portal-admin"]}},"/api/v1/clients/{id}/completeness":{"get":{"operationId":"clients_completeness","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientCompletenessView"}}},"description":""},"404":{"description":"client not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/clients/{id}/completeness — backend documentation-completeness\nscoring (Wave 17 lane 13): the weights live SERVER-side now, replacing\nthe honest client-side metadata scoring the clients lane shipped.\nSame shape, same pro-ration doctrine: score = round(earned /\nmaxKnown × 100) with null legs (leg absent) pro-rated OUT, never a\nsilent zero.","tags":["clients"]}},"/api/v1/clients/{id}/contacts":{"get":{"operationId":"clients_list_contacts","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Contact"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/clients/{id}/contacts","tags":["clients"]},"post":{"operationId":"clients_create_contact","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateContactRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/clients/{id}/contacts","tags":["clients"]}},"/api/v1/clients/{id}/portal-users":{"get":{"operationId":"portal_users_list","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PortalUser"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/clients/{id}/portal-users — directory for one client.","tags":["portal-admin"]},"post":{"operationId":"portal_users_create","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePortalUserRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalUser"}}},"description":""},"409":{"description":"email already provisioned"}},"security":[{"bearer":[]}],"summary":"POST /api/clients/{id}/portal-users — provision (invite) one identity.","tags":["portal-admin"]}},"/api/v1/clients/{id}/sites":{"get":{"operationId":"clients_list_sites","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Site"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/clients/{id}/sites","tags":["clients"]},"post":{"operationId":"clients_create_site","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSiteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/clients/{id}/sites","tags":["clients"]}},"/api/v1/clients/{id}/workflow-overrides":{"get":{"operationId":"client_workflow_overrides_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientWorkflowResolution"}}},"description":""},"404":{"description":"client not found in this org"}},"security":[{"bearer":[]}],"summary":"GET /api/clients/{id}/workflow-overrides — the client's resolved workflow:\none entry per registry declaration with precedence outcome, provenance,\nrenderable details (names + semantic status categories), and drift issues.","tags":["workflow-overrides"]}},"/api/v1/clients/{id}/workflow-overrides/{key}":{"delete":{"operationId":"client_workflow_override_clear","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"key","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OverrideMutationResponse"}}},"description":""},"404":{"description":"client not found in this org (rls_sparse in-mutation)"},"422":{"description":"unknown override key"}},"security":[{"bearer":[]}],"summary":"DELETE /api/clients/{id}/workflow-overrides/{key} — remove the override so\nthe client inherits the org default again. Distinct from an explicit\ndisable; clearing an absent override is a no-op that still resolves.","tags":["workflow-overrides"]},"put":{"operationId":"client_workflow_override_put","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutOverrideRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OverrideMutationResponse"}}},"description":""},"404":{"description":"client not found in this org (rls_sparse in-mutation)"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"unregistered key, mistyped value, invalid reference, or disallowed disable"}},"security":[{"bearer":[]}],"summary":"PUT /api/clients/{id}/workflow-overrides/{key} — set a value or record an\nexplicit disable. Unknown keys, mistyped payloads, out-of-org references,\nand disallowed disables are typed 422s before anything persists.","tags":["workflow-overrides"]}},"/api/v1/config-items":{"get":{"operationId":"config_items_list","parameters":[{"in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"layout_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"CI6 semantic class filter (workstation | server | vm |\nnetwork_appliance | printer | iot).","in":"query","name":"semantic_class","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"q","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ConfigItemView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/config-items — active items only (merged tombstones excluded).","tags":["config-items"]}},"/api/v1/config-items/feed-upsert":{"post":{"operationId":"config_items_feed_upsert","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedUpsertRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedUpsertResponse"}}},"description":""},"409":{"description":"idempotency key reused"},"422":{"description":"source system / external id validation"}},"security":[{"bearer":[]}],"summary":"POST /api/config-items/feed-upsert — CI2: the single feeder write path\n(lane 8's seam). Never blind-inserts: existing (source_system,\nexternal_id) links resolve to their own item; identity keys resolve the\ndeterministic case; ambiguity queues reviewable candidates and only\nauto-merges at/above the org threshold.","tags":["config-items"]}},"/api/v1/config-items/interfaces/{id}":{"delete":{"operationId":"config_items_delete_interface","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/config-items/interfaces/{id}","tags":["config-items"]}},"/api/v1/config-items/merge-candidates":{"get":{"operationId":"config_items_list_candidates","parameters":[{"description":"pending | auto_merged | accepted | rejected","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CandidateView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/config-items/merge-candidates — the review queue.","tags":["config-items"]}},"/api/v1/config-items/merge-candidates/batch":{"post":{"operationId":"config_items_batch_candidates","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchCandidateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchCandidateResponse"}}},"description":""},"422":{"description":"unknown batch action / empty id list"}},"security":[{"bearer":[]}],"summary":"POST /api/config-items/merge-candidates/batch — accept/reject/defer N\ncandidates in ONE call. Every ACCEPT is an audited, individually\nundoable merge (per-candidate split substrate); a failed member never\naborts the batch (per-item outcome, never a silent partial).","tags":["config-items"]}},"/api/v1/config-items/merge-candidates/{id}/accept":{"post":{"operationId":"config_items_accept_candidate","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CiMerge"}}},"description":""},"404":{"description":"candidate not visible in scope"},"409":{"description":"candidate already decided"}},"security":[{"bearer":[]}],"summary":"POST /api/config-items/merge-candidates/{id}/accept — fold the candidate\ninto the primary with the candidate's evidence attached.","tags":["config-items"]}},"/api/v1/config-items/merge-candidates/{id}/defer":{"post":{"operationId":"config_items_defer_candidate","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CiMergeCandidate"}}},"description":""},"404":{"description":"candidate not visible in scope"},"409":{"description":"candidate already decided"}},"security":[{"bearer":[]}],"summary":"POST /api/config-items/merge-candidates/{id}/defer — park the candidate\nout of the pending queue WITHOUT deciding it (reversible via restore;\na feed upsert refreshes evidence but never un-defers silently).","tags":["config-items"]}},"/api/v1/config-items/merge-candidates/{id}/reject":{"post":{"operationId":"config_items_reject_candidate","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CiMergeCandidate"}}},"description":""},"404":{"description":"candidate not visible in scope"},"409":{"description":"candidate already decided"}},"security":[{"bearer":[]}],"summary":"POST /api/config-items/merge-candidates/{id}/reject — keep both items.","tags":["config-items"]}},"/api/v1/config-items/merge-candidates/{id}/restore":{"post":{"operationId":"config_items_restore_candidate","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CiMergeCandidate"}}},"description":""},"404":{"description":"candidate not visible in scope"},"409":{"description":"candidate is not deferred"}},"security":[{"bearer":[]}],"summary":"POST /api/config-items/merge-candidates/{id}/restore — un-defer: a\nparked candidate returns to the pending queue.","tags":["config-items"]}},"/api/v1/config-items/merge-candidates/{id}/workbench":{"get":{"operationId":"config_items_candidate_workbench","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MergeWorkbenchView"}}},"description":""},"404":{"description":"candidate not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/config-items/merge-candidates/{id}/workbench — the guided\nmerge workbench's read model: side-by-side cards across ALL\nConfigSourceLinks, the field-by-field picker rows, the inline\ndeterministic + decision evidence, and the threshold context.","tags":["config-items"]}},"/api/v1/config-items/merges/{id}/split":{"post":{"operationId":"config_items_split","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CiMerge"}}},"description":""},"404":{"description":"merge not visible in scope"},"409":{"description":"merge already undone"}},"security":[{"bearer":[]}],"summary":"POST /api/config-items/merges/{id}/split — undo a merge (IF7): links,\ninterfaces, and field tiers ride back from the audit row's packet.","tags":["config-items"]}},"/api/v1/config-items/precedence":{"get":{"operationId":"config_items_list_precedence","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CiFieldPrecedence"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/config-items/precedence — org overrides (shipped ADR defaults\nresolve in Rust when no row matches).","tags":["config-items"]},"put":{"operationId":"config_items_upsert_precedence","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertPrecedenceRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CiFieldPrecedence"}}},"description":""},"422":{"description":"unknown semantic class"}},"security":[{"bearer":[]}],"summary":"PUT /api/config-items/precedence — a per-type/per-source precedence\noverride (CI5). Upserts by (layout, field, source).","tags":["config-items"]}},"/api/v1/config-items/settings":{"get":{"operationId":"config_items_get_settings","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CiSettingsView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/config-items/settings — the org's auto-merge threshold.","tags":["config-items"]},"put":{"operationId":"config_items_put_settings","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSettingsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CiSettingsView"}}},"description":""},"422":{"description":"threshold out of range"}},"security":[{"bearer":[]}],"summary":"PUT /api/config-items/settings — the org's auto-merge confidence\nthreshold (CI4).","tags":["config-items"]}},"/api/v1/config-items/{id}":{"get":{"operationId":"config_items_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigItemView"}}},"description":""},"404":{"description":"config item not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/config-items/{id} — the CI with its source links + interfaces.","tags":["config-items"]},"patch":{"operationId":"config_items_curate","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurateFieldsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigItemMutationResponse"}}},"description":""},"404":{"description":"config item not visible in scope"},"409":{"description":"version conflict or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"PATCH /api/config-items/{id} — human curation (CI5): curated values are\nsticky forever; feeds never auto-overwrite them afterwards.","tags":["config-items"]}},"/api/v1/config-items/{id}/accept-observed":{"post":{"operationId":"config_items_accept_observed","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptObservedRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigItemMutationResponse"}}},"description":""},"404":{"description":"config item not visible in scope"},"409":{"description":"no observed value for that field / version conflict"}},"security":[{"bearer":[]}],"summary":"POST /api/config-items/{id}/accept-observed — CI5 one-click \"accept the\nnew value\": promotes the observed value into the sticky curated tier.","tags":["config-items"]}},"/api/v1/config-items/{id}/interfaces":{"post":{"operationId":"config_items_add_interface","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInterfaceRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CiInterface"},"type":"array"}}},"description":""},"404":{"description":"config item not visible in scope"}},"security":[{"bearer":[]}],"summary":"POST /api/config-items/{id}/interfaces — record a NIC (ADR 0003\nlifecycle rule: NICs are attributes, not their own CIs).","tags":["config-items"]}},"/api/v1/config-items/{id}/merge":{"post":{"operationId":"config_items_merge","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CiMergeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CiMerge"}}},"description":""},"404":{"description":"config item not visible in scope"},"409":{"description":"cannot merge an item into itself / already merged"}},"security":[{"bearer":[]}],"summary":"POST /api/config-items/{id}/merge — an explicit human merge (the queue's\naccept path rides this too). Audited + undoable (split).","tags":["config-items"]}},"/api/v1/config-items/{id}/merges":{"get":{"operationId":"config_items_list_merges","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CiMerge"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/config-items/{id}/merges — the item's merge audit trail.","tags":["config-items"]}},"/api/v1/connectors":{"get":{"operationId":"connectors_index","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorIndexResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/v1/connectors — the org's installed bridges (presence only).","tags":["integrations"]}},"/api/v1/connectors/voip/call-logs":{"post":{"operationId":"connectors_voip_ingest","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoipCallLogRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoipCallLogResponse"}}},"description":""},"404":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/v1/connectors/voip/call-logs — machine ingest of one call\nrecord (idempotent per provider call id). Requires the consents +\nthe `calls.ingest` scope.","tags":["integrations"]}},"/api/v1/connectors/{kind}":{"delete":{"operationId":"connectors_remove","parameters":[{"in":"path","name":"kind","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/v1/connectors/{kind} — withdraws the consent.","tags":["integrations"]},"post":{"operationId":"connectors_install","parameters":[{"in":"path","name":"kind","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorInstallRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorInstallationView"}}},"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/v1/connectors/{kind} — the admin installs / re-consents.","tags":["integrations"]}},"/api/v1/connectors/{kind}/test":{"post":{"operationId":"connectors_test","parameters":[{"in":"path","name":"kind","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestResult"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/v1/connectors/{kind}/test — a provisioned canary through\nthe adapter (slack/teams notifications; rewst trigger).","tags":["integrations"]}},"/api/v1/contacts/feed-upsert":{"post":{"operationId":"contacts_feed_upsert","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactFeedUpsertRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactFeedUpsertResponse"}}},"description":""},"409":{"description":"idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/contacts/feed-upsert — the contact ingest seam (the CI2\npattern generalized): never blind-inserts; email/phone identity keys\nresolve the deterministic case; ambiguity queues reviewable candidates;\nthe same org auto-merge threshold applies.","tags":["entity-dedupe"]}},"/api/v1/contracts":{"get":{"operationId":"contracts_list","parameters":[{"in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Exact status: `draft|active|ended|cancelled`.","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}},{"description":"Only contracts effective on this date.","in":"query","name":"effective_on","required":false,"schema":{"format":"date","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Contract"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/contracts — list with optional client/status/effective-date\nfilters, ordered by contract number.","tags":["billing"]},"post":{"operationId":"contracts_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateContractRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractMutationResponse"}}},"description":""},"409":{"description":"duplicate contract number, idempotency key reused, or version conflict"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/contracts — instant-first (IF1/IF2): idempotency-keyed and\nreturns the read-your-write `mutation` metadata.","tags":["billing"]}},"/api/v1/contracts/{id}":{"delete":{"description":"The response is an acknowledgement, not the aggregate: deleting is a\ncommercial action, not a rate read (RB2), so a commercial-delete holder\nwithout `financial:read` can still remove a contract and never receives\nits money. Every rate-bearing surface in this module keeps its\n`financial:read` gate.","operationId":"contracts_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractRemovedAck"}}},"description":""},"404":{"description":"contract not visible in scope"},"409":{"description":"contract has invoices"}},"security":[{"bearer":[]}],"summary":"DELETE /api/contracts/{id} — removes the contract aggregate. A typed\n404 (`rls_sparse`) means the target was never visible in scope.\nThe Wave 9 invoicing lane's billing-lock guard refuses the delete\nonce invoices reference the contract (typed 409).","tags":["billing"]},"get":{"operationId":"contracts_get_one","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractDetail"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/contracts/{id} — the contract aggregate (agreement + rate\ncard + fee schedule).","tags":["billing"]},"patch":{"operationId":"contracts_patch","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchContractRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractMutationResponse"}}},"description":""},"404":{"description":"contract not visible in scope"},"409":{"description":"version conflict, duplicate number, or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"PATCH /api/contracts/{id} — instant-first (IF1/IF2) partial update.\nAbsent fields keep their values; `rates`/`fees`, when present,\nreplace the whole card/schedule. The version gate rejects stale\nwrites with a typed 409.","tags":["billing"]}},"/api/v1/contracts/{id}/billing-preview":{"get":{"operationId":"contracts_billing_preview","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Window start (inclusive), ISO date.","in":"query","name":"from","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Window end (inclusive), ISO date.","in":"query","name":"to","required":false,"schema":{"format":"date","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingEvaluation"}}},"description":""},"404":{"description":"contract not found"},"422":{"description":"missing or inverted window"}},"security":[{"bearer":[]}],"summary":"GET /api/contracts/{id}/billing-preview — evaluate this contract's\nbilling rules over `[from, to]` and return the draft invoice-line\ncandidates. READ-ONLY: nothing persists, no billed markers exist.","tags":["billing"]}},"/api/v1/contracts/{id}/change-orders":{"post":{"operationId":"contracts_change_orders_create","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateChangeOrderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractChangeOrder"}}},"description":""},"404":{"description":"contract not visible in scope"},"409":{"description":"duplicate change order number or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/contracts/{id}/change-orders — create a DRAFT order. The\nlifecycle forward-path (approve → billed at run time) keeps billing\nhonest: only APPROVED orders ever price, and the run itself records\nwhich invoice consumed them.","tags":["billing"]}},"/api/v1/contracts/{id}/change-orders/{order_id}/transition":{"post":{"operationId":"contracts_change_orders_transition","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"order_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransitionChangeOrderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractChangeOrder"}}},"description":""},"404":{"description":"order not visible in scope"},"409":{"description":"transition not allowed from the current status"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/contracts/{id}/change-orders/{order_id}/transition — move a\ndraft order through its lifecycle. `approve` stamps the approver +\ntime (the audit trail's who/when); `reject`/`cancel` are terminal.","tags":["billing"]}},"/api/v1/contracts/{id}/sla-credits":{"post":{"operationId":"contracts_sla_credits_create","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSlaCreditRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractSlaCredit"}}},"description":""},"404":{"description":"contract not visible in scope"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/contracts/{id}/sla-credits — record a pending service\ncredit. NEGATIVE at billing: the next run whose window covers\n`applies_on` prices it as the invoice's `sla_credit` line.","tags":["billing"]}},"/api/v1/contracts/{id}/sla-credits/{credit_id}/cancel":{"post":{"operationId":"contracts_sla_credits_cancel","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"credit_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractSlaCredit"}}},"description":""},"404":{"description":"credit not visible in scope"},"409":{"description":"credit already applied"}},"security":[{"bearer":[]}],"summary":"POST /api/contracts/{id}/sla-credits/{credit_id}/cancel — withdraw a\npending credit before any run consumes it. Applied credits are\nimmutable (additive-ledger rule: corrections issue NEW credits).","tags":["billing"]}},"/api/v1/credit-notes":{"get":{"operationId":"credit_notes_list","parameters":[{"in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Exact status: `issued` | `void`.","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CreditNote"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/credit-notes — newest first, client/status filters.","tags":["billing"]},"post":{"operationId":"credit_notes_issue","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueCreditNoteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditNoteMutationResponse"}}},"description":""},"404":{"description":"client or invoice not visible in scope"},"409":{"description":"idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/credit-notes — issue a client credit through the\ninstant-first write path.","tags":["billing"]}},"/api/v1/credit-notes/{id}":{"get":{"operationId":"credit_notes_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditNoteDetail"}}},"description":""},"404":{"description":"not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/credit-notes/{id} — the credit plus its application ledger.","tags":["billing"]}},"/api/v1/credit-notes/{id}/apply":{"post":{"operationId":"credit_notes_apply","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyCreditNoteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditNoteAllocationResponse"}}},"description":""},"404":{"description":"credit note or invoice not visible in scope"},"409":{"description":"not enough remaining credit, invoice not payable, version conflict, or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/credit-notes/{id}/apply — allocate part of the remaining\ncredit onto an issued invoice. The allocation inserts a real payment\nreceipt (provider `credit_note`) through the same apply path as\nordinary payments, then moves the credit's counter in the SAME\ntransaction (both compare-and-set: over-allocation is impossible).","tags":["billing"]}},"/api/v1/credit-notes/{id}/refund":{"post":{"operationId":"credit_notes_refund","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundCreditNoteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditNoteAllocationResponse"}}},"description":""},"404":{"description":"credit note not visible in scope"},"409":{"description":"not enough remaining credit, version conflict, or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/credit-notes/{id}/refund — pay part of the remaining credit\nback to the client. v1 approves the refund locally (none-mode\nprovider, exactly like charges); the amount may never exceed the\nremaining credit.","tags":["billing"]}},"/api/v1/credit-notes/{id}/void":{"post":{"operationId":"credit_notes_void","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoidCreditNoteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditNoteMutationResponse"}}},"description":""},"404":{"description":"credit note not visible in scope"},"409":{"description":"already void, has allocations, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/credit-notes/{id}/void — withdraw a note that has moved\nnothing. Terminal.","tags":["billing"]}},"/api/v1/day-pack":{"get":{"operationId":"day_pack_read","parameters":[{"description":"The day to assemble; defaults to today (UTC). Callers with a local\ntimezone pass their own date so the panel agrees with the wall clock.","in":"query","name":"date","required":false,"schema":{"format":"date","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DayPackView"}}},"description":""}},"security":[{"bearer":[]}],"tags":["scheduling"]}},"/api/v1/day-pack/picks":{"get":{"operationId":"day_pack_picks_list","parameters":[{"in":"query","name":"ticket_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TicketPickRowView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"tags":["scheduling"]},"post":{"operationId":"day_pack_pick_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePickRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PickMutationResponse"}}},"description":""},"409":{"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"tags":["scheduling"]}},"/api/v1/day-pack/picks/{id}":{"delete":{"operationId":"day_pack_pick_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PickMutationResponse"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"tags":["scheduling"]}},"/api/v1/day-pack/rules":{"get":{"operationId":"day_pack_rules_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PackRuleView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"tags":["scheduling"]},"post":{"operationId":"day_pack_rule_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRuleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleMutationResponse"}}},"description":""},"409":{"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"tags":["scheduling"]}},"/api/v1/day-pack/rules/{id}":{"delete":{"operationId":"day_pack_rule_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleMutationResponse"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"tags":["scheduling"]},"patch":{"operationId":"day_pack_rule_patch","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchRuleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleMutationResponse"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"tags":["scheduling"]}},"/api/v1/day-pack/ticks":{"delete":{"operationId":"day_pack_tick_delete","parameters":[{"in":"query","name":"date","required":true,"schema":{"format":"date","type":"string"}},{"in":"query","name":"line_key","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TickMutationResponse"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"tags":["scheduling"]},"post":{"operationId":"day_pack_tick_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTickRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TickMutationResponse"}}},"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"tags":["scheduling"]}},"/api/v1/dedupe/workflow":{"get":{"operationId":"dedupe_get_workflow","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DedupeWorkflowView"}}},"description":""},"404":{"description":"workflow not configured yet (typed unavailable state)"}},"security":[{"bearer":[]}],"summary":"GET /api/dedupe/workflow — the org's workflow + the honest gate state.","tags":["dedupe"]},"put":{"operationId":"dedupe_put_workflow","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DedupeWorkflowRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DedupeWorkflowView"}}},"description":""},"409":{"description":"dedupe_decision_not_configured — the decision model is not configured"},"422":{"description":"threshold / schedule / entity kinds out of range"}},"security":[{"bearer":[]}],"summary":"PUT /api/dedupe/workflow — configure the intelligent dedupe workflow.\nThe gate is the ADR 0011 §3 posture: enabling requires the org's\ndecision model to be configured — the typed refusal is\n`dedupe_decision_not_configured`, never a silent turn-on.","tags":["dedupe"]}},"/api/v1/dedupe/workflow/run":{"post":{"operationId":"dedupe_run_workflow","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DedupeRunRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DedupeRunResponse"}}},"description":""},"409":{"description":"the workflow is disabled or its decision model is unconfigured"}},"security":[{"bearer":[]}],"summary":"POST /api/dedupe/workflow/run — the manual run (and the workbench's\n\"run intelligent dedupe\" trigger). Report-first: every suggestion is\nrecorded suggestion-grade and visible in the workbench; in auto_merge\nmode ONLY the at/above-threshold folds apply (audited, undoable).","tags":["dedupe"]}},"/api/v1/directory-groups":{"get":{"operationId":"directory_groups_list","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectoryGroupsListResponse"}}},"description":""},"403":{"description":"requires manage_users, manage_roles, directory_groups:read or directory_groups:write"}},"security":[{"bearer":[]}],"summary":"GET /api/directory-groups — every group in the org (manager+).","tags":["org-users"]},"post":{"operationId":"directory_groups_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDirectoryGroupRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectoryGroupMutationResponse"}}},"description":""},"409":{"description":"name or external id already in use, or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/directory-groups — create a group. The group grants nothing until\na role is mapped onto it.","tags":["org-users"]}},"/api/v1/directory-groups/{id}":{"delete":{"operationId":"directory_groups_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectoryGroupMutationResponse"}}},"description":""},"404":{"description":"group not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/directory-groups/{id} — remove the group. Its memberships and\nrole mappings go with it, so every member loses exactly what this group\ngranted.","tags":["org-users"]},"get":{"operationId":"directory_groups_detail","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectoryGroupDetailResponse"}}},"description":""},"404":{"description":"group not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/directory-groups/{id} — the group with its members and role\nmappings (manager+).","tags":["org-users"]},"patch":{"operationId":"directory_groups_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDirectoryGroupRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectoryGroupMutationResponse"}}},"description":""},"404":{"description":"group not visible in scope"},"409":{"description":"name or external id already in use, version conflict, or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"PATCH /api/directory-groups/{id} — rename, re-describe, or re-point the\nexternal id. Absent fields keep their values.","tags":["org-users"]}},"/api/v1/directory-groups/{id}/members/{userId}":{"delete":{"operationId":"directory_groups_remove_member","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"userId","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectoryGroupDetailResponse"}}},"description":""},"404":{"description":"group or membership not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/directory-groups/{id}/members/{userId} — take a user out of the\ngroup. The roles that group carried for them are gone; every other grant\nsurvives.","tags":["org-users"]},"put":{"description":"The ceiling applies to the insert and not to the no-op: adding a person is\nwhat turns the roles this group already carries into that person's grants,\nso every mapped role must be inside the caller's own set. A repeat of a\nmembership that already exists confers nothing new, which is why it stays a\n200 — refusing it would break the documented idempotency a sync depends on\nwhile closing no hole.\n\nThe 403 the ceiling raises is not added to the `utoipa` response list here:\nthe OpenAPI pins are a reviewed artifact, and this lane changes behaviour\nrather than the documented contract. The next sanctioned re-pin should add\nit to this operation and to `map_role`.","operationId":"directory_groups_add_member","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"userId","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectoryGroupDetailResponse"}}},"description":""},"404":{"description":"group or user not visible in scope"}},"security":[{"bearer":[]}],"summary":"PUT /api/directory-groups/{id}/members/{userId} — put a user in the group.\nIdempotent; a repeated call reports the same membership and emits nothing.","tags":["org-users"]}},"/api/v1/directory-groups/{id}/roles/{roleId}":{"delete":{"operationId":"directory_groups_unmap_role","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"roleId","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectoryGroupDetailResponse"}}},"description":""},"404":{"description":"group or mapping not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/directory-groups/{id}/roles/{roleId} — drop a role from the\ngroup. Members lose exactly that role where the group was its only source.","tags":["org-users"]},"put":{"description":"The ceiling applies to the mapping write itself: the role's resolved\npermission set must be inside the caller's own, or the mapping is a typed\n403 and no row is written. A no-op repeat of an existing mapping confers\nnothing new, so it stays a 200 for the same reason a repeated member add\ndoes.\n\nThe 403 stays out of the `utoipa` response list for the reason given on\n`add_member`: the pins are a reviewed artifact and this lane does not\nre-pin them.","operationId":"directory_groups_map_role","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"roleId","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectoryGroupDetailResponse"}}},"description":""},"404":{"description":"group or role not visible in scope"}},"security":[{"bearer":[]}],"summary":"PUT /api/directory-groups/{id}/roles/{roleId} — map a role onto the group.\nEvery current and future member holds it from now on (additive union with\ntheir directly assigned roles). Idempotent.","tags":["org-users"]}},"/api/v1/dispatch-connector/appointments/{id}/status":{"post":{"operationId":"dispatch_connector_push_status","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushStatusRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushStatusResponse"}}},"description":""},"404":{"description":"appointment not visible in scope"},"409":{"description":"idempotency key reused with a different payload"},"422":{"description":"unknown status vocabulary"},"429":{"description":"rate limit exceeded (Retry-After header)"}},"security":[{"bearer":[]},{"api_key":[]}],"summary":"POST /api/dispatch-connector/appointments/{id}/status — push one\nfield-service status update. Idempotent via the instant envelope: the\nsame key + payload replays the same stored row (never double-pushes);\na foreign-org appointment id is a bare 404.","tags":["dispatch-connector"]}},"/api/v1/dispatch-connector/schedule":{"get":{"operationId":"dispatch_connector_schedule","parameters":[{"in":"query","name":"from","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Window length in days (1..=62, default 7).","in":"query","name":"days","required":false,"schema":{"format":"int32","minimum":0,"type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorScheduleView"}}},"description":""},"403":{"description":"missing dispatch:read scope (keys) or not an org admin (humans)"}},"security":[{"bearer":[]},{"api_key":[]}],"summary":"GET /api/dispatch-connector/schedule — bookings in a bounded window\nwith technician/ticket/client coordinates and the latest pushed\nfield-service status per booking.","tags":["dispatch-connector"]}},"/api/v1/dispatch-connector/technicians":{"get":{"operationId":"dispatch_connector_technicians","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorTechniciansView"}}},"description":""},"403":{"description":"missing dispatch:read scope (keys) or not an org admin (humans)"}},"security":[{"bearer":[]},{"api_key":[]}],"summary":"GET /api/dispatch-connector/technicians — active technicians with\ntheir current (non-expired) skills.","tags":["dispatch-connector"]}},"/api/v1/dispatch/assign":{"post":{"operationId":"dispatch_assign","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispatchAppointment"}}},"description":""},"409":{"description":"slot already booked"}},"security":[{"bearer":[]}],"summary":"POST /api/dispatch/assign — place an unassigned ticket on a\ntechnician's board: creates the booking AND flips the ticket\nassignee in one transaction.","tags":["scheduling"]}},"/api/v1/dispatch/bulk-move":{"post":{"description":"Every move is re-validated against the TARGET user's live bookings\nin the union window (each moved booking vacates its old slot, so\nswaps are legal); the first violating move rejects the WHOLE batch\nwith a typed 409 naming the move index and blocking booking.","operationId":"dispatch_bulk_move","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkMoveRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkMoveResponse"}}},"description":""},"409":{"description":"target slot already booked (typed conflict)"}},"security":[{"bearer":[]}],"summary":"POST /api/dispatch/bulk-move — atomic batch reschedule.","tags":["scheduling"]}},"/api/v1/dispatch/capacity":{"get":{"operationId":"dispatch_capacity","parameters":[{"in":"query","name":"from","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Horizon length in days (1..=31).","in":"query","name":"days","required":false,"schema":{"format":"int32","minimum":0,"type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CapacityHorizonView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/dispatch/capacity — multi-day capacity horizon: the day view's\ncapacity math repeated per UTC day over the window, one org-wide\navailability read + one exclusion read + one appointment read.","tags":["scheduling"]}},"/api/v1/dispatch/day":{"get":{"operationId":"dispatch_day","parameters":[{"description":"The day to assemble; defaults to today (UTC).","in":"query","name":"date","required":false,"schema":{"format":"date","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispatchDayView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/dispatch/day — the whole board for one day.","tags":["scheduling"]}},"/api/v1/dispatch/journey-start":{"get":{"operationId":"dispatch_journey_start_get","parameters":[{"description":"Whose journey; defaults to the caller (another user requires\nowner/admin).","in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"The day; defaults to today (UTC).","in":"query","name":"date","required":false,"schema":{"format":"date","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JourneyStartView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/dispatch/journey-start — the resolved start point plus the\nexplicit override, if any.","tags":["scheduling"]},"put":{"operationId":"dispatch_journey_start_set","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetJourneyStartRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JourneyStartMutationResponse"}}},"description":""},"404":{"description":"site not found in scope"},"409":{"description":"idempotency key reused"},"422":{"description":"invalid start point"}},"security":[{"bearer":[]}],"summary":"PUT /api/dispatch/journey-start — set (or replace) the technician's\nstart point for one day.","tags":["scheduling"]}},"/api/v1/dispatch/match":{"get":{"operationId":"dispatch_match","parameters":[{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/dispatch/match — for every unassigned open ticket carrying\nrequired skills, the active technicians sorted best-first by how many\nrequired skills they are missing (0 = fully qualified). Expired\ncertifications never count as held.","tags":["scheduling"]}},"/api/v1/dispatch/planning/allocations":{"get":{"operationId":"dispatch_phase_allocations_list","parameters":[{"in":"query","name":"project_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhaseAllocationListView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/dispatch/planning/allocations — who is committed to which phase.","tags":["scheduling"]},"put":{"description":"Upsert on `(phase_id, user_id)`, so restating a share updates the row.\nAppending instead would leave two contradictory shares that SUM in the\nforecast and report a technician as over-committed by construction.","operationId":"dispatch_phase_allocation_upsert","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhaseAllocationMutation"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhaseAllocationView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PUT /api/dispatch/planning/allocations — state (or restate) a\ntechnician's commitment to a phase.","tags":["scheduling"]}},"/api/v1/dispatch/planning/allocations/{allocation_id}":{"delete":{"operationId":"dispatch_phase_allocation_delete","parameters":[{"in":"path","name":"allocation_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"no such allocation"}},"security":[{"bearer":[]}],"summary":"DELETE /api/dispatch/planning/allocations/{id} — release a commitment.","tags":["scheduling"]}},"/api/v1/dispatch/planning/forecast":{"get":{"operationId":"dispatch_capacity_forecast","parameters":[{"in":"query","name":"from","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Horizon length in days (1..=31).","in":"query","name":"days","required":false,"schema":{"format":"int32","minimum":0,"type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CapacityForecastView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/dispatch/planning/forecast — committed project load per\ntechnician over a window.","tags":["scheduling"]}},"/api/v1/dispatch/queue":{"get":{"operationId":"dispatch_queue_list","parameters":[{"description":"Page size, clamped to [1, 500].","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispatchQueueView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/dispatch/queue — the full unassigned queue with dispatcher\noverrides applied: overridden tickets sort by explicit rank first\n(ties fall back to the computed order), then everything else in the\ncomputed order (priority rank, then effective SLA/due deadline).","tags":["scheduling"]}},"/api/v1/dispatch/queue/{ticket_id}":{"delete":{"operationId":"dispatch_queue_clear","parameters":[{"in":"path","name":"ticket_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueueMutationResponse"}}},"description":""},"404":{"description":"ticket not visible"}},"security":[{"bearer":[]}],"summary":"DELETE /api/dispatch/queue/{ticket_id} — clear a rank override.\nIdempotent: clearing an unset override still returns 200 with\n`cleared: false` (the dispatcher's intent is already satisfied).","tags":["scheduling"]},"put":{"operationId":"dispatch_queue_set","parameters":[{"in":"path","name":"ticket_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetQueueRankRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueueMutationResponse"}}},"description":""},"404":{"description":"ticket not visible or not open/unassigned"},"409":{"description":"version conflict or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"PUT /api/dispatch/queue/{ticket_id} — pin a ticket's queue position.\nThe ticket must exist and still be open+unassigned (an override for a\nplaced ticket is meaningless); idempotent via the instant envelope.","tags":["scheduling"]}},"/api/v1/dispatch/reconciliation":{"get":{"operationId":"dispatch_reconciliation","parameters":[{"description":"Inclusive window start (default: 14 days before `to`).","in":"query","name":"from","required":false,"schema":{"format":"date-time","type":["string","null"]}},{"description":"Exclusive window end (default: now). Capped at 366 days.","in":"query","name":"to","required":false,"schema":{"format":"date-time","type":["string","null"]}},{"in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReconciliationView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/dispatch/reconciliation — planned-vs-actual rollup over the\nexisting travel drafts and their linked expenses: mileage, time, and\nreimbursements per technician, in the org's base currency.","tags":["scheduling"]}},"/api/v1/dispatch/routing":{"get":{"operationId":"dispatch_routing_day","parameters":[{"description":"The day to assemble; defaults to today (UTC), matching the board.","in":"query","name":"date","required":false,"schema":{"format":"date","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoutingDayView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/dispatch/routing — the day's journey plans, typed warnings,\nand (measured providers only) suggestion-grade route improvements.","tags":["scheduling"]}},"/api/v1/dispatch/skills":{"get":{"operationId":"dispatch_skills_catalog","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkillsCatalogView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/dispatch/skills — the org's skill/certification catalog.","tags":["scheduling"]}},"/api/v1/dispatch/tickets/{ticket_id}/required-skills":{"put":{"operationId":"dispatch_ticket_skills_replace","parameters":[{"in":"path","name":"ticket_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplaceTicketSkillsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketSkillsMutationResponse"}}},"description":""},"404":{"description":"ticket not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"validation failed (labels, duplicates, count)"}},"security":[{"bearer":[]}],"summary":"PUT /api/dispatch/tickets/{ticket_id}/required-skills — full-replace\nthe job's required-skill set. The ticket must exist and be open +\nunassigned (requirements on placed work are history, not planning).","tags":["scheduling"]}},"/api/v1/dispatch/travel-drafts":{"get":{"operationId":"dispatch_travel_draft_list","parameters":[{"in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"`draft` | `submitted`.","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}},{"description":"Page size, clamped to [1, 200].","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TravelDraftView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/dispatch/travel-drafts — the caller's drafts (all users for\nowner/admin via `user_id`), newest first.","tags":["scheduling"]},"post":{"operationId":"dispatch_travel_draft_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTravelDraftRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TravelDraftMutationResponse"}}},"description":""},"404":{"description":"ticket not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/dispatch/travel-drafts — a draft mileage row against a\nticket. Instant-first (IF1/IF2): idempotency-keyed, version-gated,\nwrite-trailed.","tags":["scheduling"]}},"/api/v1/dispatch/travel-drafts/{id}":{"delete":{"operationId":"dispatch_travel_draft_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TravelDraftMutationResponse"}}},"description":""},"404":{"description":"not visible, or already submitted"}},"security":[{"bearer":[]}],"summary":"DELETE /api/dispatch/travel-drafts/{id} — discard a draft. Submitted\ndrafts are immutable history (delete the TIME ENTRY through invoicing\ncorrections, never the draft's backlink).","tags":["scheduling"]},"patch":{"operationId":"dispatch_travel_draft_patch","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchTravelDraftRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TravelDraftMutationResponse"}}},"description":""},"404":{"description":"not visible, or already submitted"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"PATCH /api/dispatch/travel-drafts/{id} — edit a draft. Submitted\ndrafts are frozen (the time entry already exists).","tags":["scheduling"]}},"/api/v1/dispatch/travel-drafts/{id}/expense":{"post":{"operationId":"dispatch_travel_draft_expense","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTravelExpenseRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseMutationResponse"}}},"description":""},"404":{"description":"draft not found in scope"},"409":{"description":"draft already expensed or key reused"},"422":{"description":"invalid amount"}},"security":[{"bearer":[]}],"summary":"POST /api/dispatch/travel-drafts/{id}/expense — book the draft's\nmileage as a ticket expense at the org rate (the reimbursement the\nbusiness owes the technician or bills the client). One expense per\ndraft, idempotent through the standard envelope.","tags":["scheduling"]}},"/api/v1/dispatch/travel-drafts/{id}/submit":{"post":{"operationId":"dispatch_travel_draft_submit","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitTravelDraftRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryMutationResponse"}}},"description":""},"404":{"description":"draft not visible in scope (rls_sparse)"},"409":{"description":"already submitted, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/dispatch/travel-drafts/{id}/submit — convert a draft into a\nreal time entry through the EXISTING time-entry path\n(`insert_time_entry` + the `time_entry.created` event), then freeze\nthe draft with the entry backlink. The entry's notes carry the\nmachine-readable provenance marker (`travel_draft:<id>`); the write\ntrail records the full before/after so \"who did this\" and undo stay\nqueryable. Idempotent: the same submit replays the SAME entry.","tags":["scheduling"]}},"/api/v1/dispatch/users/{user_id}/skills":{"put":{"operationId":"dispatch_user_skills_replace","parameters":[{"in":"path","name":"user_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplaceUserSkillsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSkillsMutationResponse"}}},"description":""},"404":{"description":"user not visible in scope"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"validation failed (labels, expiry, duplicates)"}},"security":[{"bearer":[]}],"summary":"PUT /api/dispatch/users/{user_id}/skills — full-replace the\ntechnician's skill set. The technician must be an active member of\nthis org.","tags":["scheduling"]}},"/api/v1/dispatch/work-next":{"delete":{"operationId":"dispatch_work_next_remove","parameters":[{"in":"query","name":"ticket_id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/dispatch/work-next?ticket_id=… — drop a ticket from one\ntechnician's queue, or from every queue when `user_id` is absent.","tags":["scheduling"]},"get":{"operationId":"dispatch_work_next_list","parameters":[{"description":"Whose queue to read. Admins may read another technician's (a\ndispatcher setting up a shift); anyone may read their own, and a\nnon-admin asking for someone else is refused rather than silently\nserved their own.","in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkNextQueueView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/dispatch/work-next — the caller's curated queue, or another\ntechnician's when the caller may see theirs.","tags":["scheduling"]},"put":{"operationId":"dispatch_work_next_add","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkNextMutation"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkNextEntryView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PUT /api/dispatch/work-next — add a ticket to a technician's queue at a\nposition, or move it there if the pair already exists.","tags":["scheduling"]}},"/api/v1/dispatch/work-next/reorder":{"post":{"description":"Returns the WHOLE queue rather than just the moved row because the move\nshifts everything between the old and new rank. A response with only the\nmoved entry would leave the client guessing the positions of everything\nit did not drag, which is exactly the state a drag-and-drop list must not\nbe in.","operationId":"dispatch_work_next_reorder","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkNextMutation"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/WorkNextEntryView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/dispatch/work-next/reorder — move an entry and return the\ndensified queue.","tags":["scheduling"]}},"/api/v1/documents":{"get":{"operationId":"documents_list","parameters":[{"description":"Title substring narrowing; ranked full-text search rides /api/search.","in":"query","name":"q","required":false,"schema":{"type":["string","null"]}},{"description":"Scope narrow: a client uuid, or the literal `global` for org-wide\ndocuments (docs-platform v1). Absent = all scopes.","in":"query","name":"client_id","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"layout_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Document"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/documents","tags":["documents"]},"post":{"operationId":"documents_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDocumentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentMutationResponse"}}},"description":""},"409":{"description":"idempotency key reused"},"422":{"description":"title/body rejected by document rules"}},"security":[{"bearer":[]}],"summary":"POST /api/documents — instant-first (IF1/IF2); creates the document and\nits immutable version 1 in one commit.","tags":["documents"]}},"/api/v1/documents/layouts":{"get":{"operationId":"documents_list_layouts","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/DocumentLayout"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/documents/layouts — the org's layout catalog (system first).","tags":["documents"]},"post":{"operationId":"documents_create_layout","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLayoutRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LayoutMutationResponse"}}},"description":""},"409":{"description":"idempotency key reused or slug taken"},"422":{"description":"invalid field schema"}},"security":[{"bearer":[]}],"summary":"POST /api/documents/layouts — instant-first; system slugs are reserved.","tags":["documents"]}},"/api/v1/documents/layouts/{id}":{"delete":{"operationId":"documents_delete_layout","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"deleted"},"404":{"description":"layout not visible in scope"},"409":{"description":"system layouts cannot be deleted"}},"security":[{"bearer":[]}],"summary":"DELETE /api/documents/layouts/{id} — custom layouts only; system\nlayouts are a typed 409 (imports and the DMS seam rely on them).","tags":["documents"]},"patch":{"operationId":"documents_update_layout","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLayoutRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LayoutMutationResponse"}}},"description":""},"404":{"description":"layout not visible in scope"}},"security":[{"bearer":[]}],"summary":"PATCH /api/documents/layouts/{id} — instant-first; system layouts are\neditable (schema and branding), never deletable.","tags":["documents"]}},"/api/v1/documents/links/{id}":{"delete":{"operationId":"documents_delete_link","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"deleted"},"404":{"description":"link not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/documents/links/{id}","tags":["documents"]}},"/api/v1/documents/{id}":{"get":{"operationId":"documents_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentView"}}},"description":""},"404":{"description":"document not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/documents/{id}","tags":["documents"]},"patch":{"operationId":"documents_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDocumentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentMutationResponse"}}},"description":""},"404":{"description":"document not visible in scope"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"title/body rejected by document rules"}},"security":[{"bearer":[]}],"summary":"PATCH /api/documents/{id} — instant-first (IF1/IF2); an edit APPENDS\nthe next immutable version and moves the document's head to it. No\nexisting version row is touched.","tags":["documents"]}},"/api/v1/documents/{id}/attachments":{"get":{"operationId":"document_attachments_list","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentAttachmentList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/documents/{id}/attachments — newest-first list for one doc.","tags":["documents"]},"post":{"operationId":"document_attachments_upload","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"items":{"format":"int32","minimum":0,"type":"integer"},"type":"array"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentAttachmentView"}}},"description":""},"404":{"description":"document not visible in scope"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/documents/{id}/attachments — raw octet-stream upload (same\nheader contract as the ticket surface: `x-filename` + declared\ncontent-type verified against sniffed magic bytes).","tags":["documents"]}},"/api/v1/documents/{id}/attachments/{attachment_id}":{"delete":{"operationId":"document_attachments_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"attachment_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentAttachmentDeleted"}}},"description":""},"404":{"description":"attachment not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/documents/{id}/attachments/{attachment_id} — author-gated\n(the uploader, or an org owner/admin). The stored object is reclaimed\nonly when NO row in EITHER attachments table still references the same\ncontent-addressed key.","tags":["documents"]}},"/api/v1/documents/{id}/attachments/{attachment_id}/download":{"get":{"description":"Same rule as the ticket attachment door: the stream is the contract and\nthe ticket is the optimisation, and no URL is minted for an object whose\nscan verdict has not been read and admitted.","operationId":"document_attachments_download","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"attachment_id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"`true` asks for a download ticket instead of the bytes; the answer says whether a URL was minted","in":"query","name":"presign","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentDownloadTicket"}},"application/octet-stream":{"schema":{"items":{"format":"int32","minimum":0,"type":"integer"},"type":"array"}}},"description":""},"409":{"description":"the object is held by the scan gate: neither a URL nor the bytes are issued"}},"security":[{"bearer":[]}],"summary":"GET /api/documents/{id}/attachments/{attachment_id}/download — streams\nthe stored object behind documents authz, or answers with a presigned URL\nwhen the caller asks for one (`?presign=true`) and the store can sign it.","tags":["documents"]}},"/api/v1/documents/{id}/links":{"get":{"operationId":"documents_list_links","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ResolvedLink"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"tags":["documents"]},"post":{"operationId":"documents_add_link","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ResolvedLink"},"type":"array"}}},"description":""},"404":{"description":"document or link target not visible in scope"},"409":{"description":"link already exists"}},"security":[{"bearer":[]}],"summary":"POST /api/documents/{id}/links — target existence is enforced by the\nresolution join (the link row itself allows any uuid, so the route\nchecks the target kind's table exists for the org before insert).","tags":["documents"]}},"/api/v1/documents/{id}/restore":{"post":{"operationId":"documents_restore","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestoreDocumentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentMutationResponse"}}},"description":""},"404":{"description":"document or restore target not visible in scope"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"restore rejected by document rules"}},"security":[{"bearer":[]}],"summary":"POST /api/documents/{id}/restore — instant-first (IF1/IF2); §6 rule:\nthe restore APPENDS a new version copying the target's content. The\ntarget's title comes along, renames after it are not silently undone.","tags":["documents"]}},"/api/v1/documents/{id}/versions":{"get":{"operationId":"documents_list_versions","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/DocumentVersion"},"type":"array"}}},"description":""},"404":{"description":"document not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/documents/{id}/versions — the full immutable history, newest\nfirst, complete bodies included so any pair is diffable.","tags":["documents"]}},"/api/v1/documents/{id}/versions/{version}":{"get":{"operationId":"documents_get_version","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"version","required":true,"schema":{"format":"int32","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentVersion"}}},"description":""},"404":{"description":"document or version not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/documents/{id}/versions/{version} — one immutable snapshot.","tags":["documents"]}},"/api/v1/email/delivery-ledger":{"get":{"operationId":"email_delivery_ledger_get","parameters":[{"description":"queued | sending | sent | dead","in":"query","name":"status","required":false,"schema":{"type":"string"}},{"description":"page size (default 25, max 100)","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"description":"opaque next-page cursor","in":"query","name":"cursor","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryLedgerResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/email/delivery-ledger — the outbound message ledger\n(queued/sending/sent/dead) with attempts, last error, and counts.","tags":["email"]}},"/api/v1/email/inbound-holds":{"get":{"operationId":"email_inbound_holds_list","parameters":[{"description":"Defaults to 50, capped at 200.","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HeldMessagesView"}}},"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/email/inbound-holds — messages waiting for a decision, newest\nfirst, with the reason each is held and the engine's proposal when the org\nruns shadow or autonomous.","tags":["email"]}},"/api/v1/email/inbound-holds/{id}/decide":{"post":{"operationId":"email_inbound_hold_decide","parameters":[{"description":"Ledger message id","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecideHeldRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecideHeldResponse"}}},"description":""},"403":{"description":""},"404":{"description":"no such message in this org"},"409":{"description":"already decided"},"422":{"description":"unknown decision, no ticket to attach to, or a reason over the cap"}},"security":[{"bearer":[]}],"summary":"POST /api/email/inbound-holds/{id}/decide — the person's decision on one\nheld message. `accept` threads it onto the ticket and leaves a\nclient-visible comment; `refuse` records the refusal. Both write the\ndecision columns, an audit row naming the person, and\n`email.sender_binding_decided`. A message that is no longer held answers a\ntyped 409, so a decision cannot thread twice.","tags":["email"]}},"/api/v1/email/inbound-settings":{"get":{"operationId":"email_inbound_settings_get","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundSettingsResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/email/inbound-settings — current org catch-all, if configured.","tags":["email"]},"put":{"operationId":"email_inbound_settings_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutInboundSettingsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundSettingsResponse"}}},"description":""},"409":{"description":"address already routed"}},"security":[{"bearer":[]}],"summary":"PUT /api/email/inbound-settings — upsert the org catch-all route.","tags":["email"]}},"/api/v1/email/outbound-settings":{"put":{"operationId":"email_outbound_settings_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutOutboundSettingsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailSettingsResponse"}}},"description":""},"422":{"description":"invalid value"}},"security":[{"bearer":[]}],"summary":"PUT /api/email/outbound-settings — partial update of the org outbound\nidentity, transport selection, and digest policy. Values are validated\nagainst their registry declarations before any write.","tags":["email"]}},"/api/v1/email/settings":{"get":{"operationId":"email_settings_get","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailSettingsResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/email/settings — the whole mail surface in one read: inbound\nroutes + per-board identities, outbound identity + transport readiness\n(never credential material), the delivery ledger's enablement bits, and\nthe org digest policy.","tags":["email"]}},"/api/v1/email/test-send":{"post":{"operationId":"email_test_send","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestSendRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestSendResponse"}}},"description":""},"409":{"description":"transport not configured"}},"security":[{"bearer":[]}],"summary":"POST /api/email/test-send — renders a branded test message and enqueues\nit through the configured transport. Refuses with a typed conflict when\nno transport is configured (the honest not-configured state).","tags":["email"]}},"/api/v1/email/transport-credentials":{"delete":{"operationId":"email_transport_credentials_delete","parameters":[{"description":"postmark | smtp | ses","in":"query","name":"transport","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialsView"}}},"description":""},"422":{"description":"unknown transport"}},"security":[{"bearer":[]}],"summary":"DELETE /api/email/transport-credentials?transport=… — removes the sealed\ncredential; the transport then reports not-configured (mail stays queued).","tags":["email"]},"put":{"operationId":"email_transport_credentials_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutTransportCredentialsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialsView"}}},"description":""},"422":{"description":"unknown transport or missing secret"}},"security":[{"bearer":[]}],"summary":"PUT /api/email/transport-credentials — seals the selected transport's\ncredential into the sealed slot (presence-only response). The plaintext\nexists exactly once, in this request.","tags":["email"]}},"/api/v1/entity-dedupe/candidates":{"get":{"operationId":"entity_dedupe_list_candidates","parameters":[{"description":"contact | client","in":"query","name":"kind","required":false,"schema":{"type":["string","null"]}},{"description":"pending | auto_merged | accepted | rejected | deferred","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/EntityCandidateView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/entity-dedupe/candidates — the client/contact review queue.","tags":["entity-dedupe"]}},"/api/v1/entity-dedupe/candidates/batch":{"post":{"operationId":"entity_dedupe_batch_candidates","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntityBatchRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntityBatchResponse"}}},"description":""},"422":{"description":"unknown batch action / empty id list"}},"security":[{"bearer":[]}],"summary":"POST /api/entity-dedupe/candidates/batch — accept/reject/defer N.","tags":["entity-dedupe"]}},"/api/v1/entity-dedupe/candidates/{id}/accept":{"post":{"operationId":"entity_dedupe_accept_candidate","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntityMerge"}}},"description":""},"404":{"description":"candidate not visible in scope"},"409":{"description":"candidate already decided"}},"security":[{"bearer":[]}],"summary":"POST /api/entity-dedupe/candidates/{id}/accept — contact: audited +\nundoable merge; client: writes the durable operator map.","tags":["entity-dedupe"]}},"/api/v1/entity-dedupe/candidates/{id}/defer":{"post":{"operationId":"entity_dedupe_defer_candidate","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntityMergeCandidate"}}},"description":""},"404":{"description":"candidate not visible in scope"},"409":{"description":"candidate already decided"}},"security":[{"bearer":[]}],"summary":"POST /api/entity-dedupe/candidates/{id}/defer — park without deciding.","tags":["entity-dedupe"]}},"/api/v1/entity-dedupe/candidates/{id}/reject":{"post":{"operationId":"entity_dedupe_reject_candidate","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntityMergeCandidate"}}},"description":""},"404":{"description":"candidate not visible in scope"},"409":{"description":"candidate already decided"}},"security":[{"bearer":[]}],"summary":"POST /api/entity-dedupe/candidates/{id}/reject — keep both rows (or\nkeep the vendor org unmapped).","tags":["entity-dedupe"]}},"/api/v1/entity-dedupe/candidates/{id}/restore":{"post":{"operationId":"entity_dedupe_restore_candidate","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntityMergeCandidate"}}},"description":""},"404":{"description":"candidate not visible in scope"},"409":{"description":"candidate is not deferred"}},"security":[{"bearer":[]}],"summary":"POST /api/entity-dedupe/candidates/{id}/restore — un-defer.","tags":["entity-dedupe"]}},"/api/v1/entity-dedupe/candidates/{id}/workbench":{"get":{"operationId":"entity_dedupe_candidate_workbench","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntityWorkbenchView"}}},"description":""},"404":{"description":"candidate not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/entity-dedupe/candidates/{id}/workbench — the client/contact\nside of the guided merge workbench.","tags":["entity-dedupe"]}},"/api/v1/entity-dedupe/merges/{id}/split":{"post":{"operationId":"entity_dedupe_split_merge","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntityMerge"}}},"description":""},"404":{"description":"merge not visible in scope"},"409":{"description":"merge already undone"}},"security":[{"bearer":[]}],"summary":"POST /api/entity-dedupe/merges/{id}/split — undo a contact merge (IF7).","tags":["entity-dedupe"]}},"/api/v1/events/stream":{"get":{"description":"Response is `text/event-stream`: `data:` frames are the StreamFrame\nJSON documented above, each carrying its event uuid as the SSE `id:`\nfield; comment heartbeats every 15s keep intermediaries from closing\nan idle connection. A per-user connection cap (8) guards fan-out\nmemory; over the cap the request is a typed 429. A `Last-Event-ID`\nheader triggers a bounded outbox replay of the events missed while\ndisconnected (see the resume section above); past the replay buffer\nthe client falls back to full snapshot rehydration.","operationId":"events_stream","parameters":[{"description":"Optional per-entity scope: only frames for this ticket (its events\nAND its presence) are delivered. Omit for the org-wide stream.","in":"query","name":"ticket","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"text/event-stream":{}},"description":"text/event-stream of StreamFrame JSON data lines with id: fields and comment heartbeats; honors Last-Event-ID for bounded resume replay"},"401":{"description":"missing or invalid bearer token"},"429":{"description":"per-user connection cap reached; reconnect later"}},"security":[{"bearer":[]}],"summary":"GET /api/events/stream — live contract-event + presence frames.","tags":["events"]}},"/api/v1/external-links/{id}":{"delete":{"operationId":"external_links_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""},"404":{"description":"link not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/external-links/{id}","tags":["purchasing"]}},"/api/v1/fx/rates":{"get":{"operationId":"fx_rates_list","parameters":[{"in":"query","name":"base_currency","required":false,"schema":{"type":"string"}},{"in":"query","name":"quote_currency","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FxRateEntry"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/fx/rates — the org's append-only rate book, newest first.","tags":["fx"]},"post":{"operationId":"fx_rates_record","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordRateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateMutationResponse"}}},"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/fx/rates — manual entry (the built-in provider's write path).\nAppend-only: corrections are new rows; the effective-rate resolver\nalways picks the newest capture on-or-before the requested date.","tags":["fx"]}},"/api/v1/fx/rates/resolve":{"get":{"operationId":"fx_rates_resolve","parameters":[{"in":"query","name":"from","required":true,"schema":{"type":"string"}},{"in":"query","name":"to","required":true,"schema":{"type":"string"}},{"in":"query","name":"as_of","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FxRateEntry"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/fx/rates/resolve — the effective rate for one direction on\none date (defaults to today). Read-only; mirrors the resolver the\nlock-moment write paths use.","tags":["fx"]}},"/api/v1/fx/reconciliation":{"get":{"operationId":"fx_reconciliation","parameters":[{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FxReconciliationRow"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/fx/reconciliation — the FX deliverable to the books (FX7):\noriginal amount, rate, source/date, base equivalent, sync target. The\ncore records conversions that happened; realized/unrealized gain-loss\nstays in the accounting system.","tags":["fx"]}},"/api/v1/imports/batches":{"get":{"operationId":"imports_list_batches","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BatchViewResponse"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/imports/batches","tags":["imports"]},"post":{"operationId":"imports_create_batch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBatchRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchCreatedResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/imports/batches","tags":["imports"]}},"/api/v1/imports/batches/{batch_id}":{"get":{"operationId":"imports_get_batch","parameters":[{"in":"path","name":"batch_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchViewResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/imports/batches/{id}","tags":["imports"]}},"/api/v1/imports/batches/{batch_id}/chunks":{"post":{"operationId":"imports_post_chunk","parameters":[{"in":"path","name":"batch_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChunkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunReportResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/imports/batches/{id}/chunks — plans then executes ONE chunk\natomically. A fail-policy chunk with any conflict persists nothing and\nreturns 409 with the full per-row report.","tags":["imports"]}},"/api/v1/imports/batches/{batch_id}/finish":{"post":{"operationId":"imports_finish_batch","parameters":[{"in":"path","name":"batch_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchViewResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/imports/batches/{id}/finish — closes the span, emits\n`import.batch_finished`, and records cumulative counters in audit.","tags":["imports"]}},"/api/v1/imports/batches/{batch_id}/run":{"post":{"operationId":"imports_run_batch","parameters":[{"in":"path","name":"batch_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunBatchResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/imports/batches/{id}/run — hands the staged batch to the\nworker queue exactly once (generalized 2.7 job queue). Re-running a\nfinished or queued batch is a typed 409; the worker replays chunks\nidempotently through the SAME commit path as synchronous pushes.","tags":["imports"]}},"/api/v1/imports/batches/{batch_id}/stage":{"post":{"operationId":"imports_stage_chunk","parameters":[{"in":"path","name":"batch_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StageChunkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StageChunkResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/imports/batches/{id}/stage — uploads ONE chunk of rows to the\nObjectStore under a deterministic key WITHOUT executing anything. An\noptional saved template is applied at stage time so the stored chunk is\nalready canonical; the worker later replays the staged sequence.","tags":["imports"]}},"/api/v1/imports/dry-run":{"post":{"operationId":"imports_dry_run","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DryRunRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunReportResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/imports/dry-run","tags":["imports"]}},"/api/v1/imports/templates":{"get":{"operationId":"imports_list_templates","parameters":[{"description":"Narrow to one source system","in":"query","name":"source_system","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TemplateViewResponse"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/imports/templates","tags":["imports"]},"post":{"operationId":"imports_create_template","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateImportTemplateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateViewResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/imports/templates — saves one reusable source→canonical column\nmapping. Targets are validated against the canonical field catalog for\nthe entity kind; duplicates per (org, source system, kind, name) are a\ntyped 409.","tags":["imports"]}},"/api/v1/imports/templates/{template_id}":{"delete":{"operationId":"imports_delete_template","parameters":[{"in":"path","name":"template_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateViewResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/imports/templates/{id}","tags":["imports"]},"get":{"operationId":"imports_get_template","parameters":[{"in":"path","name":"template_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateViewResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/imports/templates/{id}","tags":["imports"]},"put":{"operationId":"imports_update_template","parameters":[{"in":"path","name":"template_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTemplateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateViewResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PUT /api/imports/templates/{id} — content update; uniqueness conflicts\n(renaming onto an existing sibling) surface as typed 409s.","tags":["imports"]}},"/api/v1/internal/effects":{"post":{"description":"The subject may be ticket-less (Wave 43 lane 3): a board-scoped rule\nwhose envelope carried no ticket. Then the board is resolved for scope\nand only subject-agnostic effects (notifications) run — every\nticket-writing effect skips with a warn, never fails the batch, so one\nbad rule can never wedge the queue.\n\n# The batch runs as the rule's author (Wave 58 lane `p58-effects-seam`)\n\nThe token is one deployment-wide bearer, so before this lane the request's\n`org_id` was both the RLS scope and the actor: the holder of the token could\nname any org and write there with no permission check. The principal is now\nthe author recorded on `automation_rules.created_by`, resolved into a full\n[`Principal`] exactly as a session builds one, and the grant a manual write\nwould need is applied before dispatch ([`effects_authority`]). A rule with\nno author is refused rather than attributed: the remedy is an admin\nre-saving it, which records the editor as the author. The batch's action\nlist must be the rule's own, so a rule id authorises that rule's effects and\nnot the author's whole authority. Every refusal is a\ntyped 4xx with an `automation.effects_denied` audit row, and nothing the\nbatch asked to write survives: the identity and matrix gates refuse before\nthe write transaction opens, and a board denial rolls it back.","operationId":"internal_effects","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EffectsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":""}},"summary":"POST /api/internal/effects — applies automation-rule effects to one\nticket through the SAME write paths users go through (ticket patch,\ncomment insert, notification fan-out), emitting every corresponding\ndomain event tagged `via_automation` so evaluators never loop.","tags":["automations"]}},"/api/v1/internal/sla/verdict":{"post":{"operationId":"internal_sla_verdict","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlaVerdictRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerdictResponse"}}},"description":""}},"summary":"POST /api/internal/sla/verdict — fire-once application of one ladder\nstep: ledger insert (PK-guarded), breach flip, notification fan-out with\npreference gates, events + audit, all in ONE transaction.","tags":["sla"]}},"/api/v1/invoice-candidates":{"get":{"operationId":"invoice_candidates_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/InvoiceCandidateView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/invoice-candidates — every billable, not-yet-billed cost line\nand expense in the org, oldest first. The invoicing run consumes rows\nby writing `invoice_line_id` through the costs module's marker.","tags":["ticket-costs"]}},"/api/v1/invoices":{"get":{"operationId":"invoices_list","parameters":[{"in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"contract_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Exact status: `draft|issued|paid|void`.","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}},{"description":"Exact dunning state:\n`none|reminder_sent|overdue|final_notice|collections`.","in":"query","name":"dunning_state","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Invoice"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/invoices — list with client/contract/status/dunning filters.","tags":["billing"]}},"/api/v1/invoices/{id}":{"get":{"operationId":"invoices_get_one","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceDetail"}}},"description":""},"404":{"description":"invoice not found"}},"security":[{"bearer":[]}],"summary":"GET /api/invoices/{id} — the invoice aggregate (document, lines,\npayment receipts).","tags":["billing"]}},"/api/v1/invoices/{id}/document":{"get":{"operationId":"invoices_document","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"text/html":{"schema":{"type":"string"}}},"description":""},"404":{"description":"invoice not found"}},"security":[{"bearer":[]}],"summary":"GET /api/invoices/{id}/document — the print-ready HTML invoice\ndocument (print-to-PDF from any browser; v1 ships no PDF binary\noutput — see the private build tracker).","tags":["billing"]}},"/api/v1/invoices/{id}/dunning":{"post":{"operationId":"invoices_dunning_transition","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DunningTransitionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceMutationResponse"}}},"description":""},"404":{"description":"invoice not visible in scope"},"409":{"description":"invoice not issuable, version conflict, or idempotency key reused"},"422":{"description":"backwards or same-state transition"}},"security":[{"bearer":[]}],"summary":"POST /api/invoices/{id}/dunning — move an ISSUED invoice's dunning\nstate forward (none → reminder_sent → overdue → final_notice →\ncollections; skipping rungs is allowed, backwards is refused).\nStates are data: the write emits `invoice.dunning_changed` and lands\nin the write trail. Instant-first gated (IF1/IF2).","tags":["billing"]}},"/api/v1/invoices/{id}/dunning/statement":{"post":{"operationId":"invoices_dunning_statement","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendStatementRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendStatementResponse"}}},"description":""},"404":{"description":"invoice not visible in scope"},"409":{"description":"invoice not issuable or idempotency key reused"},"422":{"description":"no contact emails on the client to deliver to"}},"security":[{"bearer":[]}],"summary":"POST /api/invoices/{id}/dunning/statement — generate an account\nstatement (the client's OPEN invoices with outstanding balances) and\nqueue it to the client's contacts through the outbound ledger\n(`MailTransport`; none-mode stays queued like every other leg). The\nstatement rides the dunning path but is a document delivery, not a\ndunning-state change. Instant-first gated (IF1).","tags":["billing"]}},"/api/v1/invoices/{id}/payments":{"get":{"operationId":"invoices_payments_list","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/InvoicePayment"},"type":"array"}}},"description":""},"404":{"description":"invoice not found"}},"security":[{"bearer":[]}],"summary":"GET /api/invoices/{id}/payments — the payment receipts.","tags":["billing"]},"post":{"operationId":"invoices_payments_create","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordPaymentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMutationResponse"}}},"description":""},"404":{"description":"invoice not visible in scope"},"409":{"description":"invoice not payable, overpayment, version conflict, or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/invoices/{id}/payments — record a payment through the\n`PaymentProvider` seam. The charge runs INSIDE the mutation\ntransaction so receipt, payment row, and invoice status commit (or\nroll back) atomically. Instant-first gated (IF1/IF2).","tags":["billing"]}},"/api/v1/invoices/{id}/void":{"post":{"operationId":"invoices_void","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoidInvoiceRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceMutationResponse"}}},"description":""},"404":{"description":"invoice not visible in scope"},"409":{"description":"invoice not voidable, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/invoices/{id}/void — withdraw an ISSUED invoice. Terminal:\nconsumption markers stay, so re-running the window can never\ndouble-bill. Instant-first gated (IF1/IF2).","tags":["billing"]}},"/api/v1/kb/articles":{"get":{"operationId":"kb_list_articles","parameters":[{"in":"query","name":"category_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"status","required":false,"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/KbArticleStatus"}]}},{"description":"Only articles passing the effective client-facing gate.","in":"query","name":"client_visible","required":false,"schema":{"type":"boolean"}},{"description":"Title substring narrowing; ranked full-text search rides /api/search.","in":"query","name":"q","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/KbArticle"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/kb/articles","tags":["kb"]},"post":{"operationId":"kb_create_article","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateArticleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticleMutationResponse"}}},"description":""},"409":{"description":"slug already in use or idempotency key reused"},"422":{"description":"title/body rejected by KB rules"}},"security":[{"bearer":[]}],"summary":"POST /api/kb/articles — instant-first (IF1/IF2).","tags":["kb"]}},"/api/v1/kb/articles/{article_id}/embeds":{"get":{"operationId":"kb_list_article_embeds","parameters":[{"in":"path","name":"article_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/KbEmbed"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/kb/articles/{article_id}/embeds","tags":["kb"]},"post":{"operationId":"kb_create_embed","parameters":[{"in":"path","name":"article_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmbedRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbedMutationResponse"}}},"description":""},"404":{"description":"article not visible in scope"},"409":{"description":"url already attached or idempotency key reused"},"422":{"description":"url is not on the allowlist"}},"security":[{"bearer":[]}],"summary":"POST /api/kb/articles/{article_id}/embeds — resolve-then-store;\ninstant-first (IF1/IF2).","tags":["kb"]}},"/api/v1/kb/articles/{id}":{"delete":{"operationId":"kb_delete_article","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"article not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/kb/articles/{id} — emits `kb_article.deleted` so the\nunified-search indexer drops the article's index entry.","tags":["kb"]},"get":{"operationId":"kb_get_article","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticleDetail"}}},"description":""},"404":{"description":"article not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/kb/articles/{id} — with the sanitized read-only HTML\nprojection (no editor package in any read bundle).","tags":["kb"]},"patch":{"operationId":"kb_update_article","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateArticleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticleMutationResponse"}}},"description":""},"404":{"description":"article not visible in scope"},"409":{"description":"archived content lock, version conflict, slug reuse, or idempotency key reused"},"422":{"description":"title/body rejected by KB rules"}},"security":[{"bearer":[]}],"summary":"PATCH /api/kb/articles/{id} — instant-first (IF1/IF2). Archived\narticles refuse title/body edits until unarchived (typed conflict);\nstatus flips stay legal.","tags":["kb"]}},"/api/v1/kb/categories":{"get":{"operationId":"kb_list_categories","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/KbCategory"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/kb/categories","tags":["kb"]},"post":{"operationId":"kb_create_category","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCategoryRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryMutationResponse"}}},"description":""},"409":{"description":"slug already in use or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/kb/categories — instant-first (IF1/IF2).","tags":["kb"]}},"/api/v1/kb/categories/{id}":{"delete":{"operationId":"kb_delete_category","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"category not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/kb/categories/{id} — articles keep existing (category_id\nclears; ON DELETE SET NULL), so no content is lost behind a removal.","tags":["kb"]},"patch":{"operationId":"kb_update_category","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCategoryRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryMutationResponse"}}},"description":""},"404":{"description":"category not visible in scope"},"409":{"description":"version conflict, slug reuse, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"PATCH /api/kb/categories/{id} — instant-first (IF1/IF2).","tags":["kb"]}},"/api/v1/kb/embed-providers":{"get":{"operationId":"kb_list_embed_providers","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/KbEmbedProvider"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/kb/embed-providers — system rows + the org's extensions.","tags":["kb"]},"post":{"operationId":"kb_create_embed_provider","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProviderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderMutationResponse"}}},"description":""},"409":{"description":"slug already in use or idempotency key reused"},"422":{"description":"provider shape rejected (hosts/regex/endpoint)"}},"security":[{"bearer":[]}],"summary":"POST /api/kb/embed-providers — registry extension (admin-managed);\ninstant-first (IF1/IF2). System rows are shipped, never created here.","tags":["kb"]}},"/api/v1/kb/embed-providers/{id}":{"delete":{"operationId":"kb_delete_embed_provider","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"provider not visible in scope"},"409":{"description":"system rows are not deletable"}},"security":[{"bearer":[]}],"summary":"DELETE /api/kb/embed-providers/{id} — org rows only. Deleting a\nprovider leaves its embeds in place with `provider_id` cleared (the\nFK is ON DELETE SET NULL) so they degrade to blocked, never to\nunvalidated.","tags":["kb"]},"patch":{"operationId":"kb_update_embed_provider","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProviderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderMutationResponse"}}},"description":""},"404":{"description":"provider not visible in scope (system rows are not patchable through the org filter)"},"409":{"description":"system-row edit, version conflict, or idempotency key reused"},"422":{"description":"provider shape rejected"}},"security":[{"bearer":[]}],"summary":"PATCH /api/kb/embed-providers/{id} — org rows only; instant-first.","tags":["kb"]}},"/api/v1/kb/embeds/resolve":{"post":{"operationId":"kb_resolve_embed","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveEmbedRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbedResolution"}}},"description":""},"422":{"description":"url is not on the allowlist"},"503":{"description":"provider oEmbed endpoint unreachable"}},"security":[{"bearer":[]}],"summary":"POST /api/kb/embeds/resolve — validate a URL against the allowlist\nwithout attaching it to an article (live preview in the editor).","tags":["kb"]}},"/api/v1/kb/embeds/{id}":{"delete":{"operationId":"kb_delete_embed","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"embed not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/kb/embeds/{id}","tags":["kb"]}},"/api/v1/kb/external-documents":{"get":{"operationId":"kb_list_external_documents","parameters":[{"description":"Narrow to one article's attachments.","in":"query","name":"article_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/KbExternalDocument"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/kb/external-documents","tags":["kb"]},"post":{"operationId":"kb_create_external_document","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExternalDocumentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalDocumentMutationResponse"}}},"description":""},"404":{"description":"attached article not visible in scope"},"409":{"description":"idempotency key reused"},"422":{"description":"title/url rejected by KB rules"}},"security":[{"bearer":[]}],"summary":"POST /api/kb/external-documents — instant-first (IF1/IF2).","tags":["kb"]}},"/api/v1/kb/external-documents/{id}":{"delete":{"operationId":"kb_delete_external_document","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"external document not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/kb/external-documents/{id}","tags":["kb"]},"get":{"operationId":"kb_get_external_document","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KbExternalDocument"}}},"description":""},"404":{"description":"external document not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/kb/external-documents/{id}","tags":["kb"]},"patch":{"operationId":"kb_update_external_document","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateExternalDocumentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalDocumentMutationResponse"}}},"description":""},"404":{"description":"external document not visible in scope"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"title/url rejected by KB rules"}},"security":[{"bearer":[]}],"summary":"PATCH /api/kb/external-documents/{id} — instant-first (IF1/IF2). A\nURL change resets the link-check columns: the old verification is\nabout the old URL.","tags":["kb"]}},"/api/v1/kb/external-documents/{id}/link-check":{"post":{"operationId":"kb_link_check_external_document","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkCheckResponse"}}},"description":""},"404":{"description":"external document not visible in scope"}},"security":[{"bearer":[]}],"summary":"POST /api/kb/external-documents/{id}/link-check — performs one\nserver-side HTTPS HEAD (GET fallback) and records the outcome as\ndata. `unreachable` covers DNS/TLS/timeouts/non-2xx; the endpoint\nnever fails on an unreachable link — the record is the result.","tags":["kb"]}},"/api/v1/label-templates":{"get":{"operationId":"label_templates_list","parameters":[{"description":"Filter to one print purpose","in":"query","name":"purpose","required":false,"schema":{"type":"string"}},{"description":"Defaults to true","in":"query","name":"active_only","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/LabelTemplateView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/label-templates — org templates, optionally per purpose.","tags":["labels"]},"post":{"operationId":"label_templates_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLabelTemplateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelTemplateMutationResponse"}}},"description":""},"409":{"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/label-templates — create a template (Admin:edit).","tags":["labels"]}},"/api/v1/label-templates/preview":{"post":{"operationId":"label_templates_preview","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewLabelRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelRenderResponse"}}},"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/label-templates/preview — render a DRAFT template against\ndeterministic sample values (the designer's live preview).","tags":["labels"]}},"/api/v1/label-templates/{id}":{"get":{"operationId":"label_templates_get","parameters":[{"description":"Template id","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelTemplateView"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/label-templates/{id}.","tags":["labels"]},"patch":{"operationId":"label_templates_update","parameters":[{"description":"Template id","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLabelTemplateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelTemplateMutationResponse"}}},"description":""},"404":{"description":""},"409":{"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /api/label-templates/{id} — absent fields keep their value\n(Admin:edit).","tags":["labels"]}},"/api/v1/label-templates/{id}/archive":{"post":{"operationId":"label_templates_archive","parameters":[{"description":"Template id","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelTemplateMutationResponse"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/label-templates/{id}/archive — archival is a flag; the\narchived template frees the purpose's default slot.","tags":["labels"]}},"/api/v1/labels/render":{"post":{"operationId":"labels_render","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderLabelRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabelRenderResponse"}}},"description":""},"404":{"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/labels/render — resolve a real entity and render its label:\nbrowser-print geometry + the exact ZPL the desktop shell transports.","tags":["labels"]}},"/api/v1/mcp/capabilities":{"get":{"operationId":"mcp_capabilities_list","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CapabilityGrantList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/v1/mcp/capabilities — every granted capability of the org.","tags":["agent-platform"]},"put":{"operationId":"mcp_capability_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutCapabilityRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CapabilityGrantView"}}},"description":""},"403":{"description":"owner/admin surface"},"422":{"description":"invalid capability or dial position"}},"security":[{"bearer":[]}],"summary":"PUT /api/v1/mcp/capabilities — grant or re-dial one capability.","tags":["agent-platform"]}},"/api/v1/mcp/capabilities/{capability}":{"delete":{"operationId":"mcp_capability_revoke","parameters":[{"in":"path","name":"capability","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/v1/mcp/capabilities/{capability} — revoke (deny again).","tags":["agent-platform"]}},"/api/v1/me":{"get":{"operationId":"handlers_me","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/me — the authenticated principal with resolved permissions.","tags":["auth"]}},"/api/v1/meeting-types":{"get":{"operationId":"meeting_types_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/MeetingType"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/meeting-types","tags":["scheduling"]},"post":{"operationId":"meeting_types_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingTypeBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingTypeMutation"}}},"description":""},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/meeting-types","tags":["scheduling"]}},"/api/v1/meeting-types/{id}":{"delete":{"operationId":"meeting_types_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingTypeDeleted"}}},"description":""},"404":{"description":"meeting type not visible in scope"},"409":{"description":"a scheduling link still references this meeting type"}},"security":[{"bearer":[]}],"summary":"DELETE /api/meeting-types/{id} — HARD delete with audit (Wave 17 lane\n13): unlike the Pause toggle (PATCH `is_active`), this removes the\ntemplate outright. A scheduling link still referencing the row refuses\nwith a typed conflict (the FK RESTRICT surfaces) — pause the links\nFIRST or re-target them; history rows (bookings) are untouched by the\ntemplate's deletion.","tags":["scheduling"]},"patch":{"operationId":"meeting_types_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingTypeBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingTypeMutation"}}},"description":""},"404":{"description":""},"409":{"description":"version conflict or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"PATCH /api/meeting-types/{id}","tags":["scheduling"]}},"/api/v1/migration/pull/{family}":{"get":{"operationId":"migration_pull","parameters":[{"description":"Entity family: client, site, contact, board, ticket, time_entry, kb_category, kb_article","in":"path","name":"family","required":true,"schema":{"type":"string"}},{"description":"Opaque keyset cursor from a previous page's `next_cursor` (the last\nrow id). Absent = start of the family.","in":"query","name":"cursor","required":false,"schema":{"type":["string","null"]}},{"description":"Page size, clamped to [1, 200] (default 100).","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PullPage"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/migration/pull/{family} — stateless cursor-paged export of one\nentity family: internal id + stable external key + the canonical\nimport projection. Keyset-paged on the uuidv7 id (creation order), so\npages never skip or repeat rows under concurrent writes.","tags":["migration"]}},"/api/v1/migration/push/sessions":{"post":{"operationId":"migration_create_push_session","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePushSessionRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushSessionCreated"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/migration/push/sessions — opens a connector push session over\na fresh D2 import batch. One session targets exactly one entity kind;\ncross-family ordering is the tool's run script's job (the D2 import\norder: client → site → contact → board → ticket → time entry).","tags":["migration"]}},"/api/v1/migration/push/sessions/{session_id}":{"get":{"operationId":"migration_get_push_session","parameters":[{"in":"path","name":"session_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushSessionView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/migration/push/sessions/{id} — the resume view: where the\ncheckpoint stands and which chunks committed.","tags":["migration"]}},"/api/v1/migration/push/sessions/{session_id}/chunks":{"post":{"operationId":"migration_push_chunk","parameters":[{"in":"path","name":"session_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushChunkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushChunkResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/migration/push/sessions/{id}/chunks — plans then executes ONE\nchunk through the D2 commit path, then stamps the checkpoint. A\nfail-policy chunk with any conflict persists nothing and returns the\ntyped 409 with the full per-row report (checkpoint untouched — fix the\nrows and re-send the same index).","tags":["migration"]}},"/api/v1/migration/push/sessions/{session_id}/finish":{"post":{"operationId":"migration_finish_push_session","parameters":[{"in":"path","name":"session_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushSessionFinishedResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/migration/push/sessions/{id}/finish — closes the session and\nits D2 batch, emitting BOTH the engine lifecycle event\n(`import.batch_finished`) and the connector-level\n`migration.push_session_finished`.","tags":["migration"]}},"/api/v1/notification-org-defaults":{"get":{"operationId":"notification_org_defaults_get","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgDefaultsResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/notification-org-defaults — the org's default rows (admin).","tags":["notifications"]},"put":{"operationId":"notification_org_defaults_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrgDefaultRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgDefaultView"}}},"description":""},"422":{"description":"invalid quiet-hours window or timezone"}},"security":[{"bearer":[]}],"summary":"PUT /api/notification-org-defaults — upserts one kind's org default.","tags":["notifications"]}},"/api/v1/notification-preferences":{"get":{"operationId":"notification_prefs_get","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PreferenceView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/notification-preferences — the caller's effective preference\nfor every kind (defaults-first resolution applied).","tags":["notifications"]},"put":{"operationId":"notification_prefs_update","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePreferenceRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreferenceView"}}},"description":""},"422":{"description":"invalid quiet-hours window or timezone"}},"security":[{"bearer":[]}],"summary":"PUT /api/notification-preferences — upserts the caller's override row\nfor one kind and emits `notification_prefs.updated`. Quiet hours\nvalidate as a paired `HH:MM` window in a real IANA timezone;\nvalidation errors return 422.","tags":["notifications"]}},"/api/v1/notifications":{"get":{"operationId":"notifications_list","parameters":[{"in":"query","name":"unread_only","required":false,"schema":{"type":"boolean"}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"description":"Opaque keyset cursor from a previous page's `next_cursor`.","in":"query","name":"cursor","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPage"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/notifications — the caller's notifications, newest first,\nkeyset-paginated.","tags":["notifications"]}},"/api/v1/notifications/mark-read":{"post":{"operationId":"notifications_mark_read_bulk","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkReadBulkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkReadResult"}}},"description":""},"422":{"description":"empty or oversized id list"}},"security":[{"bearer":[]}],"summary":"POST /api/notifications/mark-read — bulk variant; unknown and\nalready-read ids are skipped (idempotent).","tags":["notifications"]}},"/api/v1/notifications/read-all":{"post":{"operationId":"notifications_mark_all_read","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkReadResult"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/notifications/read-all — empties the unread badge.","tags":["notifications"]}},"/api/v1/notifications/unread-count":{"get":{"operationId":"notifications_unread_count","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadCount"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/notifications/unread-count","tags":["notifications"]}},"/api/v1/notifications/{id}/read":{"post":{"operationId":"notifications_mark_read","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/notifications/{id}/read — marks one notification read and\nemits `notification.read` for cross-session badge sync.","tags":["notifications"]}},"/api/v1/on-call-blocks":{"get":{"operationId":"on_call_list","parameters":[{"description":"Whose calendar. Defaults to the caller; another user requires\nowner/admin.","in":"path","name":"user_id","required":true,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Local date in the TARGET user's timezone. Day view: the day itself.\nWeek view: any date inside the week (Monday-start). Defaults to\ntoday in that timezone.","in":"path","name":"date","required":true,"schema":{"format":"date","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/OnCallBlock"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/on-call-blocks?user_id=&from=&to=","tags":["scheduling"]},"post":{"operationId":"on_call_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOnCallRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnCallBlock"}}},"description":""},"409":{"description":"overlapping rotation"}},"security":[{"bearer":[]}],"summary":"POST /api/on-call-blocks — add a rotation segment (409 when stacking).","tags":["scheduling"]}},"/api/v1/on-call-blocks/{id}":{"delete":{"operationId":"on_call_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Owner of the row being deleted (must match self-or-admin rule).","in":"query","name":"user_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/on-call-blocks/{id}?user_id=","tags":["scheduling"]}},"/api/v1/onboarding":{"get":{"operationId":"onboarding_summary","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingSummary"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/onboarding — the caller's effective onboarding state: the org\nwizard's per-step flags + live gate checks, and (admin callers only)\nevery active member's user-level onboarding state.","tags":["onboarding"]}},"/api/v1/onboarding/disable":{"post":{"operationId":"onboarding_disable","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingDisableRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingDisableResponse"}}},"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/onboarding/disable — org-wide master switch (Settings →\nOnboarding). True disables every onboarding flow org-wide (both the\nwizard banner and member first-run); false re-enables — state is kept,\nso flows resume exactly where they were left.","tags":["onboarding"]}},"/api/v1/onboarding/org/steps/{step}/complete":{"post":{"operationId":"onboarding_org_step_complete","parameters":[{"description":"org wizard step id","in":"path","name":"step","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingStepView"}}},"description":""},"403":{"description":""},"409":{"description":"gate not yet satisfied"}},"security":[{"bearer":[]}],"summary":"POST /api/onboarding/org/steps/{step}/complete — record server-gated\ncompletion. Owner/admin only (the wizard is an owner/admin flow).","tags":["onboarding"]}},"/api/v1/onboarding/org/steps/{step}/skip":{"post":{"operationId":"onboarding_org_step_skip","parameters":[{"description":"org wizard step id","in":"path","name":"step","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingSkipRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingStepView"}}},"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/onboarding/org/steps/{step}/skip — owner/admin records the\nskip choice. `never: true` persists the dismissed flag (\"never show\nthis again\"); false is the \"skip for now\" no-op (the step stays pending\nand resumable).","tags":["onboarding"]}},"/api/v1/onboarding/reset/org":{"post":{"operationId":"onboarding_org_reset","responses":{"200":{"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/onboarding/reset/org — admin re-run of the org wizard: clears\nevery per-step flag (the flows_disabled switch is untouched on purpose;\nre-running a disabled family stays silent until re-enabled).","tags":["onboarding"]}},"/api/v1/onboarding/reset/user":{"post":{"operationId":"onboarding_user_reset","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingTriggerRequest"}}},"required":true},"responses":{"200":{"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/onboarding/reset/user — re-run one member's first-run flow\n(admin re-trigger) or the CALLER's own. Clears that user's onboarding\nflags so every step resumes pending.","tags":["onboarding"]}},"/api/v1/onboarding/user/steps/{step}/complete":{"post":{"operationId":"onboarding_user_step_complete","parameters":[{"description":"user onboarding step id","in":"path","name":"step","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingStepView"}}},"description":""},"403":{"description":""},"409":{"description":"gate not yet satisfied"}},"security":[{"bearer":[]}],"summary":"POST /api/onboarding/user/steps/{step}/complete — the caller records\nprogress on their OWN first-run flow. Profile/mfa gates are\nserver-verified; orientation/defaults record on click (they are pure\nUI milestones by design).","tags":["onboarding"]}},"/api/v1/onboarding/user/steps/{step}/skip":{"post":{"operationId":"onboarding_user_step_skip","parameters":[{"description":"user onboarding step id","in":"path","name":"step","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingSkipRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingStepView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/onboarding/user/steps/{step}/skip — self-scoped skip recording\n(same never/skip-for-now semantics as the org steps).","tags":["onboarding"]}},"/api/v1/org":{"get":{"operationId":"org_profile_get","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgProfileResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/org — profile fields shown on General settings / the wizard.","tags":["settings"]},"patch":{"operationId":"org_profile_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchOrgRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgProfileResponse"}}},"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /api/org — updates name and/or ticket prefix (Admin:edit).\nIdempotent; emits `org.updated` + audit row.","tags":["settings"]}},"/api/v1/org/exports":{"get":{"operationId":"org_export_list","parameters":[{"description":"Filter by archive state; absent returns all rows.","in":"query","name":"archived","required":false,"schema":{"type":["boolean","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"in":"query","name":"offset","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportHistoryList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/org/exports — the org-export ledger read model (0089):\nnewest-first list over the exports the org has created so\n/settings/export grows its list/archive surface over real history.","tags":["org"]},"post":{"operationId":"org_export_create","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportCreatedResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/org/exports — builds the versioned bundle, streams it into\nthe ObjectStore, and returns the signed download link.","tags":["org"]}},"/api/v1/org/exports/{export_id}":{"patch":{"operationId":"org_export_archive","parameters":[{"in":"path","name":"export_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportArchiveUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportHistoryView"}}},"description":""},"404":{"description":"export not in this org's ledger"}},"security":[{"bearer":[]}],"summary":"PATCH /api/org/exports/{export_id} — archives (or restores) ONE ledger\nrow; a pure read-model toggle over the row, content untouched.","tags":["org"]}},"/api/v1/org/exports/{export_id}/download-link":{"post":{"operationId":"org_export_link","parameters":[{"in":"path","name":"export_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportLinkRefreshed"}}},"description":""},"404":{"description":"export not in this org's ledger"}},"security":[{"bearer":[]}],"summary":"POST /api/org/exports/{export_id}/download-link — mints ONE fresh\n15-minute signed link for a LEDGERED export (the link is never stored\n— this is the per-row download affordance the history surface uses).","tags":["org"]}},"/api/v1/org/exports/{org_id}/{export_id}/download":{"get":{"operationId":"org_export_download","parameters":[{"in":"path","name":"org_id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"export_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportBundle"}}},"description":""}},"summary":"GET /api/org/exports/{org_id}/{export_id}/download — streams the stored\nbundle. Authenticated by the signed link alone (no JWT): an expired or\nforged token is an unauthorized request, indistinguishable between the\ntwo. The org id is IN the path (and inside the signature) so a token\nauthorizes exactly one (org, export, expiry) triple.","tags":["org"]}},"/api/v1/org/manifest":{"get":{"description":"Owner/admin gated (policy §5: the manifest is an org-level\nadministrative artifact; technicians and dispatchers have no business\nreading the full customization inventory, e.g. webhook URLs).","operationId":"org_manifest","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomizationManifest"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/org/manifest — the org's customization manifest.","tags":["org"]}},"/api/v1/org/pod-visibility":{"get":{"operationId":"org_pod_visibility_get","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodVisibilityView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/org/pod-visibility — the org's stored matrix; `config` is\n`{}` when the org never customized (clients fall back to their\nregistry defaults, defaults-first).","tags":["settings"]},"put":{"operationId":"org_pod_visibility_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutPodVisibilityRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodVisibilityView"}}},"description":""},"403":{"description":""},"422":{"description":"invalid matrix shape"}},"security":[{"bearer":[]}],"summary":"PUT /api/org/pod-visibility — rewrites the org's matrix (Admin:edit).\nEmits `org.pod_visibility_updated` + audit row in the same tx.","tags":["settings"]}},"/api/v1/org/sign-off-consents":{"post":{"operationId":"sign_off_consents_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConsentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignOffConsent"}}},"description":""},"401":{"description":"missing or invalid credentials"},"403":{"description":"not permitted on this board"},"404":{"description":"ticket or sign-off not found"},"409":{"description":"already captured, revoked, or a version collision"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/org/sign-off-consents — append the next consent version. The\nversion comes from the current max; existing sign-offs keep their own\nsnapshot, so revising the wording never rewrites a past agreement.","tags":["sign_offs"]}},"/api/v1/org/sign-off-consents/current":{"get":{"operationId":"sign_off_consents_current","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignOffConsent"}}},"description":""},"401":{"description":"missing or invalid credentials"},"403":{"description":"not permitted"}},"security":[{"bearer":[]}],"summary":"GET /api/org/sign-off-consents/current — the wording a pad must show. The\npad fetches this before capture so the version it sends back is the one\nthe signer actually read.","tags":["sign_offs"]}},"/api/v1/org/users":{"get":{"operationId":"org_users_list","parameters":[{"description":"Page size (default 50, max 200).","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}},{"description":"Offset paging.","in":"query","name":"offset","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgUserListResponse"}}},"description":""},"403":{"description":"requires manager role"}},"security":[{"bearer":[]}],"summary":"GET /api/org/users — the staff directory (manager+).","tags":["org-users"]},"post":{"operationId":"org_users_invite","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteUserRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgUserMutationResponse"}}},"description":""},"409":{"description":"email already in org, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/org/users — invite (create) a staff user. The row lands\nINACTIVE with an unusable password; the response carries a one-time\nsetup token the invitee redeems at `POST /org/users/accept-invite`.","tags":["org-users"]}},"/api/v1/org/users/accept-invite":{"post":{"operationId":"org_users_accept_invite","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptInviteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenPair"}}},"description":""},"404":{"description":"invite unknown, already used, or expired"},"422":{"description":"validation error"}},"summary":"POST /api/org/users/accept-invite — public invite redemption: set the\nfirst password and activate the account; returns a normal token pair.\nThe token is single-use and expiring; an unknown, used, or expired\ntoken is an indistinguishable 404.","tags":["org-users"]}},"/api/v1/org/users/{id}/email-verified":{"post":{"operationId":"org_users_set_email_verified","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetEmailVerifiedRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailVerifiedResponse"}}},"description":""},"403":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/org/users/{id}/email-verified — the admin OVERRIDE for the\nemail-verification gate (migration 0088). Audited both ways.","tags":["org-users"]}},"/api/v1/org/users/{id}/role":{"patch":{"operationId":"org_users_set_role","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetUserRoleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgUserMutationResponse"}}},"description":""},"409":{"description":"last active owner, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"PATCH /api/org/users/{id}/role — assign a builtin org role. Each change\nis audited; the last active owner cannot be demoted away from owner.","tags":["org-users"]}},"/api/v1/org/users/{id}/status":{"patch":{"operationId":"org_users_set_status","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetUserStatusRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgUserMutationResponse"}}},"description":""},"409":{"description":"last active owner, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"PATCH /api/org/users/{id}/status — activate or deactivate. Deactivation\nrevokes every live session and blocks login in the same commit.","tags":["org-users"]}},"/api/v1/orgs/{org_id}/custom-domain":{"delete":{"operationId":"org_custom_domain_delete","parameters":[{"description":"the org; must be the caller's own org","in":"path","name":"org_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"domain cleared"},"401":{"description":"not authenticated"},"403":{"description":"not an admin / missing org_custom_domain:manage"},"404":{"description":"no such org for this caller"}},"security":[{"bearer":[]}],"summary":"`DELETE /api/v1/orgs/{org_id}/custom-domain` — clear the domain.","tags":["org"]},"get":{"operationId":"org_custom_domain_get","parameters":[{"description":"the org; must be the caller's own org","in":"path","name":"org_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomDomainView"}}},"description":""},"401":{"description":"not authenticated"},"403":{"description":"service account without org_custom_domain:read"},"404":{"description":"no such org for this caller"}},"security":[{"bearer":[]}],"summary":"`GET /api/v1/orgs/{org_id}/custom-domain` — current state + verification\nstatus + provider-plugin availability.","tags":["org"]},"post":{"operationId":"org_custom_domain_set","parameters":[{"description":"the org; must be the caller's own org","in":"path","name":"org_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetCustomDomainRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetCustomDomainResponse"}}},"description":""},"400":{"description":"invalid domain"},"401":{"description":"not authenticated"},"403":{"description":"not an admin / missing org_custom_domain:manage"},"404":{"description":"no such org for this caller"},"409":{"description":"domain already claimed by another workspace"}},"security":[{"bearer":[]}],"summary":"`POST /api/v1/orgs/{org_id}/custom-domain` — claim a domain.","tags":["org"]}},"/api/v1/orgs/{org_id}/custom-domain/verify":{"post":{"operationId":"org_custom_domain_verify","parameters":[{"description":"the org; must be the caller's own org","in":"path","name":"org_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyCustomDomainResponse"}}},"description":""},"401":{"description":"not authenticated"},"403":{"description":"not an admin / missing org_custom_domain:manage"},"404":{"description":"no such org for this caller"},"409":{"description":"no custom domain is set"}},"security":[{"bearer":[]}],"summary":"`POST /api/v1/orgs/{org_id}/custom-domain/verify` — run the generic DNS\nverification (the private build tracker A8 (C)). The TXT token is the authority; the\nA/CNAME leg is plausibility only.","tags":["org"]}},"/api/v1/orgs/{org_id}/quotas":{"get":{"description":"Informational by construction: a percentage here is a measurement, and\nnothing in the core reads this endpoint to limit anything. `limits`\nis a real configuration read — an org that has configured none reports\n`false`, because a default limit is a fake gate.","operationId":"org_quotas_get","parameters":[{"description":"the org to report on; must be the caller's own org","in":"path","name":"org_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgQuotaView"}}},"description":""},"401":{"description":"not authenticated"},"403":{"description":"service account without org_usage:read"},"404":{"description":"no such org for this caller"}},"security":[{"bearer":[]}],"summary":"GET /api/v1/orgs/{org_id}/quotas — usage beside configured limits.","tags":["org"]}},"/api/v1/orgs/{org_id}/service-accounts":{"get":{"operationId":"service_accounts_list","parameters":[{"in":"path","name":"org_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccountList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /orgs/{org_id}/service-accounts — admin listing with derived cert\nstatus.","tags":["integrations"]},"post":{"operationId":"service_accounts_create","parameters":[{"in":"path","name":"org_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateServiceAccountRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccountView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /orgs/{org_id}/service-accounts — enroll a service account by\npinning a certificate fingerprint with permission grants and an\noptional expiry.","tags":["integrations"]}},"/api/v1/orgs/{org_id}/service-accounts/{id}":{"delete":{"operationId":"service_accounts_delete","parameters":[{"in":"path","name":"org_id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"reason","required":false,"schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /orgs/{org_id}/service-accounts/{id} — immediate, permanent\nrevocation. Idempotent: a second delete of an already-revoked account\nstays successful (the row is present in-scope), so the surface is safe\nto retry.","tags":["integrations"]},"patch":{"operationId":"service_accounts_update","parameters":[{"in":"path","name":"org_id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateServiceAccountRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccountView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /orgs/{org_id}/service-accounts/{id} — update grants, expiry, or\nlifecycle status. The pinned fingerprint and org binding are immutable\n(a re-pin is a create + revoke).","tags":["integrations"]}},"/api/v1/orgs/{org_id}/suspension-status":{"get":{"operationId":"org_suspension_status_get","parameters":[{"description":"the org; must be the caller's own org","in":"path","name":"org_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuspensionStatusView"}}},"description":""},"401":{"description":"not authenticated"},"403":{"description":"service account without org_suspension:read"},"404":{"description":"no such org for this caller"}},"security":[{"bearer":[]}],"summary":"`GET /api/v1/orgs/{org_id}/suspension-status` — the org's notice state.","tags":["org"]},"patch":{"operationId":"org_suspension_status_patch","parameters":[{"description":"the org; must be the caller's own org","in":"path","name":"org_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetSuspensionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuspensionStatusView"}}},"description":""},"401":{"description":"not authenticated"},"403":{"description":"not an admin (no admin:edit)"},"404":{"description":"no such org for this caller"},"422":{"description":"unknown reason, or a missing one when suspending"}},"security":[{"bearer":[]}],"summary":"`PATCH /api/v1/orgs/{org_id}/suspension-status` — set or clear the flag\n(`admin:edit`). Idempotent; emits `org.suspension_changed` + an audit row.","tags":["org"]}},"/api/v1/orgs/{org_id}/usage":{"get":{"description":"`refreshed_at` is when the numbers were computed; a consumer that\nneeds fresh numbers reads the row's own timestamp instead of assuming.\n`unavailable` names every metric this build cannot compute honestly,\nwith the reason — it is a real, renderable list, not a placeholder.","operationId":"org_usage_get","parameters":[{"description":"the org to report on; must be the caller's own org","in":"path","name":"org_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgUsageView"}}},"description":""},"401":{"description":"not authenticated"},"403":{"description":"service account without org_usage:read"},"404":{"description":"no such org for this caller"}},"security":[{"bearer":[]}],"summary":"GET /api/v1/orgs/{org_id}/usage — the org's current usage counts.","tags":["org"]}},"/api/v1/passwords":{"get":{"operationId":"passwords_list","parameters":[{"in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"`true` = org-level entries only.","in":"query","name":"global","required":false,"schema":{"type":["boolean","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PasswordEntry"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/passwords — vault browser rows; never carries secrets.","tags":["passwords"]},"post":{"operationId":"passwords_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePasswordRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordMutationResponse"}}},"description":""},"409":{"description":"idempotency key reused"},"422":{"description":"title/secret rejected"}},"security":[{"bearer":[]}],"summary":"POST /api/passwords — instant-first (IF1/IF2); seals the secret before\nthe claim so a crypto failure never consumes an idempotency key.","tags":["passwords"]}},"/api/v1/passwords/{id}":{"delete":{"operationId":"passwords_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"deleted"},"404":{"description":"entry not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/passwords/{id} — the vault is deletion-capable; history\ncascade-deletes. Emits `password.deleted`.","tags":["passwords"]},"get":{"operationId":"passwords_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordEntry"}}},"description":""},"404":{"description":"entry not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/passwords/{id}","tags":["passwords"]},"patch":{"operationId":"passwords_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePasswordRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordMutationResponse"}}},"description":""},"404":{"description":"entry not visible in scope"},"409":{"description":"idempotency key reused"},"422":{"description":"validation error"}},"security":[{"bearer":[]}],"summary":"PATCH /api/passwords/{id} — instant-first; a present `secret` rotates\nthe ciphertext and archives the previous one to history in-commit.","tags":["passwords"]}},"/api/v1/passwords/{id}/history":{"get":{"operationId":"passwords_history","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PasswordHistoryEntry"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/passwords/{id}/history — change ledger metadata (the\nciphertexts are retained for forensic re-derivation, never exposed).","tags":["passwords"]}},"/api/v1/passwords/{id}/links":{"get":{"operationId":"passwords_list_links","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ResolvedLink"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/passwords/{id}/links","tags":["passwords"]},"post":{"operationId":"passwords_add_link","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ResolvedLink"},"type":"array"}}},"description":""},"404":{"description":"password or link target not visible in scope"}},"security":[{"bearer":[]}],"summary":"POST /api/passwords/{id}/links","tags":["passwords"]}},"/api/v1/passwords/{id}/reveal":{"post":{"operationId":"passwords_reveal","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordReveal"}}},"description":""},"403":{"description":"reveal requires passwords:edit"},"404":{"description":"entry not visible in scope"}},"security":[{"bearer":[]}],"summary":"POST /api/passwords/{id}/reveal — the ONLY plaintext path. Requires\n`passwords:edit`, runs inside the org tx so the reveal event commits\natomically with the read, and the response is the sole carrier of the\ndecrypted secret.","tags":["passwords"]}},"/api/v1/passwords/{id}/share-links":{"get":{"operationId":"passwords_list_share_links","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SecretShareLink"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/passwords/{id}/share-links","tags":["passwords"]},"post":{"operationId":"passwords_create_share_link","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSecretShareLinkRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretShareLinkIssued"}}},"description":""},"404":{"description":"entry not visible in scope"},"422":{"description":"expiry/label rejected"}},"security":[{"bearer":[]}],"summary":"POST /api/passwords/{id}/share-links — one-time link for the legacy\nbyte-compatible entry surface (the same token posture as records).","tags":["passwords"]}},"/api/v1/passwords/{id}/share-links/{share_id}":{"delete":{"operationId":"passwords_delete_share_link","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"share_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/passwords/{id}/share-links/{share_id}","tags":["passwords"]}},"/api/v1/pipeline/deals":{"get":{"operationId":"pipeline_deals_list","parameters":[{"in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"stage_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"quote_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Exact status: `open|won|lost`.","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"owner_user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PipelineDeal"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/pipeline/deals — newest first, filterable.","tags":["commercial"]},"post":{"operationId":"pipeline_deals_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDealRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealMutationResponse"}}},"description":""},"404":{"description":"client, quote, or stage not visible in scope"},"409":{"description":"idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/pipeline/deals — create an opportunity and record its\ninitial placement in the transition ledger (same transaction).","tags":["commercial"]}},"/api/v1/pipeline/deals/{id}":{"get":{"operationId":"pipeline_deals_get_one","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealDetail"}}},"description":""},"404":{"description":"not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/pipeline/deals/{id} — deal + transition ledger.\n(Registered at the `get_one` name in the router.)","tags":["commercial"]},"patch":{"operationId":"pipeline_deals_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDealRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealMutationResponse"}}},"description":""},"404":{"description":"deal, client, or quote not visible in scope"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"PATCH /api/pipeline/deals/{id} — editable fields; the stage is not\ntouched here (that is the audited /stage leg).","tags":["commercial"]}},"/api/v1/pipeline/deals/{id}/stage":{"post":{"operationId":"pipeline_deals_move_stage","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveStageRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealMutationResponse"}}},"description":""},"404":{"description":"deal or target stage not visible in scope"},"409":{"description":"concurrent move or version conflict"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/pipeline/deals/{id}/stage — one audited stage transition.\nGuarded on the CURRENT stage (concurrent moves: first wins, loser gets\na typed 409); the ledger row commits in the same transaction.","tags":["commercial"]}},"/api/v1/pipeline/stages":{"get":{"operationId":"pipeline_stages_list","parameters":[{"description":"`None` = active stages only (default); `true` includes inactive.","in":"query","name":"include_inactive","required":false,"schema":{"type":["boolean","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PipelineStage"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/pipeline/stages — ordered pipeline definition.","tags":["commercial"]},"post":{"operationId":"pipeline_stages_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateStageRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StageMutationResponse"}}},"description":""},"409":{"description":"stage name already exists or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/pipeline/stages — create a stage (name unique per org).","tags":["commercial"]}},"/api/v1/pipeline/stages/{id}":{"patch":{"operationId":"pipeline_stages_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStageRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StageMutationResponse"}}},"description":""},"404":{"description":"stage not visible in scope"},"409":{"description":"stage name already exists, version conflict, or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"PATCH /api/pipeline/stages/{id}.","tags":["commercial"]}},"/api/v1/plugins/catalog":{"get":{"operationId":"plugin_catalog_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CatalogEntryView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/catalog — the marketplace registry.","tags":["plugins"]},"post":{"operationId":"plugin_catalog_publish","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishCatalogEntryRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogEntryView"}}},"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/catalog — publish a catalog entry with its first\nversion. Operator action on a self-hosted core (the catalog is\nglobal); audited, and the manifest is validated against the closed\ncapability vocabulary and the pinned interface version BEFORE any\nrow exists.","tags":["plugins"]}},"/api/v1/plugins/catalog/{slug}":{"get":{"operationId":"plugin_catalog_get","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogEntryView"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/catalog/{slug}.","tags":["plugins"]}},"/api/v1/plugins/catalog/{slug}/revoke":{"post":{"operationId":"plugin_catalog_revoke","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeCatalogEntryRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogEntryView"}}},"description":""},"404":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/catalog/{slug}/revoke — the registry kill switch.\nDenies new installs and marks every existing install revoked\n(deny-by-default: a revoked plugin never runs); one\n`plugin.install_revoked` event per affected install.","tags":["plugins"]}},"/api/v1/plugins/catalog/{slug}/versions":{"post":{"operationId":"plugin_catalog_append_version","parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppendVersionRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogEntryView"}}},"description":""},"404":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/catalog/{slug}/versions — append a version to a\nlive entry (revoked entries accept no new versions).","tags":["plugins"]}},"/api/v1/plugins/distribution/agent-skills":{"get":{"operationId":"plugin_distribution_skills_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SkillVersionView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/distribution/agent-skills — the ACTIVE version of\nevery org skill, with its provenance.","tags":["plugins"]}},"/api/v1/plugins/distribution/agent-skills/{id}/enabled":{"post":{"operationId":"plugin_distribution_skill_enabled","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefuseStagedRequest"}}},"required":true},"responses":{"204":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/distribution/agent-skills/{id}/enabled — the skills\nhalf's enable gate (deny-by-default: enable is an explicit\nowner/admin action on the active version row; version rows other than\nthe active one are never enabled).","tags":["plugins"]}},"/api/v1/plugins/distribution/publishers":{"get":{"operationId":"plugin_distribution_publishers_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PublisherVerificationView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/distribution/publishers — the org's publisher\nregistry (vouched history included; `verified` = the active badge).","tags":["plugins"]},"post":{"operationId":"plugin_distribution_publishers_verify","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyPublisherRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublisherVerificationView"}}},"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/distribution/publishers — admin vouches a\npublisher identity on the org's behalf (re-vouch flips a revoked row\nback; a double-verify conflicts).","tags":["plugins"]}},"/api/v1/plugins/distribution/publishers/{id}":{"delete":{"operationId":"plugin_distribution_publishers_revoke","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublisherVerificationView"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/plugins/distribution/publishers/{id} — withdraws the\nvouch; the badge disappears and the row persists as history.","tags":["plugins"]}},"/api/v1/plugins/distribution/ratings":{"get":{"operationId":"plugin_distribution_ratings_list","parameters":[{"in":"query","name":"source_url","required":false,"schema":{"type":"string"}},{"in":"query","name":"artifact_digest","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RatingListView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/distribution/ratings — the org's ratings, optionally\nnarrowed to one artifact release.","tags":["plugins"]},"put":{"operationId":"plugin_distribution_ratings_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutRatingRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RatingSummary"}}},"description":""},"400":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"PUT /api/plugins/distribution/ratings — upserts the caller's rating\non one artifact release (one per user per release; re-rate wins).","tags":["plugins"]}},"/api/v1/plugins/distribution/staged":{"get":{"operationId":"plugin_distribution_staged_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/StagedItemView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/distribution/staged — pending + terminal rows.\nOwners/admins see everything; plain members see only their OWN\nuploads (the deny-by-default staged visibility).","tags":["plugins"]}},"/api/v1/plugins/distribution/staged/github":{"post":{"operationId":"plugin_distribution_stage_github","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StageFromGithubRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StagedItemView"}}},"description":""},"404":{"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/distribution/staged/github — the pinned GitHub\npull: the manifest is fetched AT THE PINNED COMMIT (never HEAD), the\ndigest is computed over the fetched bytes, the pin is recorded on the\nstaged row; a no-pin call is refused.","tags":["plugins"]}},"/api/v1/plugins/distribution/staged/registry":{"post":{"operationId":"plugin_distribution_stage_registry","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StageFromRegistryRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StagedItemView"}}},"description":""},"404":{"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/distribution/staged/registry — a public marketplace\nregistry entry (static JSON, git-tag pinned, code-signature/digest\nshape: the BUILD-PROMPT v1 fixture). The SERVER fetches and the\nadapter validates BEFORE anything registers.","tags":["plugins"]}},"/api/v1/plugins/distribution/staged/skill":{"post":{"operationId":"plugin_distribution_stage_skill","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StageSkillRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StagedItemView"}}},"description":""},"404":{"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/distribution/staged/skill — skills from free/open\ncommunity marketplaces/repo APIs (`skill_community`: the server\nrefetches the manifest and digest-verifies it before the row exists)\nand manual member uploads (`manual_upload`: inline manifest). Skill\ncontent is UNTRUSTED RAG-class data — staged, never executed.","tags":["plugins"]}},"/api/v1/plugins/distribution/staged/zip":{"post":{"operationId":"plugin_distribution_stage_zip","requestBody":{"content":{"application/zip":{"schema":{"items":{"format":"int32","minimum":0,"type":"integer"},"type":"array"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StagedItemView"}}},"description":""},"400":{"description":""},"413":{"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/distribution/staged/zip — the local zip import.\nALL the sandbox validation (manifest schema, artifact digest,\nsize/entry caps, exactly one root manifest.json, traversal safety)\nruns BEFORE the row exists; the declared capability list rides into\nthe SAME consent screen.","tags":["plugins"]}},"/api/v1/plugins/distribution/staged/{id}/approve":{"post":{"operationId":"plugin_distribution_staged_approve","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveStagedRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StagedItemView"}}},"description":""},"404":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/distribution/staged/{id}/approve — the owner/admin\nreview. Approve MINTS but remains deny-by-default: a plugin adopt is\ncreated DISABLED (a public-registry source lands the SAME staged\npipeline — browsing is help, not bypass); a skill lands disabled\nuntil enabled. Grants must subset the staged manifest's declared set.","tags":["plugins"]}},"/api/v1/plugins/distribution/staged/{id}/consent":{"get":{"operationId":"plugin_distribution_staged_consent","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsentView"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/distribution/staged/{id}/consent — the SAME consent\nshape for every source: declared capabilities + the manifest's\nsettings JSON-Schema (the typed-registry render source).","tags":["plugins"]}},"/api/v1/plugins/distribution/staged/{id}/refuse":{"post":{"operationId":"plugin_distribution_staged_refuse","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefuseStagedRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StagedItemView"}}},"description":""},"404":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/distribution/staged/{id}/refuse — the reviewer\ndeclines: terminal for the staged row (the uploader may re-stage a\nfresh upload). A typed note is required.","tags":["plugins"]}},"/api/v1/plugins/distribution/theme-packs":{"get":{"operationId":"theme_packs_list","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThemePackList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/distribution/theme-packs — the org's installed\ntheme packs (ADR 0009 V1 distribution; restyle-only manifests).","tags":["plugins"]}},"/api/v1/plugins/distribution/theme-packs/{slug}":{"delete":{"operationId":"theme_packs_delete","parameters":[{"description":"installed pack slug","in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":""},"404":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/plugins/distribution/theme-packs/{slug} — uninstall.\nRefused while the pack is the org's active theme (repoint the typed\n`appearance.theme` setting first).","tags":["plugins"]}},"/api/v1/plugins/installed":{"get":{"operationId":"plugin_install_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/InstallView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/installed — this org's adoptions.","tags":["plugins"]},"post":{"operationId":"plugin_install_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallPluginRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallView"}}},"description":""},"404":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/installed — adopt a catalog entry at a pinned\nversion. Created DISABLED (deny-by-default); enabling is a separate\nexplicit admin action.","tags":["plugins"]}},"/api/v1/plugins/installed/{id}":{"delete":{"operationId":"plugin_install_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/plugins/installed/{id} — uninstall (the adoption row\ngoes away; re-install is a fresh adoption).","tags":["plugins"]},"patch":{"operationId":"plugin_install_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateInstallRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallView"}}},"description":""},"404":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /api/plugins/installed/{id} — enable/disable, upgrade the\npinned version, or adjust the grant set. Widening grants\nre-validates against the NEW pinned manifest. Revoked installs are\nterminal.","tags":["plugins"]}},"/api/v1/plugins/installed/{id}/consent":{"post":{"operationId":"plugin_install_update_consent","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProposalView"}}},"description":""},"404":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/installed/{id}/consent — commit the CONSENTED\nupdate: the swap IS the consent action (deny-by-default — until the\nowner/admin consents, the OLD version is what runs). Consent appends\nthe provenance chain and rides the existing update event.","tags":["plugins"]}},"/api/v1/plugins/installed/{id}/discard-update":{"post":{"operationId":"plugin_install_update_discard","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/installed/{id}/discard-update — abandon the pending\nre-consent (the OLD version keeps running; the pending columns clear).","tags":["plugins"]}},"/api/v1/plugins/installed/{id}/provenance":{"get":{"operationId":"plugin_distribution_provenance_list","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ProvenanceView"},"type":"array"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/installed/{id}/provenance — the append-only\n`{source_kind, source_url, fetched_ref, artifact_digest, uploader}`\nfor one install. The chain SURVIVES terminal uninstalls (no cascade).","tags":["plugins"]}},"/api/v1/plugins/installed/{id}/revoke":{"post":{"description":"Distinct from disabling (reversible, grants untouched) and from the\ncatalog-wide revocation (which cascades to every tenant's installs). Revoke\nis terminal, the reason is required, and execution stops at the parent's\ngate: `authorize_invocation` refuses a revoked status, and the artifact load\npath refuses before it reads a byte.","operationId":"plugin_install_revoke","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeInstallRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallView"}}},"description":""},"404":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/installed/{id}/revoke — the org's own kill switch for ONE\ninstall.","tags":["plugins"]}},"/api/v1/plugins/installed/{id}/rollback":{"post":{"description":"A rollback is a POINTER MOVE: version, digest and grants return to what they\nwere before the last update, and no bytes are fetched. The old module is\nstill in the store because keys are content-addressed, so an update wrote a\nnew key rather than overwriting the old one.\n\nOne step, not a toggle: the parked target is cleared, so a second rollback\nanswers `409` until the update is re-consented. That is deliberate — the\noperator should say which bytes they want, not ping-pong between two.","operationId":"plugin_install_rollback","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallView"}}},"description":""},"404":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/installed/{id}/rollback — move the pin back to the\nprevious digest.","tags":["plugins"]}},"/api/v1/plugins/installed/{id}/settings":{"get":{"operationId":"plugin_install_settings_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallSettingsView"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/installed/{id}/settings — the render source: the\npinned manifest's declared JSON-Schema + the validated config.","tags":["plugins"]},"patch":{"operationId":"plugin_install_settings_put","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutInstallSettingsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstallSettingsView"}}},"description":""},"404":{"description":""},"409":{"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /api/plugins/installed/{id}/settings — the config is validated\nagainst the pinned manifest's schema AT THE WRITE BOUNDARY (the\ntyped-registry doctrine: no hand-authored per-plugin pages, and a\nplugin with no declared schema gets consent/enable/disable only —\nthis endpoint refuses its config writes). Every write is audited.","tags":["plugins"]}},"/api/v1/plugins/installed/{id}/update":{"get":{"operationId":"plugin_install_update_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProposalView"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/installed/{id}/update — the pending-state read (the\nconsent screen shows exactly what consent would do).","tags":["plugins"]},"post":{"operationId":"plugin_install_update_propose","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposeUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProposalView"}}},"description":""},"404":{"description":""},"409":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/plugins/installed/{id}/update — propose the update: the\ncandidate version lands in the pending columns while the OLD version\nkeeps running; the response IS the re-consent diff the consent screen\nrenders.","tags":["plugins"]}},"/api/v1/plugins/surface/automation-actions":{"get":{"operationId":"plugin_surface_automation_actions","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SurfaceEntryList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/surface/automation-actions — the org's registered\nplugin automation action kinds (the automations editor lists them\nalongside the built-ins).","tags":["plugins"]}},"/api/v1/plugins/surface/field-validators":{"get":{"operationId":"plugin_surface_field_validators","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SurfaceEntryList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/surface/field-validators — the org's registered\nplugin schema validators for board custom fields.","tags":["plugins"]}},"/api/v1/plugins/surface/report-renders":{"get":{"operationId":"plugin_surface_report_renders","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SurfaceEntryList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/surface/report-renders — the org's registered\nplugin render kinds for the report builder.","tags":["plugins"]}},"/api/v1/plugins/surface/widgets":{"get":{"operationId":"plugin_surface_widgets","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SurfaceEntryList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/plugins/surface/widgets — the org's registered plugin\nwidgets (dashboard + ticket-pane slots), enabled installs only.","tags":["plugins"]}},"/api/v1/pm-schedules":{"get":{"operationId":"pm_schedules_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PmSchedule"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/pm-schedules — every schedule, next-run ordered.","tags":["pm-schedules"]},"post":{"operationId":"pm_schedules_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateScheduleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleMutationResponse"}}},"description":""},"404":{"description":"board not found"},"409":{"description":"duplicate schedule name or idempotency key reused"},"422":{"description":"invalid cadence shape"}},"security":[{"bearer":[]}],"summary":"POST /api/pm-schedules — create a recurring-ticket template.","tags":["pm-schedules"]}},"/api/v1/pm-schedules/{id}":{"delete":{"operationId":"pm_schedules_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"schedule not found"}},"security":[{"bearer":[]}],"summary":"DELETE /api/pm-schedules/{id} — removes the schedule. Existing\ngenerated tickets are unaffected (templates are blueprints).","tags":["pm-schedules"]},"get":{"operationId":"pm_schedules_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PmSchedule"}}},"description":""},"404":{"description":"schedule not found"}},"security":[{"bearer":[]}],"summary":"GET /api/pm-schedules/{id}","tags":["pm-schedules"]},"patch":{"operationId":"pm_schedules_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateScheduleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleMutationResponse"}}},"description":""},"404":{"description":"schedule not found"},"409":{"description":"version conflict or duplicate name"},"422":{"description":"invalid cadence shape"}},"security":[{"bearer":[]}],"summary":"PATCH /api/pm-schedules/{id} — edit template/cadence/enabled. A\ncadence edit re-materializes `next_run_at` from the new shape.","tags":["pm-schedules"]}},"/api/v1/pm-schedules/{id}/runs":{"get":{"operationId":"pm_schedules_runs","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PmRun"},"type":"array"}}},"description":""},"404":{"description":"schedule not found"}},"security":[{"bearer":[]}],"summary":"GET /api/pm-schedules/{id}/runs — the append-only run ledger (the run\nrecord; the worker emits nothing).","tags":["pm-schedules"]}},"/api/v1/portal-users/{id}":{"delete":{"operationId":"portal_users_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/portal-users/{id} — hard removal (sessions cascade). Prefer\nPATCH status=disabled to preserve history.","tags":["portal-admin"]},"patch":{"operationId":"portal_users_patch","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchPortalUserRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalUser"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /api/portal-users/{id} — rename / re-tier / enable / disable /\nreset password.","tags":["portal-admin"]}},"/api/v1/portal/appointment-requests":{"get":{"operationId":"portal_list_appointment_requests","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AppointmentRequest"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/portal/appointment-requests — the caller's OWN requests,\nnewest first.","tags":["portal"]},"post":{"operationId":"portal_create_appointment_request","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalCreateAppointmentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalAppointmentRequestMutationResponse"}}},"description":""},"404":{"description":"requested staff member not found"},"409":{"description":"idempotency key reused"},"422":{"description":"validation failure (span, lead time, or slot outside published availability)"}},"security":[{"bearer":[]}],"summary":"POST /api/portal/appointment-requests — ask for a slot on a staff\nmember's calendar. The slot must be FULLY inside one free interval of\nthat staff member's PUBLISHED availability (windows minus time-off\nminus bookings) — requests are intents, so several clients may hold\noverlapping pending requests; the staff approval is the single point\nthat books the calendar (and re-validates everything).","tags":["portal"]}},"/api/v1/portal/appointment-requests/{id}/cancel":{"post":{"operationId":"portal_cancel_appointment_request","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalCancelRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalAppointmentRequestMutationResponse"}}},"description":""},"404":{"description":"request not owned by this portal identity"},"409":{"description":"request is not pending, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/portal/appointment-requests/{id}/cancel — the requester\nwithdraws their own still-pending request. Terminal; decided requests\ncannot be cancelled here (staff owns the calendar once decided).","tags":["portal"]}},"/api/v1/portal/csat/{token}":{"get":{"operationId":"portal_csat_view","parameters":[{"in":"path","name":"token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalCsatView"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/portal/csat/{token} — validity check for the in-app response\nview. Scoped TWICE: the survey row must belong to the caller's client,\nand the token must be live (sent, unexpired). Read-only — rendering the\nform never burns the single-use token.","tags":["portal"]},"post":{"operationId":"portal_csat_submit","parameters":[{"in":"path","name":"token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalCsatSubmit"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalCsatView"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/portal/csat/{token} — the single-use consume + score record.\nThe consume UPDATE wins exactly once per token; the event (and the\naudit row) ride the same transaction, so `csat.response_recorded`\nnever fires for a replayed or expired link.","tags":["portal"]}},"/api/v1/portal/invoices":{"get":{"operationId":"portal_list_invoices","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PortalInvoice"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/portal/invoices — the caller's client's invoices (issued and\npaid only), newest issued first.","tags":["portal"]}},"/api/v1/portal/invoices/{id}":{"get":{"operationId":"portal_get_invoice","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalInvoiceDetail"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/portal/invoices/{id} — one of the caller's client's invoices\nwith lines and payment receipts. Another client's document (or a\nvoided one) is a 404, indistinguishable from absent.","tags":["portal"]}},"/api/v1/portal/invoices/{id}/document":{"get":{"operationId":"portal_invoice_document","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"text/html":{"schema":{"type":"string"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/portal/invoices/{id}/document — the SAME print-ready HTML\ndocument staff print (shared renderer), client-scoped: another\nclient's (or a void) invoice is a plain 404.","tags":["portal"]}},"/api/v1/portal/invoices/{id}/pay":{"post":{"operationId":"portal_pay_invoice","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalPayRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalPayResponse"}}},"description":""},"404":{"description":"invoice not visible to this client"},"409":{"description":"invoice not payable, overpayment, version conflict, or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/portal/invoices/{id}/pay — record a payment through the\n`PaymentProvider` seam from the portal. v1 resolves the built-in\nnone-mode provider: NOTHING is charged externally; the receipt\n(\"ledger-only\") records that the client settled the amount — the UI\npresents it exactly that way. Provenance: the payment row keeps\n`created_by = NULL` (portal identities are not staff rows), the audit\nrow and the write trail carry the caller's portal actor kind.","tags":["portal"]}},"/api/v1/portal/kb/articles":{"get":{"operationId":"portal_list_kb_articles","parameters":[{"in":"query","name":"category_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"in":"query","name":"offset","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/KbArticle"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/portal/kb/articles — published client-facing articles\n(optionally narrowed to one visible category), gated by the effective\nclient-facing rule in the query.","tags":["portal"]}},"/api/v1/portal/kb/articles/{id}":{"get":{"operationId":"portal_get_kb_article","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KbArticle"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/portal/kb/articles/{id} — one client-gated article; a draft,\narchived, unflagged, or foreign article is a plain 404.","tags":["portal"]}},"/api/v1/portal/kb/categories":{"get":{"operationId":"portal_kb_categories","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/KbCategory"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/portal/kb/categories — the client-visible category tree.","tags":["portal"]}},"/api/v1/portal/kb/search":{"get":{"operationId":"portal_kb_search","parameters":[{"description":"Websearch query (plain words, quoted phrases, `OR`, `-`\nexclusions — Postgres websearch syntax).","in":"query","name":"q","required":true,"schema":{"type":"string"}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"in":"query","name":"offset","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PortalKbSearchHit"},"type":"array"}}},"description":""},"422":{"description":"empty query"}},"security":[{"bearer":[]}],"summary":"GET /api/portal/kb/search — FTS over client-visible published\narticles only (websearch query + trigram fallback, ranked the same\nway the unified search ranks).","tags":["portal"]}},"/api/v1/portal/login":{"post":{"operationId":"portal_login","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalLoginRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalLoginResponse"}}},"description":""},"401":{"description":"bad credentials"}},"summary":"POST /api/portal/login — argon2 verify against `portal_users`. No MFA in\nportal v1 (documented deferral). Disabled accounts resolve to nothing →\nuniform 401, never revealing which half failed.","tags":["portal"]}},"/api/v1/portal/magic-link/confirm":{"post":{"description":"One 401 for unknown, already-used, expired, and disabled alike. The claim\nitself enforces the standing check (the UPDATE joins `portal_users`), so\nthere is no window between \"may this identity sign in?\" and \"spend it\".","operationId":"portal_magic_link_confirm","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalMagicLinkConfirm"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalLoginResponse"}}},"description":""},"401":{"description":"the link is unknown, used, expired, or the account is disabled"},"429":{"description":"too many attempts"}},"summary":"POST /api/portal/magic-link/confirm — spend the link, get portal tokens.","tags":["portal"]}},"/api/v1/portal/magic-link/request":{"post":{"description":"Throttled per address and per IP on the portal's OWN gate kind, so a run\nagainst portal addresses never locks a staff member out of `/api/login`.","operationId":"portal_magic_link_request","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalMagicLinkRequest"}}},"required":true},"responses":{"200":{"description":"always ok — the answer never depends on whether the address matches"},"422":{"description":"validation error"},"429":{"description":"too many requests"}},"summary":"POST /api/portal/magic-link/request — mail a single-use sign-in link.","tags":["portal"]}},"/api/v1/portal/me":{"get":{"operationId":"portal_me","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalProfile"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/portal/me — the signed-in portal identity.","tags":["portal"]}},"/api/v1/portal/quotes":{"get":{"operationId":"portal_list_quotes","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PortalQuote"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/portal/quotes — the caller's client's quotes (non-draft\nonly), newest first.","tags":["portal"]}},"/api/v1/portal/quotes/{id}":{"get":{"operationId":"portal_get_quote","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalQuoteDetail"}}},"description":""},"404":{"description":"quote not visible to this client"}},"security":[{"bearer":[]}],"summary":"GET /api/portal/quotes/{id} — one quote of the caller's client with\nits lines. A foreign quote or a draft is a bare 404 (no existence\nleak).","tags":["portal"]}},"/api/v1/portal/quotes/{id}/approve":{"post":{"operationId":"portal_approve_quote","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalQuoteDecisionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalQuoteDecisionResponse"}}},"description":""},"404":{"description":"quote not visible to this client"},"409":{"description":"quote not decidable, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/portal/quotes/{id}/approve — the client accepts a sent\nquote. Same `sent → accepted` leg as the staff flow; the provider\ncan then run the conversion-to-invoice seam.","tags":["portal"]}},"/api/v1/portal/quotes/{id}/decline":{"post":{"operationId":"portal_decline_quote","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalQuoteDecisionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalQuoteDecisionResponse"}}},"description":""},"404":{"description":"quote not visible to this client"},"409":{"description":"quote not decidable, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/portal/quotes/{id}/decline — the client declines a sent\nquote. Same `sent → declined` leg as the staff flow; the optional\nreason is audit-only, never shown back as validation.","tags":["portal"]}},"/api/v1/portal/refresh":{"post":{"operationId":"portal_refresh","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalRefreshRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalLoginResponse"}}},"description":""}},"summary":"POST /api/portal/refresh — rotation within the portal session family.\nA presented token resolves ONLY via its live session row; rotation\nrevokes that row, so replaying an old refresh token fails.","tags":["portal"]}},"/api/v1/portal/schedule/slots":{"get":{"operationId":"portal_schedule_slots","parameters":[{"description":"Range start (inclusive), ISO date.","in":"query","name":"from","required":true,"schema":{"format":"date","type":"string"}},{"description":"Range end (inclusive), ISO date.","in":"query","name":"to","required":true,"schema":{"format":"date","type":"string"}},{"description":"Optional: narrow to one staff member.","in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PortalStaffSlots"},"type":"array"}}},"description":""},"422":{"description":"range exceeds 31 days"}},"security":[{"bearer":[]}],"summary":"GET /api/portal/schedule/slots — published availability as FREE\nintervals per staff member: weekly windows minus time-off minus\nalready-booked time (appointments + on-call). Slots that already\nstarted are dropped; anything shorter than 30 minutes is noise.","tags":["portal"]}},"/api/v1/portal/tickets":{"get":{"operationId":"portal_list_tickets","parameters":[{"in":"query","name":"board_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"category","required":false,"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TicketStatusCategory"}]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"in":"query","name":"offset","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PortalTicketView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/portal/tickets — the caller's client's tickets across EXPOSED\nboards only. Unexposed boards are invisible, not empty-filtered.","tags":["portal"]}},"/api/v1/portal/tickets/{id}":{"get":{"operationId":"portal_get_ticket","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalTicketDetail"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/portal/tickets/{id} — detail plus comments through the\nnotes-taxonomy whitelist for the caller's tier. A ticket from another\nclient (or an unexposed board) is a 404, indistinguishable from absent.","tags":["portal"]}},"/api/v1/portal/tickets/{id}/attachments":{"get":{"operationId":"portal_list_attachments","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PortalAttachmentView"},"type":"array"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/portal/tickets/{id}/attachments — attachments the portal\ncaller may see: ticket-level files always; comment files only when the\nowning note kind is visible to this tier.","tags":["portal"]}},"/api/v1/portal/tickets/{id}/attachments/{attachment_id}/download":{"get":{"operationId":"portal_download_attachment","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"attachment_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/octet-stream":{"schema":{"items":{"format":"int32","minimum":0,"type":"integer"},"type":"array"}}},"description":""},"404":{"description":""},"409":{"description":"the attachment has no admission verdict, so it is not served"}},"security":[{"bearer":[]}],"summary":"GET /api/portal/tickets/{id}/attachments/{attachment_id}/download —\nstreams one visible attachment through the API. Hidden or foreign\nattachments are plain 404s (no existence leak).","tags":["portal"]}},"/api/v1/portal/tickets/{id}/reply":{"post":{"operationId":"portal_reply","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalReplyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketComment"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/portal/tickets/{id}/reply — ALWAYS creates a `client_visible`\ncomment regardless of tier, authored under portal audit provenance\n(`actor_kind = portal_contact | portal_approver | portal_it_manager`;\n`author_id` stays NULL because portal identities are not staff rows).\nEmits the SAME `ticket.comment_added` event as staff replies so every\ndownstream notification path works unchanged.","tags":["portal"]}},"/api/v1/presence/heartbeat":{"post":{"operationId":"presence_heartbeat","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HeartbeatRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresenceRoster"}}},"description":""},"401":{"description":"missing or invalid bearer token"},"422":{"description":"malformed entity/entity_id"}},"security":[{"bearer":[]}],"summary":"POST /api/presence/heartbeat — keep this viewer's presence alive on\n{entity, entity_id}; flips the editing flag when it changed. Returns\nthe current roster so the caller can paint it immediately.","tags":["presence"]}},"/api/v1/project-templates":{"get":{"operationId":"project_templates_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ProjectTemplate"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/project-templates","tags":["projects"]},"post":{"operationId":"project_templates_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTemplateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateMutationResponse"}}},"description":""},"409":{"description":"template name already in use"},"422":{"description":"invalid template spec"}},"security":[{"bearer":[]}],"summary":"POST /api/project-templates — stores a blueprint (spec validated);\nemits `project.template_updated`.","tags":["projects"]}},"/api/v1/project-templates/{id}":{"delete":{"operationId":"project_templates_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":""},"404":{"description":"template not visible in scope (rls_sparse)"}},"security":[{"bearer":[]}],"summary":"DELETE /api/project-templates/{id} — blueprints are data, not live\nreferences: projects already instantiated are untouched; emits\n`project.template_removed`.","tags":["projects"]},"patch":{"operationId":"project_templates_patch","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchTemplateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateMutationResponse"}}},"description":""},"404":{"description":"template not visible in scope (rls_sparse)"},"422":{"description":"invalid template spec"}},"security":[{"bearer":[]}],"summary":"PATCH /api/project-templates/{id} — emits `project.template_updated`.","tags":["projects"]}},"/api/v1/project-templates/{id}/instantiate":{"post":{"operationId":"project_templates_instantiate","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstantiateTemplateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectInstantiateResponse"}}},"description":""},"404":{"description":"template not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"invalid template spec or request"}},"security":[{"bearer":[]}],"summary":"POST /api/project-templates/{id}/instantiate — expands the template\ninto a REAL project: phases, tasks, dependencies, and every lifecycle\nevent, all in ONE transaction (claim → expand → insert → events →\nrecord). Rollback on any failure leaves zero partial rows.","tags":["projects"]}},"/api/v1/project-views":{"get":{"operationId":"project_views_list","parameters":[{"description":"Scope to one project's views; omit for the org's ENTIRE view set.","in":"query","name":"project_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ProjectView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/project-views — one project's views, or all org views when\n`project_id` is omitted.","tags":["projects"]},"post":{"operationId":"project_views_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateViewRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewMutationResponse"}}},"description":""},"404":{"description":"project not visible in scope (rls_sparse)"},"409":{"description":"view name already in use in the scope"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/project-views — saves a view; emits `project.view_updated`.","tags":["projects"]}},"/api/v1/project-views/{id}":{"delete":{"operationId":"project_views_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":""},"404":{"description":"view not visible in scope (rls_sparse)"}},"security":[{"bearer":[]}],"summary":"DELETE /api/project-views/{id} — emits `project.view_removed`.","tags":["projects"]},"patch":{"operationId":"project_views_patch","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchViewRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewMutationResponse"}}},"description":""},"404":{"description":"view not visible in scope (rls_sparse)"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"PATCH /api/project-views/{id} — renames / respecs a view; emits\n`project.view_updated`.","tags":["projects"]}},"/api/v1/projects":{"get":{"operationId":"projects_list","parameters":[{"in":"query","name":"status","required":false,"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ProjectStatus"}]}},{"in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"in":"query","name":"offset","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Project"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/projects — filtered, newest-first list.","tags":["projects"]},"post":{"operationId":"projects_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectMutationResponse"}}},"description":""},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/projects — creates the project and emits `project.created` in\none transaction. Instant-first (IF1/IF2): idempotency-keyed,\nversion-gated, read-your-write response.","tags":["projects"]}},"/api/v1/projects/{id}":{"get":{"operationId":"projects_get_one","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}},"description":""},"404":{"description":"not found"}},"security":[{"bearer":[]}],"summary":"GET /api/projects/{id}","tags":["projects"]},"patch":{"operationId":"projects_patch","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchProjectRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectMutationResponse"}}},"description":""},"404":{"description":"project not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"PATCH /api/projects/{id} — tri-state partial update. Instant-first\n(IF1/IF2): idempotency-keyed, version-gated, returns prior + new values.","tags":["projects"]}},"/api/v1/projects/{id}/billing-releases":{"get":{"operationId":"projects_billing_releases","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ProjectBillingRelease"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/projects/{id}/billing-releases — the release ledger: every\nstaged milestone/fee for this project with its status (`pending`\nbills on the next run reaching its window; `billed` carries the\nconsuming invoice; `cancelled` terminal).","tags":["projects"]}},"/api/v1/projects/{id}/billing-releases/{release_id}/cancel":{"post":{"operationId":"projects_billing_release_cancel","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"release_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectBillingRelease"}}},"description":""},"404":{"description":"release not visible/already settled (rls_sparse)"}},"security":[{"bearer":[]}],"summary":"POST /api/projects/{id}/billing-releases/{release_id}/cancel — cancel\na PENDING release (admin correction; terminal). Billed releases are\nimmutable — they ride an invoice, and invoices correct through credit\nnotes.","tags":["projects"]}},"/api/v1/projects/{id}/cost-rollup":{"get":{"operationId":"projects_cost_rollup","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCostRollupView"}}},"description":""},"404":{"description":"project not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/projects/{id}/cost-rollup — posted PO bills in base currency\n(ADR 0002 locked) plus the project budget for comparison.","tags":["purchasing"]}},"/api/v1/projects/{id}/gantt":{"get":{"operationId":"projects_gantt","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectGanttView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/projects/{id}/gantt — the dated timeline plus its undated\nremainder, each phase carrying its budget burn.","tags":["projects"]}},"/api/v1/projects/{id}/phase-burn":{"get":{"operationId":"projects_phase_burn","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PhaseBurn"},"type":"array"}}},"description":""},"404":{"description":"project not visible in scope (rls_sparse)"}},"security":[{"bearer":[]}],"summary":"GET /api/projects/{id}/phase-burn — the burn read model (report P3):\nconsumed approved billable minutes and card-priced value per phase\nagainst the phase's budget, with the typed `none | warn | over`\nalert state (`warn` at ≥80%, `over` at ≥100%).","tags":["projects"]}},"/api/v1/projects/{id}/phases":{"get":{"operationId":"projects_list_phases","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ProjectPhase"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/projects/{id}/phases — ordered by sort_order.","tags":["projects"]},"post":{"operationId":"projects_create_phase","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePhaseRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhaseMutationResponse"}}},"description":""},"404":{"description":"project not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/projects/{id}/phases — adds a phase; emits\n`project.phase_created`.","tags":["projects"]}},"/api/v1/projects/{id}/phases/{phase_id}":{"patch":{"operationId":"projects_patch_phase","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"phase_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchPhaseRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhaseMutationResponse"}}},"description":""},"404":{"description":"phase not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"PATCH /api/projects/{id}/phases/{phase_id} — tri-state partial update.","tags":["projects"]}},"/api/v1/projects/{id}/phases/{phase_id}/complete":{"post":{"operationId":"projects_complete_phase","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"phase_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MutationParams"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhaseMutationResponse"}}},"description":""},"404":{"description":"phase not visible in scope (rls_sparse)"},"409":{"description":"already completed, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/projects/{id}/phases/{phase_id}/complete — stamps the phase\ncompleted and, when it is a billing milestone, emits\n`project.milestone_completed` in the same transaction (the core-billing\nseam). Instant-first: the idempotency key guarantees a retried\ncompletion can never double-emit the billing event.","tags":["projects"]}},"/api/v1/projects/{id}/tasks":{"get":{"operationId":"projects_list_tasks","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"phase_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"status","required":false,"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TaskStatus"}]}},{"in":"query","name":"assignee_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"milestone","required":false,"schema":{"type":["boolean","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"in":"query","name":"offset","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ProjectTask"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/projects/{id}/tasks — filtered by phase/status/assignee and\nthe milestone flag, sort_order-ordered.","tags":["projects"]},"post":{"operationId":"projects_create_task","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaskRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskMutationResponse"}}},"description":""},"404":{"description":"project not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/projects/{id}/tasks — creates the task (optionally with its\ndependency set) and emits `project.task_created` in one transaction.","tags":["projects"]}},"/api/v1/projects/{id}/tasks/{task_id}":{"patch":{"operationId":"projects_patch_task","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"task_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchTaskRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskMutationResponse"}}},"description":""},"404":{"description":"task not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"PATCH /api/projects/{id}/tasks/{task_id} — tri-state partial update.\n`status: \"completed\"` is rejected: completion runs through the complete\nendpoint (dependency gating + the billing seam).","tags":["projects"]}},"/api/v1/projects/{id}/tasks/{task_id}/complete":{"post":{"operationId":"projects_complete_task","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"task_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MutationParams"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskMutationResponse"}}},"description":""},"404":{"description":"task not visible in scope (rls_sparse)"},"409":{"description":"dependencies unsatisfied, already completed, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/projects/{id}/tasks/{task_id}/complete — the guarded\ncompletion: every finish-to-start dependency must already be completed\n(typed 409 `task_dependencies_unsatisfied` naming the blockers), then\n`project.task_completed` fires — plus `project.milestone_completed`\nwhen the task is a billing milestone. Instant-first: the idempotency\nkey makes retried completions replay instead of double-billing.","tags":["projects"]}},"/api/v1/projects/{id}/tasks/{task_id}/dependencies":{"get":{"operationId":"projects_get_dependencies","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"task_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DependencyListResponse"}}},"description":""},"404":{"description":"not found"}},"security":[{"bearer":[]}],"summary":"GET /api/projects/{id}/tasks/{task_id}/dependencies","tags":["projects"]},"put":{"operationId":"projects_set_dependencies","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"task_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetDependenciesRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DependencyListResponse"}}},"description":""},"404":{"description":"task not visible in scope (rls_sparse)"},"422":{"description":"dependency set rejected (self-dep, cross-project, or cycle)"}},"security":[{"bearer":[]}],"summary":"PUT /api/projects/{id}/tasks/{task_id}/dependencies — replaces the\nwhole dependency set, cycle-checked over the project graph BEFORE any\nwrite; emits `project.task_updated`.","tags":["projects"]}},"/api/v1/provenance":{"get":{"operationId":"provenance_list","parameters":[{"description":"Keyset cursor from a previous page's `next_cursor`.","in":"query","name":"cursor","required":false,"schema":{"type":["string","null"]}},{"description":"Actor-kind filter (the write-trail vocabulary: human, agent,\nimport, ...).","in":"query","name":"actor_kind","required":false,"schema":{"type":["string","null"]}},{"description":"The \"who\" filter: a user uuid. Ignored (forced to the caller) for\ncallers without the audit permission.","in":"query","name":"actor","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Entity family, e.g. \"ticket\". Required by `entity_id` and `field`.","in":"query","name":"entity","required":false,"schema":{"type":["string","null"]}},{"description":"Record id; requires `entity`.","in":"query","name":"entity_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Single-field provenance; requires `entity`.","in":"query","name":"field","required":false,"schema":{"type":["string","null"]}},{"description":"Agent session filter (rows carry it when present).","in":"query","name":"session_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Agent skill filter (rows carry it when present).","in":"query","name":"skill","required":false,"schema":{"type":["string","null"]}},{"description":"Rule-set hash filter (rows carry it when present).","in":"query","name":"ruleset_hash","required":false,"schema":{"type":["string","null"]}},{"description":"Restrict to undo reverts (`true`) or exclude them (`false`).","in":"query","name":"undo","required":false,"schema":{"type":["boolean","null"]}},{"description":"Inclusive lower bound on occurred_at.","in":"query","name":"from","required":false,"schema":{"format":"date-time","type":["string","null"]}},{"description":"Inclusive upper bound on occurred_at.","in":"query","name":"to","required":false,"schema":{"format":"date-time","type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvenancePage"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/provenance — the provenance read model: who / record+field /\nbefore-after / actor kind / agent session+skill+rule-set hash / when,\nnewest first, filterable, cursor-paged. Extends the 2.3 audit-log\nquery API with provenance-grade filters backed by the materialized\nprojection (no raw-row scans).","tags":["audit"]}},"/api/v1/public/scheduling-links/{token}":{"get":{"operationId":"public_scheduling_link_view","parameters":[{"in":"path","name":"token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicLinkView"}}},"description":""},"404":{"description":"unknown token"},"409":{"description":"revoked or already used"},"429":{"description":""}},"summary":"GET /api/public/scheduling-links/{token} — unauthenticated, token-\nscoped, rate-limited. First view stamps the link's lifecycle\n(`first_viewed_at` + count) and emits `scheduling_link.viewed`.","tags":["scheduling"]}},"/api/v1/public/scheduling-links/{token}/book":{"post":{"operationId":"public_scheduling_link_book","parameters":[{"in":"path","name":"token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookResponse"}}},"description":""},"404":{"description":"unknown token"},"409":{"description":"revoked/used/expired link, slot no longer free, double-booking backstop, or idempotency key reused"},"422":{"description":"slot outside the link's availability or booking window"},"429":{"description":""}},"summary":"POST /api/public/scheduling-links/{token}/book — unauthenticated but\ntoken-scoped and rate-limited. Race-safe exactly like portal\nappointment-request approval: the availability + conflict gate and\nthe single-winner link flip run INSIDE the booking transaction, and\nthe appointments EXCLUDE constraint is the concurrency backstop.","tags":["scheduling"]}},"/api/v1/purchase-orders":{"get":{"operationId":"purchase_orders_list","parameters":[{"in":"query","name":"status","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"vendor_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"project_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"q","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PurchaseOrderSummary"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/purchase-orders","tags":["purchasing"]},"post":{"operationId":"purchase_orders_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderMutationResponse"}}},"description":""},"404":{"description":"vendor or project not visible in scope"},"409":{"description":"order number already exists"}},"security":[{"bearer":[]}],"summary":"POST /api/purchase-orders","tags":["purchasing"]}},"/api/v1/purchase-orders/{id}":{"get":{"operationId":"purchase_orders_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseOrderDetail"}}},"description":""},"404":{"description":"order not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/purchase-orders/{id} — header + lines + receipts + bill.","tags":["purchasing"]},"patch":{"operationId":"purchase_orders_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderMutationResponse"}}},"description":""},"404":{"description":"order not visible in scope"},"409":{"description":"order is not a draft"}},"security":[{"bearer":[]}],"summary":"PATCH /api/purchase-orders/{id} — draft-only header edits.","tags":["purchasing"]}},"/api/v1/purchase-orders/{id}/bill":{"get":{"operationId":"purchase_orders_get_bill","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseBill"}}},"description":""},"404":{"description":"no bill recorded for this order"}},"security":[{"bearer":[]}],"summary":"GET /api/purchase-orders/{id}/bill","tags":["purchasing"]},"put":{"operationId":"purchase_orders_record_bill","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordBillRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillMutationResponse"}}},"description":""},"409":{"description":"order not billable yet, or the bill is already posted"}},"security":[{"bearer":[]}],"summary":"PUT /api/purchase-orders/{id}/bill — record or edit the draft bill.","tags":["purchasing"]}},"/api/v1/purchase-orders/{id}/bill/post":{"post":{"operationId":"purchase_orders_post_bill","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloseOrderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillMutationResponse"}}},"description":""},"404":{"description":"no bill recorded"},"409":{"description":"the bill is already posted"}},"security":[{"bearer":[]}],"summary":"POST /api/purchase-orders/{id}/bill/post — the rollup moment: locks\nthe ADR 0002 FX snapshot and stamps `posted_at`.","tags":["purchasing"]}},"/api/v1/purchase-orders/{id}/cancel":{"post":{"operationId":"purchase_orders_cancel","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelOrderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderMutationResponse"}}},"description":""},"409":{"description":"order already received goods or is terminal"}},"security":[{"bearer":[]}],"summary":"POST /api/purchase-orders/{id}/cancel — draft|ordered, nothing received.","tags":["purchasing"]}},"/api/v1/purchase-orders/{id}/close":{"post":{"operationId":"purchase_orders_close","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloseOrderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderMutationResponse"}}},"description":""},"409":{"description":"order is not in a closable state"}},"security":[{"bearer":[]}],"summary":"POST /api/purchase-orders/{id}/close — received|partially_received →\nclosed; the close disposition is the typed under-receipt record.","tags":["purchasing"]}},"/api/v1/purchase-orders/{id}/deeplinks":{"get":{"operationId":"purchase_orders_deeplinks","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseOrderDeeplinks"}}},"description":""},"404":{"description":"order not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/purchase-orders/{id}/deeplinks — the one-click open list:\nvendor portal links first, then PO-level links, then per-line links\n(with their line context), each https-validated at write time.","tags":["purchasing"]}},"/api/v1/purchase-orders/{id}/lines":{"post":{"operationId":"purchase_orders_add_line","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderLineInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderMutationResponse"}}},"description":""},"409":{"description":"order is not a draft"},"422":{"description":"line validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/purchase-orders/{id}/lines — draft-only line add.","tags":["purchasing"]}},"/api/v1/purchase-orders/{id}/lines/{line_id}":{"delete":{"operationId":"purchase_orders_delete_line","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"line_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteOrderLineRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderMutationResponse"}}},"description":""},"404":{"description":"order or line not visible in scope"},"409":{"description":"order is not a draft"}},"security":[{"bearer":[]}],"summary":"DELETE /api/purchase-orders/{id}/lines/{line_id} — draft-only.","tags":["purchasing"]},"patch":{"operationId":"purchase_orders_update_line","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"line_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddOrderLineRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderMutationResponse"}}},"description":""},"404":{"description":"order or line not visible in scope"},"409":{"description":"order is not a draft or the line already received goods"}},"security":[{"bearer":[]}],"summary":"PATCH /api/purchase-orders/{id}/lines/{line_id} — draft-only line edit.","tags":["purchasing"]}},"/api/v1/purchase-orders/{id}/links":{"get":{"operationId":"purchase_orders_list_links","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ExternalLink"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/purchase-orders/{id}/links","tags":["purchasing"]},"post":{"operationId":"purchase_orders_add_link","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExternalLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkMutationResponse"}}},"description":""},"404":{"description":"order or line not visible in scope"},"422":{"description":"line not in this order, or non-https url"}},"security":[{"bearer":[]}],"summary":"POST /api/purchase-orders/{id}/links — PO-level or (with `line_id`) a\nper-line order/tracking URL.","tags":["purchasing"]}},"/api/v1/purchase-orders/{id}/order":{"post":{"operationId":"purchase_orders_send","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloseOrderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderMutationResponse"}}},"description":""},"409":{"description":"order is not a draft or has no lines"}},"security":[{"bearer":[]}],"summary":"POST /api/purchase-orders/{id}/order — draft → ordered.","tags":["purchasing"]}},"/api/v1/purchase-orders/{id}/receive":{"post":{"operationId":"purchase_orders_receive","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiveOrderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiptMutationResponse"}}},"description":""},"404":{"description":"order or location not visible in scope"},"409":{"description":"order not receivable, over-receipt without acknowledgement, or a line without a stock item"},"422":{"description":"serial numbers inconsistent with the received quantity"}},"security":[{"bearer":[]}],"summary":"POST /api/purchase-orders/{id}/receive — the receipt that posts into\nthe lane-1 movement ledger at `location_id`.","tags":["purchasing"]}},"/api/v1/quotes":{"get":{"operationId":"quotes_list","parameters":[{"in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Exact status: `draft|sent|accepted|declined|expired`.","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Quote"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/quotes — newest first, client/status filters.","tags":["commercial"]},"post":{"operationId":"quotes_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateQuoteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteMutationResponse"}}},"description":""},"404":{"description":"client or catalog item not visible in scope"},"409":{"description":"idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/quotes — create a numbered draft. Lines snapshot catalog\ncontext; roll-ups are computed server-side (never trusted from the\nclient).","tags":["commercial"]}},"/api/v1/quotes/{id}":{"get":{"operationId":"quotes_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteDetail"}}},"description":""},"404":{"description":"not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/quotes/{id} — the quote plus its lines.","tags":["commercial"]},"patch":{"operationId":"quotes_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateQuoteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteMutationResponse"}}},"description":""},"404":{"description":"quote or catalog item not visible in scope"},"409":{"description":"quote is not a draft, version conflict, or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"PATCH /api/quotes/{id} — edit a DRAFT (header + optionally the whole\nline list). Sent+ quotes are immutable: typed 409.","tags":["commercial"]}},"/api/v1/quotes/{id}/accept":{"post":{"operationId":"quotes_accept","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteLifecycleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteMutationResponse"}}},"description":""},"404":{"description":"quote not visible in scope"},"409":{"description":"illegal transition, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/quotes/{id}/accept — sent → accepted (terminal).","tags":["commercial"]}},"/api/v1/quotes/{id}/convert-to-invoice":{"post":{"operationId":"quotes_convert_to_invoice","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConvertQuoteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConvertQuoteResponse"}}},"description":""},"404":{"description":"quote not visible in scope"},"409":{"description":"quote not accepted / outside validity / already converted, version conflict, or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/quotes/{id}/convert-to-invoice — materialize an ACCEPTED\nquote into a fixed-fee contract + DRAFT invoice through the existing\ninvoicing persistence (the seam's rationale is in the module docs).","tags":["commercial"]}},"/api/v1/quotes/{id}/decline":{"post":{"operationId":"quotes_decline","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteLifecycleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteMutationResponse"}}},"description":""},"404":{"description":"quote not visible in scope"},"409":{"description":"illegal transition, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/quotes/{id}/decline — sent → declined. Terminal.","tags":["commercial"]}},"/api/v1/quotes/{id}/expire":{"post":{"operationId":"quotes_expire","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteLifecycleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteMutationResponse"}}},"description":""},"404":{"description":"quote not visible in scope"},"409":{"description":"still inside validity window, illegal transition, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/quotes/{id}/expire — explicitly expire a quote PAST its\nvalidity window (decline is the in-window exit).","tags":["commercial"]}},"/api/v1/quotes/{id}/send":{"post":{"operationId":"quotes_send","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteLifecycleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteMutationResponse"}}},"description":""},"404":{"description":"quote not visible in scope"},"409":{"description":"illegal transition, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"One lifecycle leg: shared shape for send/accept/decline (expire has a\nPOST /api/quotes/{id}/send — draft → sent. Locks the document.","tags":["commercial"]}},"/api/v1/rag/chunks":{"post":{"operationId":"rag_chunks_ingest","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RagIngestRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RagIngestResponse"}}},"description":""},"401":{"description":"not authenticated"},"403":{"description":"missing kb:edit / rag.write permission"},"409":{"description":"idempotency key reused or version conflict"},"422":{"description":"validation error or wrong-width vector (must be 768)"}},"security":[{"bearer":[]}],"summary":"POST /api/v1/rag/chunks — batch upsert of RAG passage and media chunks.","tags":["rag"]}},"/api/v1/rag/delete":{"post":{"operationId":"rag_source_delete_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RagDeleteSourceRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RagDeleteSourceResponse"}}},"description":""},"401":{"description":"not authenticated"},"403":{"description":"missing kb:edit / rag.write permission"},"409":{"description":"idempotency key reused or version conflict"},"422":{"description":"invalid namespace or source identifier"}},"security":[{"bearer":[]}],"summary":"POST /api/v1/rag/delete — deletes all chunks for `(namespace, source_kind,\nsource_id)` and returns a verifiable deletion receipt (supports arbitrary\nURI/slash `source_id` values and the IF1–IF3 idempotency envelope).","tags":["rag"]}},"/api/v1/rag/deletion-receipts":{"get":{"operationId":"rag_deletion_receipts_list","parameters":[{"in":"query","name":"namespace","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"source_kind","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"source_id","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RagDeletionReceiptList"}}},"description":""},"401":{"description":"not authenticated"},"403":{"description":"missing kb:view / rag.read permission"}},"security":[{"bearer":[]}],"summary":"GET /api/v1/rag/deletion-receipts — lists verifiable deletion receipts for\nthe caller's org (including receipts minted automatically by core entity\ndeletion cascades).","tags":["rag"]}},"/api/v1/rag/deletion-receipts/{id}":{"get":{"operationId":"rag_deletion_receipt_get","parameters":[{"description":"Deletion receipt ID","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RagDeletionReceipt"}}},"description":""},"401":{"description":"not authenticated"},"403":{"description":"missing kb:view / rag.read permission"},"404":{"description":"receipt not found in caller's org"}},"security":[{"bearer":[]}],"summary":"GET /api/v1/rag/deletion-receipts/{id} — fetches a single verifiable\ndeletion receipt by `receipt_id` under RLS.","tags":["rag"]}},"/api/v1/rag/query":{"post":{"operationId":"rag_query","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RagQueryRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RagQueryResponse"}}},"description":""},"401":{"description":"not authenticated"},"403":{"description":"missing kb:view / rag.read permission"},"422":{"description":"invalid query parameters or wrong-width query_vector (must be 768)"}},"security":[{"bearer":[]}],"summary":"POST /api/v1/rag/query — hybrid BM25 + exact-KNN vector query with RRF\nfusion and mandatory source attribution.","tags":["rag"]}},"/api/v1/rag/sources/{source_kind}/{source_id}":{"delete":{"operationId":"rag_source_delete","parameters":[{"description":"Source entity family (e.g. ticket, attachment, kb_article)","in":"path","name":"source_kind","required":true,"schema":{"type":"string"}},{"description":"Source identifier within source_kind","in":"path","name":"source_id","required":true,"schema":{"type":"string"}},{"description":"Target namespace from which to delete the source's chunks (defaults to\n`\"default\"` when omitted so parameterless probes and simple callers\nhave a deterministic default).","in":"query","name":"namespace","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RagDeleteSourceResponse"}}},"description":""},"401":{"description":"not authenticated"},"403":{"description":"missing kb:edit / rag.write permission"},"422":{"description":"invalid namespace or source identifier"}},"security":[{"bearer":[]}],"summary":"DELETE /api/v1/rag/sources/{source_kind}/{source_id} — deletes all chunks\nfor `(namespace, source_kind, source_id)` and returns a verifiable deletion\nreceipt.","tags":["rag"]}},"/api/v1/reply-snippets":{"get":{"operationId":"list","parameters":[{"description":"Offer-board filter: only snippets scoped to every board or this\nboard (the composer passes the ticket's board).","in":"query","name":"board_id","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"Search-in-composer predicate: case-insensitive substring over the\nsnippet's name and body.","in":"query","name":"q","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplySnippetsListResponse"}}},"description":""},"403":{"description":"requires tickets:view or admin:view"}},"security":[{"bearer":[]}],"summary":"GET /api/v1/reply-snippets — the org's snippet library with the\nsearch-in-composer predicate applied (`board_id` offer filter + `q`\ncase-insensitive substring over name and body).","tags":["admin"]},"post":{"operationId":"create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateReplySnippetRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplySnippetMutationResponse"}}},"description":""},"403":{"description":"requires admin:create"},"422":{"description":"validation failure (name/body, or out-of-vocabulary variable)"}},"security":[{"bearer":[]}],"summary":"POST /api/v1/reply-snippets — add a snippet to the library.","tags":["admin"]}},"/api/v1/reply-snippets/{id}":{"delete":{"operationId":"delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplySnippetMutationResponse"}}},"description":""},"403":{"description":"requires admin:delete"},"404":{"description":"snippet not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/v1/reply-snippets/{id} — remove a snippet from the library.","tags":["admin"]},"get":{"operationId":"get_by_id","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplySnippetView"}}},"description":""},"403":{"description":"requires tickets:view or admin:view"},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/v1/reply-snippets/{id} — fetch one snippet (cross-lane fix,\nWave 34 lane 4: the fork-on-edit flow reads the origin after forking;\nthe route was missing so reads 405'd).","tags":["admin"]},"patch":{"operationId":"update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateReplySnippetRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplySnippetMutationResponse"}}},"description":""},"403":{"description":"requires admin:edit"},"404":{"description":"snippet not visible in scope"},"409":{"description":"version gate rejected the write (IF3)"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"PATCH /api/v1/reply-snippets/{id} — edit a snippet in place (the\nlibrary is org-owned data; fork-on-edit applies when orgs want a\ndivergent copy, via the fork endpoint).","tags":["admin"]}},"/api/v1/reply-snippets/{id}/fork":{"post":{"operationId":"fork","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForkReplySnippetRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplySnippetMutationResponse"}}},"description":""},"403":{"description":"requires admin:create"},"404":{"description":"snippet not visible in scope"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/v1/reply-snippets/{id}/fork — duplicate a snippet into a new\neditable copy (templates fork-on-edit provenance pattern). The origin\nis untouched; the copy records `forked_from`.","tags":["admin"]}},"/api/v1/reply-snippets/{id}/render":{"post":{"operationId":"render","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderReplySnippetRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderedReplySnippet"}}},"description":""},"400":{"description":"out-of-vocabulary or malformed variable"},"403":{"description":"requires tickets:view or admin:view"},"404":{"description":"snippet or ticket not visible in scope"}},"security":[{"bearer":[]}],"summary":"POST /api/v1/reply-snippets/{id}/render — resolve the snippet's\nvariable tokens against a real ticket context and return the rendered\nbody (the composer inserts the RENDERED text; the portal reply renders\nthe result). Out-of-vocabulary placeholders are typed 400s.","tags":["admin"]}},"/api/v1/report-catalog":{"get":{"operationId":"report_catalog","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportCatalog"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/report-catalog — the fixed field catalog for the R6 builder\n(ADR 0001): per-view projected columns + filterable inventory with value\nclasses. Static data served from the same Rust constants the compiler\nvalidates against, so the UI can never offer an identifier the backend\nwould reject.","tags":["reports"]}},"/api/v1/report-definitions":{"get":{"operationId":"report_definitions_list","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDefinitionList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/report-definitions — visible definitions, deterministically\nordered `(name, id)`.","tags":["reports"]},"post":{"operationId":"report_definitions_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateReportDefinitionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDefinitionMutationResponse"}}},"description":""},"403":{"description":"missing reporting:create"},"409":{"description":"name conflict or idempotency key reused"},"422":{"description":"spec failed registry validation"}},"security":[{"bearer":[]}],"summary":"POST /api/report-definitions — create a stored definition through the\ninstant-first write path (idempotency key honored; response carries\n`mutation` metadata).","tags":["reports"]}},"/api/v1/report-definitions/{id}":{"delete":{"operationId":"report_definitions_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDefinitionMutationResponse"}}},"description":""},"403":{"description":"definition outside the caller's writable scope"},"404":{"description":"rls_sparse: target not visible"},"409":{"description":"version conflict"}},"security":[{"bearer":[]}],"summary":"DELETE /api/report-definitions/{id} — removal through the same gated\npath; the write trail keeps the deleted snapshot (undo substrate).","tags":["reports"]},"get":{"operationId":"report_definitions_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDefinitionView"}}},"description":""},"404":{"description":"not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/report-definitions/{id} — one visible definition; anything not\nvisible (deleted, other org, private of another user) is a bare 404.","tags":["reports"]},"put":{"operationId":"report_definitions_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateReportDefinitionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDefinitionMutationResponse"}}},"description":""},"403":{"description":"definition outside the caller's writable scope"},"404":{"description":"rls_sparse: target not visible"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"spec failed registry validation"}},"security":[{"bearer":[]}],"summary":"PUT /api/report-definitions/{id} — full-replace update through the\ninstant-first write path (version gate enforced; a miss inside a\nmutation is the typed `rls_sparse` reason, not `not_found`).","tags":["reports"]}},"/api/v1/report-definitions/{id}/run":{"get":{"operationId":"report_definitions_run","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Inclusive window start (UTC day, YYYY-MM-DD). Default: 30 trailing days.","in":"query","name":"from","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Inclusive window end (UTC day). Max span with `from`: 366 days.","in":"query","name":"to","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Page size, clamped to [1, 500].","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}},{"description":"`\"csv\"` streams text/csv instead of the JSON envelope.","in":"query","name":"format","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportPage"}}},"description":"Report page (JSON envelope or CSV stream)"},"404":{"description":"not visible in scope"},"422":{"description":"spec failed registry validation or invalid window"}},"security":[{"bearer":[]}],"summary":"GET /api/report-definitions/{id}/run — compile-on-demand execution of a\nvisible stored definition's spec (R6 builder runs). The spec re-validates\nthrough the registry, compiles via `ReportSpec::compile_windowed`\n(identifiers stay enum-matched catalog constants; values bind as `$N`\nparameters), and executes against the reporting pool inside an\norg-bound transaction. Non-admins are structurally self-scoped exactly\nlike the prebuilt kinds.","tags":["reports"]}},"/api/v1/report-schedules":{"get":{"operationId":"report_schedules_list","parameters":[{"description":"Opaque keyset cursor from a previous page's `next_cursor`.","in":"query","name":"cursor","required":false,"schema":{"type":["string","null"]}},{"description":"Page size, clamped to [1, 200].","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportSchedulePage"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/report-schedules — cursor-paged listing (admin), ordered\n`(name, id)`, each row carrying its next-run preview.","tags":["reports"]},"post":{"operationId":"report_schedules_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateReportScheduleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportScheduleMutationResponse"}}},"description":""},"403":{"description":"admin-only surface"},"409":{"description":"name conflict or idempotency key reused"},"422":{"description":"validation failed (shape, recipients, definition)"}},"security":[{"bearer":[]}],"summary":"POST /api/report-schedules — create a schedule through the\ninstant-first write path; the next-run preview computes from now.","tags":["reports"]}},"/api/v1/report-schedules/{id}":{"delete":{"operationId":"report_schedules_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportScheduleMutationResponse"}}},"description":""},"403":{"description":"admin-only surface"},"404":{"description":"rls_sparse: target not visible"},"409":{"description":"version conflict"}},"security":[{"bearer":[]}],"summary":"DELETE /api/report-schedules/{id} — removal through the same gated\npath; ledger rows cascade and the write trail keeps the snapshot.","tags":["reports"]},"get":{"operationId":"report_schedules_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportScheduleView"}}},"description":""},"404":{"description":"not in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/report-schedules/{id} — one schedule; anything not in this\norg is a bare 404.","tags":["reports"]},"put":{"operationId":"report_schedules_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateReportScheduleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportScheduleMutationResponse"}}},"description":""},"403":{"description":"admin-only surface"},"404":{"description":"rls_sparse: target not visible"},"409":{"description":"name conflict or version conflict"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"PUT /api/report-schedules/{id} — full-replace update; the next-run\npreview recomputes from now (cadence changes cannot strand a slot in\nthe past; the worker's claim sweep catches up enabled schedules\nanyway, so this only affects the preview's freshness).","tags":["reports"]}},"/api/v1/report-schedules/{id}/deliveries":{"get":{"operationId":"report_schedules_deliveries","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Page size, clamped to [1, 200].","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}},{"description":"Row offset for paging.","in":"query","name":"offset","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDeliveryPage"}}},"description":""},"404":{"description":"not in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/report-schedules/{id}/deliveries — the delivery ledger for\none schedule (R4 history surface). Worker-written data: rows are\nappend-only run records, never mutated, newest slot first. A missing\nschedule is a bare 404.","tags":["reports"]}},"/api/v1/reports/run-spec":{"post":{"operationId":"report_run_spec","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunSpecRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportPage"}}},"description":""},"422":{"description":"spec failed registry validation or invalid window"}},"security":[{"bearer":[]}],"summary":"POST /api/reports/run-spec — execute an INLINE spec (R6 builder preview).\nSame compile/execute/RBAC seam as running a stored definition; the spec\nnever touches storage and identifiers re-validate against the static\ncatalog before compilation.","tags":["reports"]}},"/api/v1/reports/{kind}":{"get":{"operationId":"report_get","parameters":[{"description":"Inclusive window start (UTC day, YYYY-MM-DD). Default: 30 trailing days.","in":"query","name":"from","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Inclusive window end (UTC day). Max span with `from`: 366 days.","in":"query","name":"to","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Opaque keyset cursor from a previous page's `next_cursor`.","in":"query","name":"cursor","required":false,"schema":{"type":["string","null"]}},{"description":"Page size, clamped to [1, 500].","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"description":"`\"csv\"` streams text/csv instead of the JSON envelope.","in":"query","name":"format","required":false,"schema":{"type":["string","null"]}},{"description":"Manager-only narrowing onto one staff member; non-managers are\nALWAYS forced onto themselves regardless of this parameter.","in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"SLA kinds: restrict to one governing policy.","in":"query","name":"policy_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Time rollups: filter billable/non-billable entries.","in":"query","name":"billable","required":false,"schema":{"type":["boolean","null"]}},{"description":"ticket_aging only: snapshot instant for ages (default: now).","in":"query","name":"as_of","required":false,"schema":{"format":"date-time","type":["string","null"]}},{"in":"path","name":"kind","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportPage"}}},"description":"Report page (JSON envelope or CSV stream)"}},"security":[{"bearer":[]}],"summary":"GET /api/reports/{kind} — prebuilt operational report over the read-model\nviews. Kinds: time_by_user | time_by_client | time_by_ticket |\nsla_attainment | sla_breaches | ticket_throughput | ticket_aging |\nfirst_response.","tags":["reports"]}},"/api/v1/roles":{"get":{"operationId":"roles_list","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RolesListResponse"}}},"description":""},"403":{"description":"requires manage_users or manage_roles"}},"security":[{"bearer":[]}],"summary":"GET /api/roles — the org's roles plus the canonical preset + portal\ntier rosters (manager+).","tags":["org-users"]}},"/api/v1/roles/{id}":{"put":{"operationId":"roles_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRoleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleMutationResponse"}}},"description":""},"403":{"description":"builtin_role_immutable"},"404":{"description":""},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"PUT /api/roles/{id} — the custom-roles EDITOR (Wave 22): updates a\ncustom row's name and/or permission set. Builtin presets are typed\nrefusals (rb1) — restore is the ONLY preset-matrix mutation.","tags":["org-users"]}},"/api/v1/roles/{id}/fork":{"post":{"operationId":"roles_fork","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForkRoleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleMutationResponse"}}},"description":""},"404":{"description":"role not visible in scope"},"409":{"description":"role name taken or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/roles/{id}/fork — duplicate a role (typically a preset) into\na mutable custom copy on the same matrix. The origin row is untouched.","tags":["org-users"]}},"/api/v1/roles/{id}/restore":{"post":{"operationId":"roles_restore","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestoreRoleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleMutationResponse"}}},"description":""},"404":{"description":"role not visible in scope, or not a preset"},"409":{"description":"version conflict or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/roles/{id}/restore — reset a preset row's permission array to\nthe canonical shipped set (RB1: the presets stay canonical). Custom\nroles and forks are org-owned data and are never restorable.","tags":["org-users"]}},"/api/v1/scan/resolve":{"post":{"operationId":"scan_resolve","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanResolveRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanResolutionView"}}},"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/scan/resolve — resolve scanned text into a typed entity.","tags":["labels"]}},"/api/v1/scheduling-groups":{"get":{"operationId":"scheduling_groups_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SchedulingGroup"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/scheduling-groups — the org's free-form groups (availability\npools; deliberately NOT the org chart).","tags":["scheduling"]},"post":{"operationId":"scheduling_groups_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGroupRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchedulingGroup"}}},"description":""},"409":{"description":"name already taken"}},"security":[{"bearer":[]}],"summary":"POST /api/scheduling-groups — create a pool.","tags":["scheduling"]}},"/api/v1/scheduling-groups/{id}":{"delete":{"operationId":"scheduling_groups_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/scheduling-groups/{id} — removes the pool (memberships go\nwith it; meeting types pointed at it retire with their FK CASCADE,\nmirroring Calendly's delete semantics).","tags":["scheduling"]},"get":{"operationId":"scheduling_groups_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchedulingGroup"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/scheduling-groups/{id}","tags":["scheduling"]},"patch":{"operationId":"scheduling_groups_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGroupRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupMutation"}}},"description":""},"404":{"description":""},"409":{"description":"version conflict or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"PATCH /api/scheduling-groups/{id}","tags":["scheduling"]}},"/api/v1/scheduling-groups/{id}/members":{"post":{"operationId":"scheduling_groups_add_member","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMemberRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchedulingGroup"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/scheduling-groups/{id}/members — add a calendar to the pool.","tags":["scheduling"]}},"/api/v1/scheduling-groups/{id}/members/{user_id}":{"delete":{"operationId":"scheduling_groups_remove_member","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"user_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchedulingGroup"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/scheduling-groups/{id}/members/{user_id}","tags":["scheduling"]}},"/api/v1/scheduling-links":{"get":{"operationId":"scheduling_links_list","parameters":[{"in":"query","name":"ticket_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SchedulingLink"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/scheduling-links — newest-first (optionally per ticket) so\nthe tech sees the created / viewed / scheduled / expired lifecycle.","tags":["scheduling"]},"post":{"operationId":"scheduling_links_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSchedulingLinkRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedLink"}}},"description":""},"404":{"description":"meeting type not found"}},"security":[{"bearer":[]}],"summary":"POST /api/scheduling-links — mint a shareable link (token returned\nonce). A replay (same key + same body) replays the SAME token.","tags":["scheduling"]}},"/api/v1/scheduling-links/{id}/bookings":{"get":{"operationId":"scheduling_links_bookings","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SchedulingLinkBooking"},"type":"array"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/scheduling-links/{id}/bookings — the link → booking trail.","tags":["scheduling"]}},"/api/v1/scheduling-links/{id}/revoke":{"post":{"operationId":"scheduling_links_revoke","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchedulingLink"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/scheduling-links/{id}/revoke — the link can never book\nagain; an already-used link keeps its `used` state so the booking\ntrail never changes meaning.","tags":["scheduling"]}},"/api/v1/scheduling/conflict-check":{"post":{"operationId":"scheduling_conflict_check","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictCheckRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictCheckResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/scheduling/conflict-check — dry-run overlap probe that writes\nnothing: returns every blocking booking for the candidate slot.","tags":["scheduling"]}},"/api/v1/scorecards":{"get":{"operationId":"scorecard_list_team","parameters":[{"description":"Inclusive window start (UTC day, YYYY-MM-DD). Default: 30 trailing days.","in":"query","name":"from","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Inclusive window end (UTC day). Max span with `from`: 366 days.","in":"query","name":"to","required":false,"schema":{"format":"date","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamScorecardsResponse"}}},"description":""},"403":{"description":"team scorecards are owner/admin only"},"422":{"description":"invalid window"}},"security":[{"bearer":[]}],"summary":"GET /api/scorecards?from&to — every active staff member's scorecard in\none org-wide read (owner/admin only; R5 private-by-default means a\nplain tech never sees anyone else's card here, including via query\ntricks — the scope is derived from the principal, never the request).","tags":["reports"]}},"/api/v1/scorecards/clients":{"get":{"operationId":"scorecard_client_rollups","parameters":[{"description":"Inclusive window start (UTC day, YYYY-MM-DD). Default: 30 trailing days.","in":"query","name":"from","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Inclusive window end (UTC day). Max span with `from`: 366 days.","in":"query","name":"to","required":false,"schema":{"format":"date","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientCsatRollupResponse"}}},"description":""},"403":{"description":"missing reporting:view"},"422":{"description":"invalid window"}},"security":[{"bearer":[]}],"summary":"GET /api/scorecards/clients?from&to — per-client CSAT rollups over the\nwindow (owner/admin only). `available: false` until the org records its\nfirst survey, matching the per-tech section's honesty gate.","tags":["reports"]}},"/api/v1/scorecards/me":{"get":{"operationId":"scorecard_get_own","parameters":[{"description":"Inclusive window start (UTC day, YYYY-MM-DD). Default: 30 trailing days.","in":"query","name":"from","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Inclusive window end (UTC day). Max span with `from`: 366 days.","in":"query","name":"to","required":false,"schema":{"format":"date","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScorecardResponse"}}},"description":""},"403":{"description":"missing reporting:view"},"422":{"description":"invalid window"}},"security":[{"bearer":[]}],"summary":"GET /api/scorecards/me?from&to — the caller's own scorecard.","tags":["reports"]}},"/api/v1/scorecards/me/settings":{"get":{"operationId":"scorecard_get_settings","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScorecardSettingsResponse"}}},"description":""},"403":{"description":"missing reporting:view"}},"security":[{"bearer":[]}],"summary":"GET /api/scorecards/me/settings — the caller's scorecard settings\n(absent row reads as the private-by-defaults).","tags":["reports"]},"patch":{"operationId":"scorecard_update_settings","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateScorecardSettingsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScorecardSettingsResponse"}}},"description":""},"403":{"description":"missing reporting:view"}},"security":[{"bearer":[]}],"summary":"PATCH /api/scorecards/me/settings — flip the R5 opt-in gamification\ntoggle. Only ever touches the CALLER's row (no user_id in the body by\ndesign); emits `scorecard.settings_updated` in the same transaction.","tags":["reports"]}},"/api/v1/scorecards/{user_id}":{"get":{"operationId":"scorecard_get_user","parameters":[{"description":"Inclusive window start (UTC day, YYYY-MM-DD). Default: 30 trailing days.","in":"query","name":"from","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Inclusive window end (UTC day). Max span with `from`: 366 days.","in":"query","name":"to","required":false,"schema":{"format":"date","type":["string","null"]}},{"in":"path","name":"user_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScorecardResponse"}}},"description":""},"403":{"description":"other members' scorecards are owner/admin only"},"404":{"description":"member not in scope (rls_sparse)"},"422":{"description":"invalid window"}},"security":[{"bearer":[]}],"summary":"GET /api/scorecards/{user_id}?from&to — one member's scorecard; a\nmember may read their own, owner/admin may read anyone's.","tags":["reports"]}},"/api/v1/search":{"get":{"operationId":"search_query","parameters":[{"description":"Free-text query using websearch syntax: `\"exact phrases\"`,\n`-excluded`, plain terms AND-ed. Tokens ≥3 chars also match as\nsubstrings via the trigram fallback.","in":"query","name":"q","required":true,"schema":{"type":"string"}},{"description":"Optional client scope for the `document` kind (0089, per-client\ndocument scoping): when set, indexed documents answer only when\norg-global (client_scope NULL) or scoped to THIS client. Every\nother kind is untouched.","in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"in":"query","name":"offset","required":false,"schema":{"format":"int64","type":"integer"}},{"description":"Opaque keyset cursor from the previous page's `next_cursor`\n(docs/perf.md F2). When present the walk resumes strictly after the\nanchor row and `offset` is ignored — the two mechanisms answer\ndifferent callers, and the cursor is the one that stays at page-1\ncost as depth grows.","in":"query","name":"cursor","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/search — unified staff search across tickets, comments and the\nclient hierarchy.","tags":["search"]}},"/api/v1/search/pins":{"get":{"operationId":"search_pins_list","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchPinList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/search/pins — the caller's own pins, newest-first.","tags":["search"]},"post":{"operationId":"search_pins_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSearchPinRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchPinView"}}},"description":""},"403":{"description":"no permission for that kind"},"404":{"description":"that row is not in your org's search index"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/search/pins — pins one stored result. The entity must EXIST\nin the index (a pin is a stored result, never invented) and kind sits\nbehind the same permission gate the query ran behind. One pin per\n(kind, id): re-pinning is idempotent AND refreshes the stored\nsnapshot (query + title + parent) to current — the created_at stays\nthe original pin time (Wave 23 lane 1, item 6).","tags":["search"]}},"/api/v1/search/pins/{id}":{"delete":{"operationId":"search_pins_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":""},"404":{"description":"pin not visible to this user"}},"security":[{"bearer":[]}],"summary":"DELETE /api/search/pins/{id} — unpins the caller's OWN pin; another\nuser's pin is simply not theirs (typed 404, never a conflict leak).","tags":["search"]}},"/api/v1/secrets/folders":{"get":{"operationId":"secrets_folders_list","parameters":[{"description":"Client-scope tree; omitted = org-scope tree.","in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SecretFolder"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/secrets/folders — metadata-only category tree.","tags":["secrets"]},"post":{"operationId":"secrets_folders_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSecretFolderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretFolder"}}},"description":""},"409":{"description":"slug already exists in this scope"},"422":{"description":"validation error"}},"security":[{"bearer":[]}],"tags":["secrets"]}},"/api/v1/secrets/folders/{id}":{"delete":{"operationId":"secrets_folders_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"deleted"},"404":{"description":"folder not visible in scope"},"409":{"description":"seed refusal or non-empty subtree"}},"security":[{"bearer":[]}],"tags":["secrets"]},"patch":{"operationId":"secrets_folders_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSecretFolderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretFolder"}}},"description":""},"404":{"description":"folder not visible in scope"},"409":{"description":"the shipped default is immutable; fork instead"}},"security":[{"bearer":[]}],"tags":["secrets"]}},"/api/v1/secrets/folders/{id}/fork":{"post":{"operationId":"secrets_folders_fork","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForkSecretFolderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SecretFolder"},"type":"array"}}},"description":""},"404":{"description":"source folder or client not visible in scope"},"409":{"description":"a fork of that slug already exists"}},"security":[{"bearer":[]}],"summary":"POST /api/secrets/folders/{id}/fork — fork-the-seed customization\n(a NEW row graph in the client's scope, never edit-in-place, ADR 0004).","tags":["secrets"]}},"/api/v1/secrets/grants":{"delete":{"operationId":"secrets_grants_delete","parameters":[{"in":"query","name":"role_id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"folder_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"deleted"},"403":{"description":"grant administration is Owner/Admin only"},"404":{"description":"grant not found"}},"security":[{"bearer":[]}],"summary":"DELETE /api/secrets/grants?role_id=&folder_id= — revoke one grant.","tags":["secrets"]},"get":{"operationId":"secrets_grants_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SecretCategoryGrant"},"type":"array"}}},"description":""},"403":{"description":"grant administration is Owner/Admin only"}},"security":[{"bearer":[]}],"summary":"GET /api/secrets/grants — the grant matrix reads for admins (Owner\nor Admin only; grants are policy data, not vault contents).","tags":["secrets"]},"put":{"operationId":"secrets_grants_set","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetSecretGrantRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretCategoryGrant"}}},"description":""},"403":{"description":"grant administration is Owner/Admin only"},"404":{"description":"folder or role not visible in scope"},"422":{"description":"validation error"}},"security":[{"bearer":[]}],"summary":"PUT /api/secrets/grants — upserts ONE role × folder grant row\n(can_edit implies read). Owner/Admin only.","tags":["secrets"]}},"/api/v1/secrets/records":{"get":{"operationId":"secrets_records_list","parameters":[{"description":"Client-scope records; omitted = org-scope.","in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"folder_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SecretRecord"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/secrets/records — metadata-only vault rows.","tags":["secrets"]},"post":{"operationId":"secrets_records_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSecretRecordRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretRecordMutationResponse"}}},"description":""},"403":{"description":"restricted category gate denied"},"409":{"description":"title already exists in this folder / idempotency key reused"},"422":{"description":"validation error"}},"security":[{"bearer":[]}],"summary":"POST /api/secrets/records — seals fields with coordinate AAD;\ninstant-first (IF1–IF3) when an idempotency key rides the request.","tags":["secrets"]}},"/api/v1/secrets/records/bulk":{"post":{"operationId":"secrets_records_bulk","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkSecretIngestRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkSecretIngestResponse"}}},"description":""},"403":{"description":"restricted category gate denied"},"422":{"description":"chunk over cap / validation error"},"429":{"description":"org write budget exhausted"}},"security":[{"bearer":[]}],"summary":"POST /api/secrets/records/bulk — the chunked worker path.","tags":["secrets"]}},"/api/v1/secrets/records/{id}":{"delete":{"operationId":"secrets_records_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"deleted"},"403":{"description":"restricted category gate denied"},"404":{"description":"record not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/secrets/records/{id} — records retire; their fields\ncascade; the audit keeps the trail.","tags":["secrets"]},"get":{"operationId":"secrets_records_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretRecord"}}},"description":""},"404":{"description":"record not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/secrets/records/{id} — metadata only.","tags":["secrets"]},"patch":{"operationId":"secrets_records_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSecretRecordRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretRecordMutationResponse"}}},"description":""},"403":{"description":"restricted category gate denied"},"404":{"description":"record not visible in scope"},"409":{"description":"title collision / version conflict / idempotency key reused"},"422":{"description":"validation error"}},"security":[{"bearer":[]}],"summary":"PATCH /api/secrets/records/{id} — metadata + field rotation/clear\n(`value: null` clears — A5); instant-first envelope (IF1–IF3).","tags":["secrets"]}},"/api/v1/secrets/records/{id}/file":{"get":{"operationId":"secrets_records_get_file","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/octet-stream":{}},"description":""},"403":{"description":"restricted category gate denied"},"404":{"description":"record or object not found"},"409":{"description":"record has no stored file"}},"security":[{"bearer":[]}],"summary":"GET /api/secrets/records/{id}/file — the reveal-file path (document\nkind ONLY): requires `secrets:edit` + the category gate (the same\nposture as field reveal), decrypts the object-store ciphertext, and\nemits `secret.revealed` in-commit with the read. The response is the\nsole carrier of the plaintext bytes.","tags":["secrets"]},"put":{"operationId":"secrets_records_put_file","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"type":"string"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretRecordMutationResponse"}}},"description":""},"403":{"description":"restricted category gate denied"},"404":{"description":"record not visible in scope"},"422":{"description":"record is not document-kind / body over cap"}},"security":[{"bearer":[]}],"summary":"PUT /api/secrets/records/{id}/file — document-kind ONLY. Seals the\nuploaded bytes (AAD-bound to the record coordinate), PUTs the\nciphertext to the object store behind the core-store S3/local seam,\nand pins the pointer + hash + byte length on the record in the same\ntx (event emitted in-commit).","tags":["secrets"]}},"/api/v1/secrets/records/{id}/links":{"get":{"operationId":"secrets_records_list_links","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ResolvedLink"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/secrets/records/{id}/links","tags":["secrets"]},"post":{"operationId":"secrets_records_add_link","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ResolvedLink"},"type":"array"}}},"description":""},"403":{"description":"restricted category gate denied"},"404":{"description":"secret record or link target not visible in scope"}},"security":[{"bearer":[]}],"summary":"POST /api/secrets/records/{id}/links — typed labels via the seam;\nbroken targets ride the asset-docs semantic (never cascade-deleted).","tags":["secrets"]}},"/api/v1/secrets/records/{id}/reveal":{"post":{"operationId":"secrets_records_reveal","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretReveal"}}},"description":""},"403":{"description":"reveal requires secrets:edit (and a category grant on restricted chains)"},"404":{"description":"record not visible in scope"}},"security":[{"bearer":[]}],"summary":"POST /api/secrets/records/{id}/reveal — the ONLY plaintext path,\nmirroring `/passwords/{id}/reveal`: requires `secrets:edit`, runs\ninside the org tx so the reveal event commits atomically with the\nread, decrypts with coordinate AAD first (falling back to the legacy\nseal for rows the deterministic importer forwarded) and the response\nis the sole carrier of the decrypted fields.","tags":["secrets"]}},"/api/v1/secrets/records/{id}/share-links":{"get":{"operationId":"secrets_records_list_share_links","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SecretShareLink"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/secrets/records/{id}/share-links — the lifecycle ledger\n(metadata only; NEVER the tokens).","tags":["secrets"]},"post":{"operationId":"secrets_records_create_share_link","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSecretShareLinkRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretShareLinkIssued"}}},"description":""},"403":{"description":"category locked to your role"},"404":{"description":"record not visible in scope"},"422":{"description":"expiry/label rejected"}},"security":[{"bearer":[]}],"summary":"POST /api/secrets/records/{id}/share-links — mint a one-time share\nlink; the token is returned ONCE (re-issuing is the flow for mistakes).","tags":["secrets"]}},"/api/v1/secrets/records/{id}/share-links/{share_id}":{"delete":{"operationId":"secrets_records_delete_share_link","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"share_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/secrets/records/{id}/share-links/{share_id} — revoke a\nnot-yet-consumed link; consumed links are immutable history.","tags":["secrets"]}},"/api/v1/secrets/share-links/claim":{"post":{"operationId":"secrets_share_links_claim","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimShareLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareClaimMaterial"}}},"description":""},"403":{"description":"subject not revealable in this scope"},"404":{"description":"unknown token / foreign org / expired"},"409":{"description":"already claimed once / revoked"}},"security":[{"bearer":[]}],"summary":"POST /api/secrets/share-links/claim — consume a link exactly once,\nunseal its subject behind the org + permission + category gates, and\nstamp the audit trail + `secret.share_link_claimed` event.","tags":["secrets"]}},"/api/v1/settings/ai/byok":{"put":{"operationId":"settings_ai_byok_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutByokSecretRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ByokSecretMutationResponse"}}},"description":""},"403":{"description":""},"409":{"description":"idempotency key reused or version conflict"},"422":{"description":"invalid provider or key"}},"security":[{"bearer":[]}],"summary":"PUT /api/settings/ai/byok — seals and stores an API key at rest\n(Admin:edit + owner/admin standing). Rides the instant-first write\npath; replays are byte-identical and the trail stores presence flags\nonly.","tags":["settings"]}},"/api/v1/settings/ai/byok/{provider}":{"delete":{"operationId":"settings_ai_byok_delete","parameters":[{"in":"path","name":"provider","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ByokSecretMutationResponse"}}},"description":""},"403":{"description":""},"404":{"description":"rls_sparse: no key stored for that provider"}},"security":[{"bearer":[]}],"summary":"DELETE /api/settings/ai/byok/{provider} — removes a sealed BYOK key\nthrough the same gated path; the write trail keeps the presence\nsnapshot.","tags":["settings"]}},"/api/v1/settings/ai/caps":{"put":{"operationId":"settings_ai_cap_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutAiCapRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiCapMutationResponse"}}},"description":""},"403":{"description":""},"422":{"description":"cap validation"}},"security":[{"bearer":[]}],"summary":"PUT /settings/ai/caps — upsert org-wide or per-user soft/hard caps (audited).","tags":["settings"]}},"/api/v1/settings/ai/caps/{cap_id}":{"delete":{"operationId":"settings_ai_cap_delete","parameters":[{"in":"path","name":"cap_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiCapMutationResponse"}}},"description":""},"403":{"description":""},"404":{"description":"rls_sparse"}},"security":[{"bearer":[]}],"summary":"DELETE /settings/ai/caps/{cap_id}.","tags":["settings"]}},"/api/v1/settings/ai/catalog":{"put":{"operationId":"settings_ai_catalog_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutAiCatalogRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiCatalogMutationResponse"}}},"description":""},"403":{"description":""},"409":{"description":"duplicate (provider, model) pair"}},"security":[{"bearer":[]}],"summary":"PUT /settings/ai/catalog — upsert one curated (provider, model) pair.","tags":["settings"]}},"/api/v1/settings/ai/catalog/{entry_id}":{"delete":{"operationId":"settings_ai_catalog_delete","parameters":[{"in":"path","name":"entry_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiCatalogMutationResponse"}}},"description":""},"403":{"description":""},"404":{"description":"rls_sparse"}},"security":[{"bearer":[]}],"summary":"DELETE /settings/ai/catalog/{entry_id}.","tags":["settings"]}},"/api/v1/settings/ai/credentials":{"get":{"operationId":"settings_ai_credentials_get","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AiCredentialDto"},"type":"array"}}},"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /settings/ai/credentials — the org's credential summaries (admin).","tags":["settings"]},"put":{"operationId":"settings_ai_credential_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutAiCredentialRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiCredentialMutationResponse"}}},"description":""},"403":{"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"PUT /settings/ai/credentials — seal an ORG credential (admin).","tags":["settings"]}},"/api/v1/settings/ai/credentials/{credential_id}":{"delete":{"operationId":"settings_ai_credential_delete","parameters":[{"in":"path","name":"credential_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiCredentialMutationResponse"}}},"description":""},"403":{"description":""},"404":{"description":"rls_sparse"}},"security":[{"bearer":[]}],"summary":"DELETE /settings/ai/credentials/{credential_id} — revoke an ORG credential.","tags":["settings"]}},"/api/v1/settings/ai/decision":{"get":{"operationId":"settings_ai_decision_overview","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionOverviewResponse"}}},"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"tags":["settings"]}},"/api/v1/settings/ai/decision/features":{"put":{"operationId":"settings_ai_decision_features_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutDecisionFeaturesRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionFeatureMutationResponse"}}},"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"tags":["settings"]}},"/api/v1/settings/ai/decision/{surface_key}/config":{"put":{"operationId":"settings_ai_decision_config_put","parameters":[{"in":"path","name":"surface_key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutDecisionConfigRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionConfigMutationResponse"}}},"description":""},"403":{"description":""},"422":{"description":"unknown surface, or the config rode a typed refusal (reason rides verbatim)"}},"security":[{"bearer":[]}],"tags":["settings"]}},"/api/v1/settings/ai/decision/{surface_key}/preview":{"post":{"operationId":"settings_ai_decision_preview","parameters":[{"in":"path","name":"surface_key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionPreviewRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"tags":["settings"]}},"/api/v1/settings/ai/decision/{surface_key}/runs":{"get":{"operationId":"settings_ai_decision_runs","parameters":[{"in":"path","name":"surface_key","required":true,"schema":{"type":"string"}},{"description":"Window in days, 1..=90. Default 7 — the log's own window.","in":"query","name":"days","required":false,"schema":{"format":"int32","type":["integer","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionRunsResponse"}}},"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/settings/ai/decision/{surface_key}/runs — the shadow run log.","tags":["settings"]}},"/api/v1/settings/ai/grants":{"put":{"operationId":"settings_ai_grant_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutAiGrantRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiGrantMutationResponse"}}},"description":""},"403":{"description":""},"422":{"description":"unknown surface or grantee validation"}},"security":[{"bearer":[]}],"summary":"PUT /settings/ai/grants — upsert one per-surface grant (audited).","tags":["settings"]}},"/api/v1/settings/ai/grants/{grant_id}":{"delete":{"operationId":"settings_ai_grant_delete","parameters":[{"in":"path","name":"grant_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiGrantMutationResponse"}}},"description":""},"403":{"description":""},"404":{"description":"rls_sparse"}},"security":[{"bearer":[]}],"summary":"DELETE /settings/ai/grants/{grant_id} — revoke one grant (audited).","tags":["settings"]}},"/api/v1/settings/ai/matrix":{"get":{"operationId":"settings_ai_matrix_get","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiMatrixResponse"}}},"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /settings/ai/matrix — the full admin view.","tags":["settings"]}},"/api/v1/settings/ai/model-picks/{surface_key}":{"put":{"operationId":"settings_ai_org_pick_put","parameters":[{"in":"path","name":"surface_key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutAiPickRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiPickMutationResponse"}}},"description":""},"403":{"description":""},"404":{"description":"rls_sparse"},"422":{"description":"model not in the admin allowlist"}},"security":[{"bearer":[]}],"summary":"PUT /settings/ai/model-picks/{surface_key} — the ORG default pick\n(owner/admin standing; stored as user_id NULL so every member's\nresolution falls back onto it when no personal pick holds).","tags":["settings"]}},"/api/v1/settings/ai/plugin-surfaces":{"get":{"operationId":"settings_ai_plugin_surfaces_get","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiPluginSurfacesResponse"}}},"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"summary":"Org-scoped plugin surfaces registered by THIS org (kind = 'plugin'),\nfor the admin plugin-surface management view. Globally seeded\nnative/internal keys are administrable through the matrix, not here.","tags":["settings"]},"put":{"operationId":"settings_ai_plugin_surface_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutAiPluginSurfaceRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiPluginSurfaceMutationResponse"}}},"description":""},"403":{"description":""},"422":{"description":"surface key validation"}},"security":[{"bearer":[]}],"summary":"PUT /settings/ai/plugin-surfaces — register (or update) one org-scoped\nplugin surface (audited, instant-first write path). The registered key\nis deny-by-default until the grant flow covers it.","tags":["settings"]}},"/api/v1/settings/ai/plugin-surfaces/{surface_id}/disable":{"post":{"operationId":"settings_ai_plugin_surface_disable","parameters":[{"in":"path","name":"surface_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiSurfaceDto"}}},"description":""},"403":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /settings/ai/plugin-surfaces/{surface_id}/disable.","tags":["settings"]}},"/api/v1/settings/ai/plugin-surfaces/{surface_id}/enable":{"post":{"operationId":"settings_ai_plugin_surface_enable","parameters":[{"in":"path","name":"surface_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiSurfaceDto"}}},"description":""},"403":{"description":""},"404":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /settings/ai/plugin-surfaces/{surface_id}/enable.","tags":["settings"]}},"/api/v1/settings/ai/status":{"get":{"operationId":"settings_ai_status","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ByokOverviewResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/settings/ai/status — overview of AI completion provider & configured keys.","tags":["settings"]}},"/api/v1/settings/ai/test":{"post":{"operationId":"settings_ai_test","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestConnectionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestConnectionResponse"}}},"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/settings/ai/test — test-connection probe (Admin:edit gated).\nNone mode returns the typed unavailable outcome; a configured provider\nverifies its sealed key still decrypts (tamper/rotation check). No\nvendor HTTP call happens in v1 — the live probe rides the edge adapters\nwhen a lane wires them.","tags":["settings"]}},"/api/v1/settings/ai/usage/org":{"get":{"operationId":"settings_ai_usage_org_get","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiOrgUsageResponse"}}},"description":""},"403":{"description":""}},"security":[{"bearer":[]}],"summary":"GET /settings/ai/usage/org — org progress roll-up (admin).","tags":["settings"]}},"/api/v1/settings/org":{"get":{"operationId":"settings_org_get","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ValueView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/settings/org — effective org settings (defaults overlaid by\nstored overrides). Visible to every member.","tags":["settings"]},"put":{"operationId":"settings_org_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSettingRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValueView"}}},"description":""},"403":{"description":""},"422":{"description":"unknown key or invalid value"}},"security":[{"bearer":[]}],"summary":"PUT /api/settings/org — upserts one org-scope override after registry\nvalidation (Admin:edit required). Emits `setting.updated` + audit row.","tags":["settings"]}},"/api/v1/settings/registry":{"get":{"operationId":"settings_registry","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegistryResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/settings/registry — every declared setting with kind, default,\nscope and validation shape. Any authenticated member may read it.","tags":["settings"]}},"/api/v1/settings/self-host":{"get":{"operationId":"settings_self_host_get","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelfHostView"}}},"description":""},"403":{"description":"requires an org owner or admin"}},"security":[{"bearer":[]}],"summary":"GET /api/v1/settings/self-host — the self-hoster visibility payload.","tags":["settings"]}},"/api/v1/settings/user":{"get":{"operationId":"settings_user_get","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ValueView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/settings/user — the caller's effective personal settings.","tags":["settings"]},"put":{"operationId":"settings_user_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSettingRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValueView"}}},"description":""},"422":{"description":"unknown key or invalid value"}},"security":[{"bearer":[]}],"summary":"PUT /api/settings/user — upserts one of the CALLER's own user-scope\noverrides. No role requirement: everyone may tune their own preferences.","tags":["settings"]}},"/api/v1/settings/user/email-envelope":{"get":{"operationId":"settings_user_email_envelope","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailEnvelopeView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/settings/user/email-envelope — the caller's resolved envelope,\nfor the signature editor's live preview and the composer's prefill.","tags":["settings"]}},"/api/v1/sign-offs/{id}/payload":{"get":{"operationId":"sign_offs_payload","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"image/png":{}},"description":"the captured PNG bytes"},"401":{"description":"missing or invalid credentials"},"403":{"description":"not permitted on this board"},"404":{"description":"sign-off not found"}},"security":[{"bearer":[]}],"summary":"GET /api/sign-offs/{id}/payload — streams the mark's bytes. Staff-only and\naudit-free by intent: reading evidence is not the same act as capturing\nit, and the row's own trail is the record of decisions.","tags":["sign_offs"]}},"/api/v1/sla/calendars":{"get":{"operationId":"sla_calendars_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SlaCalendar"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/sla/calendars","tags":["sla"]},"post":{"operationId":"sla_calendars_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCalendarRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlaCalendar"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/sla/calendars","tags":["sla"]}},"/api/v1/sla/calendars/{id}":{"delete":{"operationId":"sla_calendars_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/sla/calendars/{id} — detaches policies (FK SET NULL); their\nembedded calendars keep governing.","tags":["sla"]},"patch":{"operationId":"sla_calendars_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCalendarRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlaCalendar"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /api/sla/calendars/{id} — partial update; validates the merged\nresult by re-reading and running domain validation.","tags":["sla"]}},"/api/v1/sla/policies":{"get":{"operationId":"sla_policies_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PolicyView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/sla/policies","tags":["sla"]}},"/api/v1/sla/policies/{id}/ladder":{"get":{"operationId":"sla_ladder_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/EscalationStep"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/sla/policies/{id}/ladder — enabled + disabled steps, ordered.","tags":["sla"]},"put":{"operationId":"sla_ladder_replace","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplaceLadderRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/EscalationStep"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PUT /api/sla/policies/{id}/ladder — replaces the whole ladder in one tx.","tags":["sla"]}},"/api/v1/sso/exchange":{"post":{"operationId":"sso_exchange","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SsoExchangeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SsoExchangeResponse"}}},"description":""},"401":{"description":""}},"summary":"POST /api/auth/sso/exchange — swaps the single-use callback code for\ntokens (or a link confirmation). Codes die on first use or after 60s.","tags":["auth"]}},"/api/v1/stock-items":{"get":{"operationId":"stock_items_list","parameters":[{"description":"`None` = all items (active + archived).","in":"query","name":"active","required":false,"schema":{"type":["boolean","null"]}},{"in":"query","name":"search","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/StockItemView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/stock-items.","tags":["commercial"]},"post":{"operationId":"stock_items_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateStockItemRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockItemMutationResponse"}}},"description":""},"409":{"description":"SKU already exists or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/stock-items — create through the instant-first write path.","tags":["commercial"]}},"/api/v1/stock-items/{id}":{"get":{"operationId":"stock_items_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockItemView"}}},"description":""},"404":{"description":"not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/stock-items/{id}.","tags":["commercial"]},"patch":{"operationId":"stock_items_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStockItemRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockItemMutationResponse"}}},"description":""},"404":{"description":"not visible in scope"},"409":{"description":"SKU already exists, version conflict, or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"PATCH /api/stock-items/{id} — absent fields keep their value.","tags":["commercial"]}},"/api/v1/stock-items/{id}/archive":{"post":{"operationId":"stock_items_archive","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArchiveStockItemRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockItemMutationResponse"}}},"description":""},"404":{"description":"not visible in scope"}},"security":[{"bearer":[]}],"summary":"POST /api/stock-items/{id}/archive — archival is a flag.","tags":["commercial"]}},"/api/v1/stock-items/{id}/movements":{"get":{"operationId":"stock_item_movements","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/StockMovementView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/stock-items/{id}/movements — THE traceability ledger read.","tags":["commercial"]}},"/api/v1/stock-items/{id}/serials":{"get":{"operationId":"stock_item_serials","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"location_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"status","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/StockSerialView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/stock-items/{id}/serials — serialized WHERE-IT-IS-NOW.","tags":["commercial"]}},"/api/v1/stock-locations":{"get":{"operationId":"stock_locations_list","parameters":[{"in":"query","name":"kind","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"active_only","required":false,"schema":{"type":["boolean","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/StockLocationView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/stock-locations.","tags":["commercial"]},"post":{"operationId":"stock_locations_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateStockLocationRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockLocationMutationResponse"}}},"description":""},"409":{"description":"idempotency key reused or name conflicts"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/stock-locations.","tags":["commercial"]}},"/api/v1/stock-locations/{id}":{"get":{"operationId":"stock_locations_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockLocationView"}}},"description":""},"404":{"description":"not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/stock-locations/{id}.","tags":["commercial"]},"patch":{"operationId":"stock_locations_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStockLocationRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockLocationMutationResponse"}}},"description":""},"404":{"description":"not visible in scope"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"PATCH /api/stock-locations/{id}.","tags":["commercial"]}},"/api/v1/stock-serials":{"get":{"operationId":"stock_serials_whereabouts","parameters":[{"in":"query","name":"item_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"location_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"in_stock | out | checked_out | loaned.","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"holder_user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"`true` = tools only; `false` = devices only; absent = both.","in":"query","name":"tool_only","required":false,"schema":{"type":["boolean","null"]}},{"description":"Only units past their checkout due date or loan expected return.","in":"query","name":"overdue_only","required":false,"schema":{"type":["boolean","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ToolSerialWhereaboutsView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/stock-serials — WHERE IS IT NOW, per serialized unit (tools\nand loaned devices; each row carries its live holder or client).","tags":["commercial"]}},"/api/v1/stock/adjust":{"post":{"operationId":"stock_adjust","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdjustRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MovementMutationResponse"}}},"description":""},"409":{"description":"insufficient stock or serialized item"},"422":{"description":"validation failure (delta must be non-zero)"}},"security":[{"bearer":[]}],"summary":"POST /api/stock/adjust — the signed counter-fix (NO serialized items;\nmove the unit instead).","tags":["commercial"]}},"/api/v1/stock/consume":{"post":{"operationId":"stock_consume","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsumeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MovementMutationResponse"}}},"description":""},"409":{"description":"insufficient stock or invalid movement"},"422":{"description":"validation failure (qty_milli must be positive)"}},"security":[{"bearer":[]}],"summary":"POST /api/stock/consume — the OUT leg charging a ticket's job.","tags":["commercial"]}},"/api/v1/stock/consumption-velocity":{"get":{"operationId":"stock_consumption_velocity","parameters":[{"description":"Lookback window in days (default 30; outbound legs summed).","in":"query","name":"window_days","required":false,"schema":{"format":"int64","type":["integer","null"]}},{"in":"query","name":"item_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/VelocityMutationless"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/stock/consumption-velocity — the outbound-rate read model.","tags":["commercial"]}},"/api/v1/stock/levels/thresholds":{"post":{"operationId":"stock_thresholds_set","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThresholdsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockLevelView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/stock/levels/thresholds — min/max/reorder per (item,\nlocation); NULL clears. Creates the level row on first touch, without\nmoving stock.","tags":["commercial"]}},"/api/v1/stock/on-hand":{"get":{"operationId":"stock_on_hand","parameters":[{"in":"query","name":"item_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"location_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"true → only rows at/below the reorder point.","in":"query","name":"low_only","required":false,"schema":{"type":["boolean","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/OnHandRow"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/stock/on-hand — the stock-on-hand read model.","tags":["commercial"]}},"/api/v1/stock/receive":{"post":{"operationId":"stock_receive","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiveRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MovementMutationResponse"}}},"description":""},"404":{"description":"item or location not visible in scope"},"409":{"description":"idempotency key reused, item archived, or invalid movement"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/stock/receive — a positive leg into ONE location. Serialized\nitems move one physical unit per ledger row (exactly one serial).","tags":["commercial"]}},"/api/v1/stock/ship-out":{"post":{"operationId":"stock_ship_out","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShipOutRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MovementMutationResponse"}}},"description":""},"409":{"description":"insufficient stock or invalid movement"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/stock/ship-out — OUT to a client or an on-site job, with\noptional carrier + tracking ref.","tags":["commercial"]}},"/api/v1/stock/transfer":{"post":{"operationId":"stock_transfer","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransferRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MovementMutationResponse"}}},"description":""},"409":{"description":"insufficient stock or invalid movement"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/stock/transfer — shrink one location, grow another.","tags":["commercial"]}},"/api/v1/tags":{"get":{"operationId":"tags_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TagView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tags — the org's tag taxonomy with application counts.","tags":["tags"]},"post":{"operationId":"tags_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTagRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagMutationResponse"}}},"description":""},"409":{"description":"duplicate tag name or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/tags — create a tag.","tags":["tags"]}},"/api/v1/tags/{id}":{"delete":{"operationId":"tags_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"tag not found"}},"security":[{"bearer":[]}],"summary":"DELETE /api/tags/{id} — removes the tag and every ticket application.","tags":["tags"]},"patch":{"operationId":"tags_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTagRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagMutationResponse"}}},"description":""},"404":{"description":"tag not found"},"409":{"description":"version conflict or duplicate name"}},"security":[{"bearer":[]}],"summary":"PATCH /api/tags/{id} — rename / recolor.","tags":["tags"]}},"/api/v1/taxonomy/categories":{"get":{"operationId":"taxonomy_list_categories","parameters":[{"description":"Include retired nodes (admin editors + the manifest want them;\npickers default to false).","in":"query","name":"include_retired","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TaxonomyNodeView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/taxonomy/categories — the two-level category tree, flat and\nordered (roots and subcategories interleaved by sort order; `parent_id`\nreconstructs the tree).","tags":["taxonomy"]},"post":{"operationId":"taxonomy_create_category","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaxonomyNodeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxonomyMutationResponse"}}},"description":""},"404":{"description":"parent category not found"},"409":{"description":"duplicate sibling name or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/taxonomy/categories — create a root category, or a\nsubcategory when `parent_id` names a root of this org.","tags":["taxonomy"]}},"/api/v1/taxonomy/categories/{id}":{"patch":{"operationId":"taxonomy_update_category","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTaxonomyNodeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxonomyMutationResponse"}}},"description":""},"404":{"description":"node not visible in scope (rls_sparse)"},"409":{"description":"duplicate sibling name, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"PATCH /api/taxonomy/categories/{id} — rename / edit description /\nre-sort (root or subcategory).","tags":["taxonomy"]}},"/api/v1/taxonomy/categories/{id}/reparent":{"post":{"operationId":"taxonomy_reparent_category","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReparentCategoryRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxonomyMutationResponse"}}},"description":""},"404":{"description":"node or new parent not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/taxonomy/categories/{id}/reparent — move a SUBCATEGORY under\na different root. Roots cannot be reparented (that would grow a third\nlevel or orphan children).","tags":["taxonomy"]}},"/api/v1/taxonomy/categories/{id}/retire":{"post":{"operationId":"taxonomy_retire_category","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetireTaxonomyNodeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxonomyMutationResponse"}}},"description":""},"404":{"description":"node not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/taxonomy/categories/{id}/retire — soft-retire a root or\nsubcategory. Idempotent like the type surface.","tags":["taxonomy"]}},"/api/v1/taxonomy/types":{"get":{"operationId":"taxonomy_list_types","parameters":[{"description":"Include retired nodes (admin editors + the manifest want them;\npickers default to false).","in":"query","name":"include_retired","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TaxonomyNodeView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/taxonomy/types — the org's ticket types; active-only unless\n`include_retired`.","tags":["taxonomy"]},"post":{"operationId":"taxonomy_create_type","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTaxonomyNodeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxonomyMutationResponse"}}},"description":""},"409":{"description":"duplicate name or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/taxonomy/types — create a ticket type.","tags":["taxonomy"]}},"/api/v1/taxonomy/types/{id}":{"patch":{"operationId":"taxonomy_update_type","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTaxonomyNodeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxonomyMutationResponse"}}},"description":""},"404":{"description":"node not visible in scope (rls_sparse)"},"409":{"description":"duplicate name, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"PATCH /api/taxonomy/types/{id} — rename / edit description / re-sort.\nBehavior-neutral: nothing keys off these trees.","tags":["taxonomy"]}},"/api/v1/taxonomy/types/{id}/retire":{"post":{"operationId":"taxonomy_retire_type","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetireTaxonomyNodeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxonomyMutationResponse"}}},"description":""},"404":{"description":"node not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/taxonomy/types/{id}/retire — soft-retire: pickers hide the\nnode, existing tickets keep their reference. Idempotent: retiring an\nalready-retired node returns its current state.","tags":["taxonomy"]}},"/api/v1/templates":{"get":{"operationId":"templates_list","parameters":[{"description":"filter to one template kind","in":"query","name":"kind","required":false,"schema":{"type":"string"}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"in":"query","name":"offset","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplatesListResponse"}}},"description":""},"403":{"description":"requires admin:view"}},"security":[{"bearer":[]}],"summary":"GET /api/templates — the org's templates (shipped defaults first, then\norg-owned rows), optionally filtered by kind.","tags":["admin"]},"post":{"operationId":"templates_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTemplateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateMutationResponse"}}},"description":""},"400":{"description":"validation failure (unknown kind, bad slug, out-of-vocabulary variable)"},"403":{"description":"requires admin:create"},"409":{"description":"slug taken or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/templates — create an org-owned template (shipped defaults\nare seed data; fork them instead of copying by hand).","tags":["admin"]}},"/api/v1/templates/{id}":{"delete":{"operationId":"templates_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateMutationResponse"}}},"description":""},"404":{"description":"template not visible in scope"},"409":{"description":"template immutable or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"DELETE /api/templates/{id} — remove an org-owned template. Shipped\ndefaults are immutable (typed 409 `template_immutable`).","tags":["admin"]},"get":{"operationId":"templates_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateView"}}},"description":""},"403":{"description":"requires admin:view"},"404":{"description":"template not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/templates/{id}","tags":["admin"]},"patch":{"operationId":"templates_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTemplateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateMutationResponse"}}},"description":""},"404":{"description":"template not visible in scope"},"409":{"description":"template immutable, version conflict, or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"PATCH /api/templates/{id} — edit an org-owned template's content.\nShipped defaults are immutable (typed 409 `template_immutable`): fork\nfirst, edit the fork.","tags":["admin"]}},"/api/v1/templates/{id}/fork":{"post":{"operationId":"templates_fork","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForkTemplateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateMutationResponse"}}},"description":""},"404":{"description":"template not visible in scope"},"409":{"description":"slug taken or idempotency key reused"},"422":{"description":"validation failure"}},"security":[{"bearer":[]}],"summary":"POST /api/templates/{id}/fork — duplicate a template (typically a\nshipped default) into a mutable org-owned copy. The origin row is\nuntouched.","tags":["admin"]}},"/api/v1/templates/{id}/render":{"post":{"operationId":"templates_render","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderTemplateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderedTemplate"}}},"description":""},"400":{"description":"out-of-vocabulary or malformed variable"},"403":{"description":"requires admin:view"},"404":{"description":"template or context entity not visible in scope"}},"security":[{"bearer":[]}],"summary":"POST /api/templates/{id}/render — resolve the template's typed\nvariables against real entity context and return the rendered\nsubject/body. Out-of-vocabulary placeholders are typed 400s, never\nsilent empties (KB3).","tags":["admin"]}},"/api/v1/templates/{id}/restore":{"post":{"operationId":"templates_restore","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestoreTemplateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateMutationResponse"}}},"description":""},"404":{"description":"template not visible in scope, or not a shipped default"},"409":{"description":"version conflict or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/templates/{id}/restore — reset a shipped default's content to\nthe canonical copy (KB4: presets stay canonical, orgs fork). Org-owned\nrows and forks are org-owned data and are never restorable.","tags":["admin"]}},"/api/v1/tickets":{"get":{"operationId":"tickets_list","parameters":[{"in":"query","name":"board_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"assignee_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"client_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"category","required":false,"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TicketStatusCategory"}]}},{"in":"query","name":"priority","required":false,"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Priority"}]}},{"description":"Comma-separated tag ids (Wave 11 lane 9): AND semantics — tickets\nmust carry every listed tag.","in":"query","name":"tags","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}},{"in":"query","name":"offset","required":false,"schema":{"format":"int64","type":"integer"}},{"description":"Keyset cursor (Wave 15 lane 3): the opaque `x-next-cursor` value\nfrom the previous page's last row. Resumes the ranked list\nstrictly after that row — deep pages stop paying O(offset) scan\ncost (docs/perf.md F2). When present, `offset` is ignored.","in":"query","name":"cursor","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TicketWithFields"},"type":"array"}}},"description":"","headers":{"x-next-cursor":{"description":"Opaque keyset cursor for the next page; present when this page is full and the walk may continue","schema":{"type":"string"}}}}},"security":[{"bearer":[]}],"summary":"GET /api/tickets — filtered, priority-ordered list; each row carries its\ncustom-field values (the private build tracker 2.17) fetched in one batched query.\nPagination is envelope-compatible: the body stays the plain ticket\narray; deep-page walks pass the opaque `cursor` query param (from the\nprevious page's `x-next-cursor` response header) and the server resumes\nstrictly after that row — keyset, not O(offset) scan (docs/perf.md F2).","tags":["tickets"]},"post":{"operationId":"tickets_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTicketRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketMutationResponse"}}},"description":""},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"missing required custom fields or invalid value"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets — allocates the human ticket number, initializes SLA\nclocks, validates + stores any initial custom-field values (REQUIRED\ndefinitions enforced), and emits `ticket.created` — all in one transaction.\nInstant-first (IF1/IF2): routed through the shared idempotency gate and\nreturns the read-your-write `mutation` metadata.","tags":["tickets"]}},"/api/v1/tickets/my-work":{"get":{"operationId":"tickets_my_work","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/MyWorkItem"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/my-work — everything assigned to me, not closed,\nacross all boards, priority-then-SLA-due ordered (the unified\ncross-board view from §1; the private build tracker 2.11 adds client/board annotations).","tags":["tickets"]}},"/api/v1/tickets/{id}":{"get":{"description":"The response carries the entity's weak validator (`ETag`) built from its\nmonotonic write seq, so a caller that still holds that revision sends\n`If-None-Match` and gets a bodyless `304 Not Modified` instead of the full\nrepresentation. Entities without a counter row behave exactly as before:\nno validator, no conditional short-circuit.","operationId":"tickets_get_one","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketWithFields"}}},"description":""},"304":{"description":"not modified; the If-None-Match validator matched the entity's current revision"}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id} — a conditional read.","tags":["tickets"]},"patch":{"operationId":"tickets_patch","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchTicketRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketMutationResponse"}}},"description":""},"404":{"description":"ticket not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"},"422":{"description":"invalid custom-field value"}},"security":[{"bearer":[]}],"summary":"PATCH /api/tickets/{id} — partial update; validates + upserts any\nprovided custom-field values; emits `ticket.updated`,\n`ticket.field_set` (per changed field), and `ticket.assigned` when the\nassignee changes. Instant-first (IF1/IF2): idempotency-keyed, version-\ngated, and returns prior + new values for undo.","tags":["tickets"]}},"/api/v1/tickets/{id}/attachments":{"get":{"operationId":"attachments_list_for_ticket","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Attachment"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/attachments — every attachment on one ticket that\nthe download door would also hand out.","tags":["attachments"]},"post":{"description":"Headers: `x-filename` (required), `content-type` (declared; verified\nagainst sniffed magic bytes). Body: raw bytes, capped by\n`MARULA_MAX_UPLOAD_MB` (default 25 MB).","operationId":"attachments_upload","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"items":{"format":"int32","minimum":0,"type":"integer"},"type":"array"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Attachment"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/attachments — stores one file against a ticket\n(linked to a comment later by the composer submit).","tags":["attachments"]}},"/api/v1/tickets/{id}/checklist":{"get":{"operationId":"tickets_get_checklist","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketChecklistView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/checklist — lazily instantiates any template items\nthe ticket has not picked up yet, then returns the full view.","tags":["tickets"]},"post":{"operationId":"tickets_add_checklist_item","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddChecklistItemRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketChecklistMutationResponse"}}},"description":""},"404":{"description":"ticket not visible in scope (rls_sparse)"},"409":{"description":"idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/checklist — appends an ad-hoc checklist item.\nAd-hoc items never block closure; emits `ticket.checklist_updated`.\nInstant-first (IF1/IF2): idempotency-keyed and returns the\nread-your-write mutation metadata for the created item.","tags":["tickets"]}},"/api/v1/tickets/{id}/checklist/complete-all":{"post":{"operationId":"tickets_complete_all_checklist","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteAllRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketChecklistBatchResponse"}}},"description":""},"404":{"description":"ticket or a listed item not visible in scope (rls_sparse; whole batch atomic)"},"409":{"description":"idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/checklist/complete-all — the close gate's Option\nA batch (docs/notes-tasks-workbar.md §4.2/§5.3): every listed task flips\nto `checked` AND lands its server-composed auto-append note, ALL in one\ntransaction with per-item idempotency keys (`complete-all:{ticket}:{item}`-\nderived under the caller's envelope key). Items already terminal\n(checked/N-A) are skipped idempotently; an unknown id 404s the whole\nbatch (atomicity). Every appended note carries\n`workflow_meta { kind: \"task_closeall\", auto: true, item_ids: [..] }` —\nthe REPORTED auto-close provenance the close-gate dialog displays.\nAlso usable pre-close, not only at the gate.","tags":["tickets"]}},"/api/v1/tickets/{id}/checklist/{item_id}":{"post":{"operationId":"tickets_set_checklist_item","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"item_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetChecklistItemRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketChecklistMutationResponse"}}},"description":""},"404":{"description":"ticket or item not visible in scope (rls_sparse)"},"409":{"description":"idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/checklist/{item_id} — toggles one checklist item;\nemits `ticket.checklist_updated` in the same transaction. Instant-first\n(IF1/IF2): idempotency-keyed and returns the read-your-write mutation\nmetadata for the toggled item.","tags":["tickets"]}},"/api/v1/tickets/{id}/comments":{"get":{"operationId":"tickets_list_comments","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Optional taxonomy filter (`internal`, `client_visible`,\n`in_house_update`). Omit for all kinds (staff view).","in":"query","name":"kind","required":false,"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/NoteKind"}]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TicketComment"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/comments — internal notes require ticket view;\nthe portal surface filters kinds separately (portal v1).","tags":["tickets"]},"post":{"operationId":"tickets_add_comment","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddCommentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketCommentMutationResponse"}}},"description":""},"404":{"description":"ticket not visible in scope (rls_sparse)"},"409":{"description":"idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/comments — adds a structured note; @mentions\nfan out into the notification center. Instant-first (IF1/IF2):\nidempotency-keyed and returns the read-your-write mutation metadata.","tags":["tickets"]}},"/api/v1/tickets/{id}/comments/{comment_id}/attachments":{"get":{"operationId":"attachments_list_for_comment","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"comment_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Attachment"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/comments/{comment_id}/attachments — one comment's\nattachments that the download door would also hand out.","tags":["attachments"]}},"/api/v1/tickets/{id}/cost-lines":{"get":{"operationId":"tickets_list_cost_lines","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TicketCostLine"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/cost-lines","tags":["ticket-costs"]},"post":{"operationId":"tickets_create_cost_line","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCostLineRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostLineMutationResponse"}}},"description":""},"404":{"description":"ticket not found in scope"},"409":{"description":"idempotency key reused"},"422":{"description":"invalid quantity/currency"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/cost-lines — add a money line (parts/flat/other).","tags":["ticket-costs"]}},"/api/v1/tickets/{id}/cost-lines/{line_id}":{"delete":{"operationId":"tickets_remove_cost_line","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"line_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"line not found in scope"},"409":{"description":"already billed"}},"security":[{"bearer":[]}],"summary":"DELETE /api/tickets/{id}/cost-lines/{line_id} — remove an unbilled line.","tags":["ticket-costs"]},"patch":{"operationId":"tickets_update_cost_line","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"line_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCostLineRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostLineMutationResponse"}}},"description":""},"404":{"description":"line not found in scope"},"409":{"description":"already billed, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"PATCH /api/tickets/{id}/cost-lines/{line_id} — edit an unbilled line.","tags":["ticket-costs"]}},"/api/v1/tickets/{id}/decision-suggestions":{"get":{"operationId":"tickets_decision_suggestions","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionSuggestionsResponse"}}},"description":""},"403":{"description":"missing tickets:view, or a board denial"},"404":{"description":"ticket not visible in scope (rls_sparse)"}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/decision-suggestions — the workbench card's\nread. Optimistic UI rides the instant-first envelope upstream; this\nendpoint is a plain RLS'd read scoped by the ticket gate: the\n`tickets:view` permission, then the board gate on the ticket's own\nboard (403), then the ticket itself (404 outside the org).","tags":["tickets"]}},"/api/v1/tickets/{id}/emails":{"get":{"operationId":"tickets_list_emails","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketEmailsView"}}},"description":""},"404":{"description":"ticket not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/emails — every ledger row routed to this ticket\n(inbound + outbound), newest first, with reply-chain parents resolved\nand outbound delivery state projected from the retry ledger.","tags":["tickets"]}},"/api/v1/tickets/{id}/expenses":{"get":{"operationId":"tickets_list_expenses","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TicketExpense"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/expenses — the ticket's expense entries.","tags":["ticket-costs"]},"post":{"operationId":"tickets_create_expense","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExpenseRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseMutationResponse"}}},"description":""},"404":{"description":"ticket not found in scope"},"409":{"description":"idempotency key reused"},"422":{"description":"invalid amount/currency"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/expenses — record an expense entry.","tags":["ticket-costs"]}},"/api/v1/tickets/{id}/expenses/{expense_id}":{"delete":{"operationId":"tickets_remove_expense","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"expense_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"expense not found in scope"},"409":{"description":"already billed"}},"security":[{"bearer":[]}],"summary":"DELETE /api/tickets/{id}/expenses/{expense_id} — remove an unbilled expense.","tags":["ticket-costs"]},"patch":{"operationId":"tickets_update_expense","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"expense_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateExpenseRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseMutationResponse"}}},"description":""},"404":{"description":"expense not found in scope"},"409":{"description":"already billed, version conflict, or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"PATCH /api/tickets/{id}/expenses/{expense_id} — edit an unbilled expense.","tags":["ticket-costs"]}},"/api/v1/tickets/{id}/fields/{field_id}":{"delete":{"operationId":"fields_clear_ticket_value","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"field_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":""},"422":{"description":"field is required and cannot be emptied"}},"security":[{"bearer":[]}],"summary":"DELETE /api/tickets/{id}/fields/{field_id} — clears one value; refuses\n(422) to empty a REQUIRED definition; emits `ticket.field_cleared`.","tags":["fields"]},"put":{"operationId":"fields_set_ticket_value","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"field_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTicketFieldValueRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketCustomFieldValue"}}},"description":""},"422":{"description":"value does not validate against the definition"}},"security":[{"bearer":[]}],"summary":"PUT /api/tickets/{id}/fields/{field_id} — validates the incoming JSON\nagainst the definition (kind coercion, option membership) and stores the\ncanonical encoding; emits `ticket.field_set`.","tags":["fields"]}},"/api/v1/tickets/{id}/links":{"get":{"operationId":"tickets_list_links","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinksView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/links — outgoing + incoming edges + the rollup.","tags":["ticket-links"]},"post":{"operationId":"tickets_create_link","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTicketLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"ticket not found in scope"},"409":{"description":"duplicate link or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/links — link this ticket (from) to another (to).","tags":["ticket-links"]}},"/api/v1/tickets/{id}/links/{link_id}":{"delete":{"operationId":"tickets_remove_link","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"link_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"link not found in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/tickets/{id}/links/{link_id} — remove one edge.","tags":["ticket-links"]}},"/api/v1/tickets/{id}/merge":{"post":{"operationId":"tickets_merge","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MergeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MergeResponse"}}},"description":""},"404":{"description":"ticket not found in scope"},"409":{"description":"typed merge conflict (self_merge, already_merged, closed_survivor, board_mismatch)"},"422":{"description":"idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/merge — merge THIS ticket into `survivor_id`.\nChildren, comments, time entries, cost lines, and expenses fold into\nthe survivor; the source is closed on its own board and marked\n`merged_into_ticket_id` (its row stays queryable — doctrine #9).","tags":["ticket-links"]}},"/api/v1/tickets/{id}/move-board":{"post":{"operationId":"tickets_move_board","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveBoardRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketBoardMoveResponse"}}},"description":""},"404":{"description":"ticket or target board not visible in scope (rls_sparse)"},"409":{"description":"no matching semantic status on the target board, version conflict, or idempotency key reused"},"422":{"description":"validation error"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/move-board — move a ticket to another board with\nthe status remapped through the SEMANTIC categories (typed 409 listing\nthe target statuses when the current category has no counterpart). SLA\nstate is preserved by default; `reset_sla` re-initializes the clocks.\nAudited, emits `ticket.board_moved`, all in one transaction. Instant-\nfirst (IF1/IF2): idempotency-keyed, version-gated, read-your-write.","tags":["taxonomy"]}},"/api/v1/tickets/{id}/note-draft":{"delete":{"operationId":"tickets_delete_note_draft","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"draft cleared (idempotent; 200 even with none)"}},"security":[{"bearer":[]}],"summary":"DELETE /api/tickets/{id}/note-draft — explicit draft discard.","tags":["tickets"]},"get":{"operationId":"tickets_get_note_draft","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteDraftResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/note-draft — restores the caller's autosaved\ncomposer draft (IF2, §5.2). Typed absence (200 with `draft: null`),\nnever a 404: an absent draft is a normal first-open state, not a miss.","tags":["tickets"]},"put":{"operationId":"tickets_put_note_draft","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutNoteDraftRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteDraftResponse"}}},"description":""},"404":{"description":"ticket not visible in scope (rls_sparse)"}},"security":[{"bearer":[]}],"summary":"PUT /api/tickets/{id}/note-draft — debounced autosave of the workbar\ncomposer draft (NOT a comment; never renders on any ledger). Fresh\ndrafts ride the IF idempotency-keyed write path and opportunistically\npurge 30-day-stale rows.","tags":["tickets"]}},"/api/v1/tickets/{id}/notes/refine":{"post":{"operationId":"tickets_refine_note","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefineNoteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefineNoteResponse"}}},"description":""},"404":{"description":"ticket not visible in scope (rls_sparse)"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/notes/refine — Tier-2 grammar pass (docs/notes-\ntasks-workbar.md §2.5): sends the draft to the org's configured\nCompletionProvider (BYOK seam) and returns a PROPOSAL. NEVER a direct\nwrite: the ledger is untouched; the composer shows a diff with\nApply/Discard and Apply rides the normal submit path. Provider\nunconfigured/unavailable → typed `status: \"unavailable\"` + the reason —\nthe Tier-1 Tidy always works and the UI owns the \"not available yet\"\nstate (Template-button pattern).","tags":["tickets"]}},"/api/v1/tickets/{id}/presence":{"get":{"operationId":"ticket_presence","parameters":[{"description":"Ticket id","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresenceRoster"}}},"description":""},"401":{"description":"missing or invalid bearer token"},"404":{"description":"ticket not visible in caller's org"}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/presence — current viewers/editors of the\nticket, TTL-pruned. The ticket must exist in the caller's org (RLS);\nthe roster is org-keyed, so it can never leak across orgs.","tags":["presence"]}},"/api/v1/tickets/{id}/revert":{"post":{"operationId":"tickets_revert","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevertTicketRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketRevertResponse"}}},"description":""},"409":{"description":"version conflict, idempotency key reuse, or undo_reverts_only (closed record, whole-record revert)"},"422":{"description":"unknown target seq, non-restorable step/field, or nothing to restore"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/revert — undo one server-accepted edit by\nrestoring its prior values as a NORMAL authorized idempotent write\n(IF1 envelope end to end). Appends a new trail row (op `revert`),\nemits `ticket.reverted` on the event bus, and writes the audited\n`ticket.reverted` trail row — the append-only history is never\nrewritten.","tags":["tickets"]}},"/api/v1/tickets/{id}/rollup":{"get":{"operationId":"tickets_rollup","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketRollup"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/rollup — children/members + folded time + billable money.","tags":["ticket-links"]}},"/api/v1/tickets/{id}/sign-offs":{"get":{"operationId":"sign_offs_list","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SignOffView"},"type":"array"}}},"description":""},"401":{"description":"missing or invalid credentials"},"403":{"description":"not permitted on this board"},"404":{"description":"ticket not found"}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/sign-offs — the ticket's sign-offs with payload\nfacts, revocation state and the correction relation. Renders a timeline\nrow without ever moving the mark's bytes.","tags":["sign_offs"]},"post":{"description":"The whole capture lands in one transaction: the payload row in the\ncontent-addressed store, the evidence row, `sign_off.created`, and the\naudit entry. The body's `idempotency_key` is the anchor the offline queue\nreplays on, so a retried queue op is a no-op rather than a second\nsignature.","operationId":"sign_offs_create","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSignOffRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignOff"}}},"description":""},"401":{"description":"missing or invalid credentials"},"403":{"description":"not permitted on this board"},"404":{"description":"ticket or sign-off not found"},"409":{"description":"already captured, revoked, or a version collision"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/sign-offs — capture one signature.","tags":["sign_offs"]}},"/api/v1/tickets/{id}/sign-offs/{sign_off_id}/revoke":{"post":{"description":"Appends one entry to the sign-off's revocation trail. The row and its\nbytes are untouched — this is why a revoked signature reads as revoked\nrather than as gone, which is the whole point of keeping evidence.","operationId":"sign_offs_revoke","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"sign_off_id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeSignOffRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignOffRevocation"}}},"description":""},"401":{"description":"missing or invalid credentials"},"403":{"description":"not permitted on this board"},"404":{"description":"ticket or sign-off not found"},"409":{"description":"already captured, revoked, or a version collision"},"422":{"description":"validation failed"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/sign-offs/{sign_off_id}/revoke","tags":["sign_offs"]}},"/api/v1/tickets/{id}/similar-tickets":{"get":{"operationId":"tickets_similar_tickets","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Override the retrieval text (defaults to the ticket's own title\nplus a body excerpt). Clamped like the search surface.","in":"query","name":"q","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimilarTicketsResponse"}}},"description":""},"404":{"description":"ticket not visible in scope (rls_sparse)"}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/similar-tickets — retrieval top-k + per-candidate\nrelatedness Noul. Degrade-never-5xx: a quiet decision edge returns the\nplain retrieval list, never an error.","tags":["tickets"]}},"/api/v1/tickets/{id}/sla":{"get":{"operationId":"tickets_sla_status","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketSlaStatus"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/comments — optional kind filter; internal notes\nrequire ticket view. The portal surface must consume the dedicated\nclient-visible read (`core-db::tickets::list_client_visible_comments`),\nnever this staff endpoint.\nGET /api/tickets/{id}/sla — clock state plus a live business-time\nremaining estimate for the resolution budget (drives the UI badge).","tags":["tickets"]}},"/api/v1/tickets/{id}/status":{"post":{"operationId":"tickets_change_status","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeStatusRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketStatusMutationResponse"}}},"description":""},"404":{"description":"ticket not visible in scope (rls_sparse)"},"409":{"description":"pre-close checklist blocked, version conflict, or idempotency key reused"},"422":{"description":"invalid transition"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/status — the guarded lifecycle transition:\ndomain transition rules, pre-close checklist enforcement (typed 409\nlisting the incomplete required items when the board enforces), SLA\npause bookkeeping, and the `ticket.status_changed` event, all in one tx.\nInstant-first (IF1/IF2): idempotency-keyed, version-gated, read-your-write.","tags":["tickets"]}},"/api/v1/tickets/{id}/stock-movements":{"get":{"operationId":"ticket_stock_movements","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/StockMovementView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/stock-movements — the job's parts-used ledger\nread: every movement anchored to this ticket, newest first. Ticket\nvisibility gates the read; cost fields stay `financial:read`-gated\n(RB2), same as the per-item ledger read.","tags":["commercial"]}},"/api/v1/tickets/{id}/tags":{"get":{"operationId":"tickets_list_tags","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Tag"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/tags — the ticket's applied tags.","tags":["tags"]},"post":{"operationId":"tickets_apply_tag","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyTagRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"ticket or tag not found"},"409":{"description":"idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/tickets/{id}/tags — apply a tag to a ticket (idempotent).","tags":["tags"]}},"/api/v1/tickets/{id}/tags/{tag_id}":{"delete":{"operationId":"tickets_remove_tag","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"tag_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"404":{"description":"ticket or tag not in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/tickets/{id}/tags/{tag_id} — remove one applied tag.","tags":["tags"]}},"/api/v1/tickets/{id}/time-entries":{"get":{"operationId":"tickets_ticket_time_entries","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TimeEntry"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/time-entries","tags":["tickets"]}},"/api/v1/tickets/{id}/undo-stack":{"get":{"operationId":"tickets_undo_stack","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"description":"Maximum steps / field rows returned (1–200, default 50). Newest\nfirst; the first step's `seq` is the record's current version.","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UndoStackResponse"}}},"description":""},"404":{"description":"ticket not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/tickets/{id}/undo-stack — the per-field / per-record\napplied-state history read model for one record.","tags":["tickets"]}},"/api/v1/time-categories":{"get":{"operationId":"time_categories_list","parameters":[{"description":"Include retired categories (settings admin reads)","in":"query","name":"include_retired","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeCategoryListResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/time-categories — the org's taxonomy (admin + pickers).","tags":["time"]},"post":{"operationId":"time_categories_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeCategoryCreateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeCategoryMutationResponse"}}},"description":""},"409":{"description":"duplicate slug or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/time-categories — create a category.","tags":["time"]}},"/api/v1/time-categories/capture-defaults":{"get":{"operationId":"time_categories_capture_defaults","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaptureDefaultsResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/time-categories/capture-defaults — the picker read for the\nwrap-up sheet + quick-log: ordered non-retired categories + the\nkept backfill mapping.","tags":["time"]}},"/api/v1/time-categories/reorder":{"post":{"operationId":"time_categories_reorder","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderCategoriesRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeCategoryListResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/time-categories/reorder — rewrite display order from an\nordered id list (settings drag-order).","tags":["time"]}},"/api/v1/time-categories/work-type-map":{"get":{"operationId":"time_categories_work_type_map_list","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/WorkTypeMapRowWire"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/time-categories/work-type-map — the kept backfill + manual\nmapping (settings read).","tags":["time"]},"post":{"operationId":"time_categories_work_type_map_upsert","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertWorkTypeMapRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/WorkTypeMapRowWire"},"type":"array"}}},"description":""},"404":{"description":"category slug not found in this org"}},"security":[{"bearer":[]}],"summary":"POST /api/time-categories/work-type-map — add a manual alias.","tags":["time"]}},"/api/v1/time-categories/work-type-map/apply":{"post":{"operationId":"time_categories_work_type_map_apply","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyWorkTypeMapRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyWorkTypeMapResponse"}}},"description":""},"404":{"description":"category slug not found in this org"}},"security":[{"bearer":[]}],"summary":"POST /api/time-categories/work-type-map/apply — recategorize\nexisting entries (explicit alias application).","tags":["time"]}},"/api/v1/time-categories/{id}":{"delete":{"operationId":"time_categories_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeCategoryMutationResponse"}}},"description":""},"404":{"description":"category not visible in scope (rls_sparse)"}},"security":[{"bearer":[]}],"summary":"DELETE /api/time-categories/{id} — soft retire (pickers drop it,\nentries keep the slug reference; nothing is hard-deleted).","tags":["time"]},"patch":{"operationId":"time_categories_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeCategoryUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeCategoryMutationResponse"}}},"description":""},"404":{"description":"category not visible in scope (rls_sparse)"}},"security":[{"bearer":[]}],"summary":"PATCH /api/time-categories/{id} — content update (slug immutable).","tags":["time"]}},"/api/v1/time-entries":{"get":{"operationId":"timers_list_entries","parameters":[{"in":"query","name":"ticket_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"since","required":false,"schema":{"format":"date-time","type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TimeEntry"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/time-entries","tags":["timers"]},"post":{"operationId":"timers_create_entry","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTimeEntryRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntry"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/time-entries — manual entry (no timer).","tags":["timers"]}},"/api/v1/time-entries/approvals/decide":{"post":{"description":"Write-gate symmetry with `submit` and `reopen_entry`: an approver never\nsigns off their own time. A self-owned id comes back as `own_entry` and\nis never transitioned; an owner or admin keeps the override, so a\none-person org can still decide.","operationId":"time_entries_decide","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkDecisionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkDecisionResponse"}}},"description":""},"422":{"description":"unknown rejection reason_code"}},"security":[{"bearer":[]}],"summary":"POST /api/time-entries/approvals/decide — bulk approve or bulk reject\n(`special:approve_time`). Only entries currently in the queue apply;\nevery other id comes back with a per-item outcome. Emits one\n`time_entry.approval_*` event per applied entry.","tags":["timers"]}},"/api/v1/time-entries/approvals/queue":{"get":{"operationId":"time_entries_queue","parameters":[{"description":"Filter by approval state; defaults to `submitted` (the queue).","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}},{"description":"Narrow to one submitter.","in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Per-approver decision history: entries this approver decided.","in":"query","name":"decided_by","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TimeEntryQueueItem"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/time-entries/approvals/queue — the approver queue (submitted\nentries by default). Approvers (`special:approve_time`) see the org\nqueue and can filter `decided_by` for their own decision history;\neveryone else sees only their own submissions.","tags":["timers"]}},"/api/v1/time-entries/billing-suggestions":{"get":{"operationId":"time_entries_billing_suggestions_bulk","parameters":[{"description":"Comma-separated entry ids (≤ 100 — approvals page batches).","in":"query","name":"ids","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkEntrySuggestionsResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/time-entries/billing-suggestions?ids=a,b,c — the bulk read\nfor approvals rows (≤ 100 ids per call).","tags":["time"]}},"/api/v1/time-entries/{id}/approvals":{"get":{"operationId":"time_entries_approval_history","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TimeEntryApprovalRecord"},"type":"array"}}},"description":""},"404":{"description":"entry not visible in scope (not_found)"}},"security":[{"bearer":[]}],"summary":"GET /api/time-entries/:id/approvals — the append-only decision trail\n(`seq` ascending). Approvers and owner/admins read any trail; other\nusers only their own entries'.","tags":["timers"]}},"/api/v1/time-entries/{id}/billing-suggestions":{"get":{"operationId":"time_entry_billing_suggestions","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntrySuggestionsResponse"}}},"description":""},"404":{"description":"entry not visible in scope (rls_sparse)"}},"security":[{"bearer":[]}],"summary":"GET /api/time-entries/{id}/billing-suggestions — the\n`decision_billing_audit` shadow answers for one entry, classified\ninto suggestion chips. Read-only: chips never mutate anything.","tags":["time"]}},"/api/v1/time-entries/{id}/fix":{"post":{"description":"This is the server side of the queue's one-click fix: adopt the factual\ndescription the server assembled from the ticket, or cut the minutes that\nanother entry of the same person already claimed. The entry stays in the\nqueue afterwards — the approver still presses Approve, because a fix clears\nan objection and it does not sign anybody's time off.","operationId":"time_entry_fix","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FixEntryRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FixEntryResponse"}}},"description":""},"404":{"description":"entry not visible in scope (rls_sparse)"},"409":{"description":"entry locked by invoicing (time_entry_locked)"},"422":{"description":"entry not in the queue, or the fix is not allowed"}},"security":[{"bearer":[]}],"summary":"POST /api/time-entries/:id/fix — apply one verdict's fix to a queued\nentry (`special:approve_time`).","tags":["timers"]}},"/api/v1/time-entries/{id}/reopen":{"post":{"operationId":"time_entries_reopen","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitResponse"}}},"description":""},"409":{"description":"entry locked by invoicing (time_entry_locked)"},"422":{"description":"only rejected entries can be reopened"}},"security":[{"bearer":[]}],"summary":"POST /api/time-entries/:id/reopen — move a rejected entry back to draft\n(owner or admin) so it can be corrected and resubmitted.","tags":["timers"]}},"/api/v1/time-entries/{id}/submit":{"post":{"operationId":"time_entries_submit","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitEntryRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitResponse"}}},"description":""},"404":{"description":"entry not visible in scope (rls_sparse)"},"409":{"description":"entry locked by invoicing (time_entry_locked)"},"422":{"description":"entry not in a submittable state"}},"security":[{"bearer":[]}],"summary":"POST /api/time-entries/:id/submit — the entry owner (or an admin)\npushes a draft/rejected entry into the approver queue.","tags":["timers"]}},"/api/v1/time-gaps":{"get":{"operationId":"time_gaps_user","parameters":[{"description":"Inclusive local-day start (YYYY-MM-DD). Defaults to yesterday.","in":"query","name":"from","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Inclusive local-day end (YYYY-MM-DD). Defaults to `from`.","in":"query","name":"to","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Manager report only: whose days. Requires owner/admin.","in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GapReportResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/time-gaps?user_id= — manager-scoped report for one member.\nOwner/admin only; everyone else uses `/api/time-gaps/me`.","tags":["timers"]}},"/api/v1/time-gaps/me":{"get":{"operationId":"time_gaps_me","parameters":[{"description":"Inclusive local-day start (YYYY-MM-DD). Defaults to yesterday.","in":"query","name":"from","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Inclusive local-day end (YYYY-MM-DD). Defaults to `from`.","in":"query","name":"to","required":false,"schema":{"format":"date","type":["string","null"]}},{"description":"Manager report only: whose days. Requires owner/admin.","in":"query","name":"user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GapReportResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/time-gaps/me — the caller's day-level gap report (defaults to\nyesterday in the caller's timezone).","tags":["timers"]}},"/api/v1/time-settings":{"get":{"operationId":"time_settings_get","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeSettingsResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/time-settings — org time-discipline knobs (admin).","tags":["timers"]},"put":{"operationId":"time_settings_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTimeSettingsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeSettingsResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PUT /api/time-settings — update the org's time-discipline knobs (admin).","tags":["timers"]}},"/api/v1/timers/active":{"get":{"operationId":"timers_active","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/timers/active — the caller's running session (at most one).","tags":["timers"]}},"/api/v1/timers/pause":{"post":{"operationId":"timers_pause","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PauseTimerRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimerSession"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/timers/pause — pauses the active (or given) session, recording\nthe reason on the segment.","tags":["timers"]}},"/api/v1/timers/resume":{"post":{"operationId":"timers_resume","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PauseTimerRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimerSession"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/timers/resume — resumes a paused session with a new segment.","tags":["timers"]}},"/api/v1/timers/start":{"post":{"operationId":"timers_start","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartTimerRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimerMutationResponse"}}},"description":""},"404":{"description":"ticket not visible in scope (rls_sparse)"},"409":{"description":"idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/timers/start — starts (or switches to) a timer. If another\nsession is running it is auto-paused with reason `switched` and stays in\nthe working set; the DB's partial unique index is the backstop.\nInstant-first (IF1/IF2): idempotency-keyed and read-your-write.","tags":["timers"]}},"/api/v1/timers/stop":{"post":{"operationId":"timers_stop","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StopTimerRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryMutationResponse"}}},"description":""},"404":{"description":"session not visible in scope (rls_sparse)"},"409":{"description":"version conflict or idempotency key reused"}},"security":[{"bearer":[]}],"summary":"POST /api/timers/stop — stops the session and materializes a time entry\n(rounded up to the minute) in the same transaction.","tags":["timers"]}},"/api/v1/timers/working-set":{"get":{"operationId":"timers_working_set","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TimerSession"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/timers/working-set — recent sessions (running first): the\ndocked working set the UI renders.","tags":["timers"]}},"/api/v1/tool-checkouts":{"get":{"operationId":"tool_checkouts_list","parameters":[{"description":"open | returned (absent = both).","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"holder_user_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"kit_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Only open checkouts past their due date.","in":"query","name":"overdue_only","required":false,"schema":{"type":["boolean","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ToolCheckoutDetailResponse"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tool-checkouts.","tags":["commercial"]},"post":{"operationId":"tool_checkouts_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCheckoutRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolCheckoutMutationResponse"}}},"description":""},"404":{"description":"item, kit, or location not visible in scope"},"409":{"description":"kit not ready / unit already out / not a tool"}},"security":[{"bearer":[]}],"summary":"POST /api/tool-checkouts — instant-first (IF1/IF2).","tags":["commercial"]}},"/api/v1/tool-checkouts/{id}":{"get":{"operationId":"tool_checkouts_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolCheckoutDetailResponse"}}},"description":""},"404":{"description":"not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/tool-checkouts/{id}.","tags":["commercial"]}},"/api/v1/tool-checkouts/{id}/checkin":{"post":{"operationId":"tool_checkouts_checkin","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckinRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolCheckoutMutationResponse"}}},"description":""},"404":{"description":"not visible in scope"},"409":{"description":"already returned / line not on this checkout"}},"security":[{"bearer":[]}],"summary":"POST /api/tool-checkouts/{id}/checkin — instant-first (IF1/IF2).","tags":["commercial"]}},"/api/v1/tool-kits":{"get":{"operationId":"tool_kits_list","parameters":[{"description":"`true` = active kits only; absent = all.","in":"query","name":"active","required":false,"schema":{"type":["boolean","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ToolKitView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tool-kits.","tags":["commercial"]},"post":{"operationId":"tool_kits_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKitRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolKitMutationResponse"}}},"description":""},"409":{"description":"kit name already exists"}},"security":[{"bearer":[]}],"summary":"POST /api/tool-kits — instant-first (IF1/IF2).","tags":["commercial"]}},"/api/v1/tool-kits/{id}":{"get":{"operationId":"tool_kits_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KitDetailResponse"}}},"description":""},"404":{"description":"not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/tool-kits/{id}.","tags":["commercial"]},"patch":{"operationId":"tool_kits_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateKitRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolKitMutationResponse"}}},"description":""},"404":{"description":"not visible in scope"},"409":{"description":"version conflict"}},"security":[{"bearer":[]}],"summary":"PATCH /api/tool-kits/{id} — instant-first (IF1/IF2).","tags":["commercial"]}},"/api/v1/tool-kits/{id}/availability":{"get":{"operationId":"tool_kits_availability","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"location_id","required":false,"schema":{"format":"uuid","type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KitAvailabilityResponse"}}},"description":""},"404":{"description":"not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/tool-kits/{id}/availability — can the kit go out here?","tags":["commercial"]}},"/api/v1/tool-kits/{id}/members":{"post":{"operationId":"tool_kits_add_member","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddKitMemberRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KitMutationWithMembersResponse"}}},"description":""},"404":{"description":"kit or item not visible in scope"},"409":{"description":"item is not a tool / already a member"}},"security":[{"bearer":[]}],"summary":"POST /api/tool-kits/{id}/members — add a member tool to the kit.","tags":["commercial"]}},"/api/v1/tool-kits/{id}/members/{member_id}":{"delete":{"operationId":"tool_kits_remove_member","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"member_id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KitDetailResponse"}}},"description":""},"404":{"description":"kit or member not visible in scope"}},"security":[{"bearer":[]}],"summary":"DELETE /api/tool-kits/{id}/members/{member_id}.","tags":["commercial"]}},"/api/v1/tools/availability":{"get":{"operationId":"tools_availability","parameters":[{"in":"query","name":"location_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"item_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ToolAvailabilityView"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/tools/availability — in-stock counts per tool item + location.","tags":["commercial"]}},"/api/v1/users/me":{"patch":{"operationId":"users_me_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchMeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}},"description":""},"422":{"description":""}},"security":[{"bearer":[]}],"summary":"Wave 26 onboarding leg (b): the per-user first-run \"check your profile\"\nstep needs a self-editable name/timezone. Self-scoped: PATCH /api/v1/users/me\nchanges ONLY the caller's own display name and timezone (email is\nimmutable here by design), with an audit row in the same transaction.","tags":["auth"]}},"/api/v1/vendors":{"get":{"operationId":"vendors_list","parameters":[{"in":"query","name":"q","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"include_inactive","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Vendor"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/vendors","tags":["purchasing"]},"post":{"operationId":"vendors_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVendorRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorMutationResponse"}}},"description":""},"409":{"description":"vendor code already exists"}},"security":[{"bearer":[]}],"summary":"POST /api/vendors","tags":["purchasing"]}},"/api/v1/vendors/{id}":{"get":{"operationId":"vendors_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorDetail"}}},"description":""},"404":{"description":"vendor not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/vendors/{id} — entity + contacts + links.","tags":["purchasing"]},"patch":{"operationId":"vendors_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVendorRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorMutationResponse"}}},"description":""},"404":{"description":"vendor not visible in scope"},"409":{"description":"vendor code already exists or version conflict"}},"security":[{"bearer":[]}],"summary":"PATCH /api/vendors/{id}","tags":["purchasing"]}},"/api/v1/vendors/{id}/contacts":{"get":{"operationId":"vendors_list_contacts","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/VendorContact"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/vendors/{id}/contacts","tags":["purchasing"]},"post":{"operationId":"vendors_replace_contacts","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplaceContactsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactsMutationResponse"}}},"description":""},"404":{"description":"vendor not visible in scope"}},"security":[{"bearer":[]}],"summary":"POST /api/vendors/{id}/contacts — replace-on-write.","tags":["purchasing"]}},"/api/v1/vendors/{id}/links":{"get":{"operationId":"vendors_list_links","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ExternalLink"},"type":"array"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/vendors/{id}/links","tags":["purchasing"]},"post":{"operationId":"vendors_add_link","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExternalLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkMutationResponse"}}},"description":""},"404":{"description":"vendor not visible in scope"},"422":{"description":"the url must be https"}},"security":[{"bearer":[]}],"summary":"POST /api/vendors/{id}/links","tags":["purchasing"]}},"/api/v1/webhooks":{"get":{"operationId":"webhooks_list","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookList"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/webhooks — the org's subscriptions (never includes secrets).","tags":["integrations"]},"post":{"operationId":"webhooks_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSecretResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/webhooks — create a subscription; responds with the signing\nsecret once. Emits an audit record in the same transaction.","tags":["integrations"]}},"/api/v1/webhooks/stripe":{"post":{"operationId":"stripe_webhook_receive","requestBody":{"content":{"application/json":{"schema":{"items":{"format":"int32","minimum":0,"type":"integer"},"type":"array"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":""},"400":{"description":""},"403":{"description":""},"409":{"description":"the invoice refused the settlement: it left its payable state, its total moved since the event was priced, or the amount exceeds the balance"}},"tags":["billing"]}},"/api/v1/webhooks/{id}":{"delete":{"operationId":"webhooks_delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"DELETE /api/webhooks/{id} — removes the subscription; deliveries\ncascade (history is intentionally scoped to the subscription's life).","tags":["integrations"]},"patch":{"operationId":"webhooks_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"PATCH /api/webhooks/{id} — mutate URL / filter / enabled flag.","tags":["integrations"]}},"/api/v1/webhooks/{id}/deliveries":{"get":{"operationId":"webhooks_deliveries_list","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"query","name":"cursor","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryPage"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/webhooks/{id}/deliveries — the in-app delivery log, newest\nfirst, keyset-cursor paged.","tags":["integrations"]}},"/api/v1/webhooks/{id}/test":{"post":{"operationId":"webhooks_test_fire","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestFireResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/webhooks/{id}/test — fires one `webhook.test` envelope into\nthe real pipeline (outbox → relay → stream → signed delivery), then\nreturns 202; poll the deliveries endpoint to watch attempts land.","tags":["integrations"]}},"/api/v1/workflow-overrides/registry":{"get":{"operationId":"workflow_overrides_registry","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OverrideRegistryResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/workflow-overrides/registry — the closed set of overridable\nconstructs with their typed value kinds (the UI renders the editor FROM\nthis; policy §4.5: customizations are data against a typed registry).","tags":["workflow-overrides"]}},"/api/v1/workflows/author":{"post":{"description":"Admin-gated (Edit). NOT a write: it returns a draft, its issues and the\ndiff, and persists nothing. The completion rides the shared AI admission\npath with the completion + tool-calling edges.","operationId":"workflow_author","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorWorkflowRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorWorkflowResponse"}}},"description":""},"403":{"description":"AI admission refused (typed reason in the envelope)"},"422":{"description":"the request or the base document did not validate"}},"security":[{"bearer":[]}],"summary":"POST /api/workflows/author — author a workflow draft from a sentence.","tags":["workflows"]}},"/api/v1/workflows/documents":{"get":{"operationId":"workflow_documents_list","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowDocumentListView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/workflows/documents","tags":["workflows"]},"post":{"operationId":"workflow_documents_create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWorkflowDocumentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowDocumentView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"POST /api/workflows/documents — the document is validated before storage\n(see [`validate_definition`]); the row is created as revision 1.","tags":["workflows"]}},"/api/v1/workflows/documents/{id}":{"get":{"operationId":"workflow_documents_get","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowDocumentView"}}},"description":""},"404":{"description":"document not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/workflows/documents/{id}","tags":["workflows"]},"patch":{"operationId":"workflow_documents_update","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWorkflowDocumentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowDocumentView"}}},"description":""},"404":{"description":"document not visible in scope"}},"security":[{"bearer":[]}],"summary":"PATCH /api/workflows/documents/{id} — replacing the document validates it\nand advances the revision.","tags":["workflows"]}},"/api/v1/workflows/nodes":{"get":{"operationId":"workflow_nodes_list","parameters":[{"description":"Which audience's slice to return: `human` (the palette) or `ai`\n(the LLM tool catalogue). Defaults to `human`.","in":"query","name":"audience","required":false,"schema":{"type":["string","null"]}},{"description":"Restrict to one tier: `a`, `b` or `c`. Defaults to every tier.","in":"query","name":"tier","required":false,"schema":{"type":["string","null"]}},{"description":"Restrict to one family: `trigger`, `decision`, `condition`,\n`action`, `control` or `value`.","in":"query","name":"family","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeCatalogueView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/workflows/nodes — the catalogue, filtered. Declarations carry\nno org data, so any authed org member may read; each consuming surface\nkeeps its own data gates.","tags":["workflows"]}},"/api/v1/workflows/nodes/{id}":{"get":{"operationId":"workflow_nodes_get","parameters":[{"description":"Node id, e.g. ticket.create","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/workflows/nodes/{id} — one node with both schemas parsed.\nThe on-demand schema leg the authoring model uses, so exposing the\ncatalogue to a prompt costs one call per node it actually needs rather\nthan the whole catalogue inlined.","tags":["workflows"]}},"/api/v1/workflows/plugin-nodes":{"get":{"operationId":"workflow_plugin_nodes","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PluginNodeListView"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/workflows/plugin-nodes — the org's plugin-contributed\nworkflow nodes, parsed through the same contract the registry uses.","tags":["workflows"]}},"/api/v1/workflows/runs":{"get":{"operationId":"workflow_runs_list","parameters":[{"description":"Optional status filter: running | waiting | succeeded | failed | abandoned.","in":"query","name":"status","required":false,"schema":{"type":["string","null"]}},{"description":"Optional rule filter: only that workflow's runs.","in":"query","name":"rule_id","required":false,"schema":{"format":"uuid","type":["string","null"]}},{"description":"Cursor from the previous page's `next_cursor`.","in":"query","name":"cursor","required":false,"schema":{"type":["string","null"]}},{"description":"Page size (default 50, capped at 200).","in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunListResponse"}}},"description":""}},"security":[{"bearer":[]}],"summary":"GET /api/workflows/runs — the run ledger, newest first, filterable by\nstatus and by rule. Keyset-cursored on `(created_at, id)`, exactly like\nthe webhook delivery ledger: the two logs page and behave alike.","tags":["workflows"]}},"/api/v1/workflows/runs/{id}":{"get":{"operationId":"workflow_run_detail","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunDetailView"}}},"description":""},"404":{"description":"run not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/workflows/runs/{id} — one run with its whole trace.","tags":["workflows"]}},"/api/v1/workflows/runs/{id}/retry":{"post":{"description":"The refusal is the point: if re-running would repeat a step that\ndeclares `idempotent = false`, the answer is a typed 409 that names every\noffending node rather than a second ticket or a second email. The\nidempotency key (IF1) is claimed before anything is written, so a\ndouble-submit returns the first retry instead of queueing two.","operationId":"workflow_run_retry","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetryRunRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunRetryResponse"}}},"description":""},"404":{"description":"run not visible in scope"},"409":{"description":"not retryable, or a repeat would double-apply"},"422":{"description":"unknown retry mode"}},"security":[{"bearer":[]}],"summary":"POST /api/workflows/runs/{id}/retry — re-run a failed run as a NEW row.","tags":["workflows"]}},"/api/v1/workflows/runs/{id}/steps":{"get":{"operationId":"workflow_run_steps","parameters":[{"in":"query","name":"cursor","required":false,"schema":{"type":["string","null"]}},{"in":"query","name":"limit","required":false,"schema":{"format":"int64","type":["integer","null"]}},{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunStepListResponse"}}},"description":""},"404":{"description":"run not visible in scope"}},"security":[{"bearer":[]}],"summary":"GET /api/workflows/runs/{id}/steps — one run's step trace, paged.","tags":["workflows"]}}},"tags":[{"description":"Registration, login, token lifecycle","name":"auth"},{"description":"Client → site → contact → asset hierarchy","name":"clients"},{"description":"Boards and semantic-category-bound statuses","name":"boards"},{"description":"Ticketing: lifecycle, notes, checklists","name":"tickets"},{"description":"Single-active-timer workflow + time entries","name":"timers"},{"description":"Notification center","name":"notifications"},{"description":"Typed settings registry: org + user scoped values","name":"settings"},{"description":"Wave 26 onboarding flows: org wizard + per-user first-run (skippable/disablable)","name":"onboarding"},{"description":"Append-only audit log with filters + cursor pagination (admin)","name":"audit"},{"description":"Unified FTS search across tickets, comments and clients","name":"search"},{"description":"Operational reports + stored report definitions over the versioned read-model views","name":"reports"},{"description":"Append-only audit log (admin)","name":"audit"},{"description":"SLA calendars, policies, escalation ladders (admin)","name":"sla"},{"description":"Event-triggered automation rules (admin)","name":"automations"},{"description":"Client portal v1: login, own-client tickets, replies","name":"portal"},{"description":"Staff administration of portal users + board exposure","name":"portal-admin"},{"description":"Signed webhooks OUT + scoped API keys (plugin runtime v1)","name":"integrations"},{"description":"Contracts (recurring / T&M / retainer / fixed-fee) + billing-rule previews + native invoicing v1: invoice runs, payments (PaymentProvider seam, none-mode), dunning states","name":"billing"},{"description":"Appointments, availability, on-call, calendar views","name":"scheduling"},{"description":"Projects: phases/tasks/dependencies/views/templates + milestone billing events","name":"projects"},{"description":"File attachments on tickets/comments (ObjectStore-backed)","name":"attachments"},{"description":"Quarantine admin: objects the malware scanner rejected, with the release override, its reason, and the delete path (org admin only)","name":"quarantine"},{"description":"Customer sign-off capture: witnessed signatures, their revocation trail, and consent wording","name":"sign_offs"},{"description":"Native knowledge base: categories, articles, client-facing flags","name":"kb"},{"description":"Documents with immutable, append-only, diffable versions (restore-creates-new-version)","name":"documents"},{"description":"Alert ingestor: HMAC-signed webhook ingest, dedup/correlation, storm collapse into incident tickets","name":"alerts"},{"description":"Dispatch connector v1: scoped machine surface — schedule reads + field-service status pushes (API-key dispatch:read / dispatch:write)","name":"dispatch-connector"},{"description":"Agent platform v1: capability registry (deny-by-default, involvement dial), versioned agent skills, IF8 proposal pipeline (approval by default)","name":"agent-platform"},{"description":"Migration connector (third-party tools): bulk push riding the import engine with checkpoint cursors, cursor-paged read-model pull; API-key scoped (migration:read/write), per-key rate limited","name":"migration"},{"description":"Vendors, purchase orders with typed receipt states, receipts posting into the stock ledger, reseller deeplinks, and the bill → project cost rollup hook (Wave 32 lane 2)","name":"purchasing"},{"description":"RAG and multimodal vector namespace (ADR 0020): 768-dim passage and media chunk ingest, hybrid BM25 + exact-KNN RRF retrieval, and provable deletion receipts","name":"rag"}]}