{"openapi":"3.1.0","info":{"title":"Limena API","version":"0.1.0"},"paths":{"/api/v1/auth/login":{"post":{"tags":["auth"],"summary":"Login","description":"Authenticate an email + password and issue access + refresh tokens.\n\nTenant is resolved from the email itself (not the Host) — production\nserves the authed surface from a single host (`app.limena.io`), so\nthere is no slug in the URL to key off. A SECURITY DEFINER function\ndoes the cross-tenant lookup; the route then sets RLS context against\nthe resolved tenant before calling `authenticate`. A missing user\nreturns the same generic `INVALID_CREDENTIALS` shape as a wrong-\npassword attempt — no email-enumeration leak.","operationId":"login_api_v1_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/login/totp":{"post":{"tags":["auth"],"summary":"Login Complete Totp","description":"Complete a partial-auth login by submitting a TOTP code. On success,\nmints the full access + refresh tokens; if `trust_device=true`, also\nissues a trusted-device cookie that skips the TOTP prompt for 30 days\non this browser.","operationId":"login_complete_totp_api_v1_auth_login_totp_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginTotpCompleteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/login/recovery":{"post":{"tags":["auth"],"summary":"Login Complete Recovery","description":"Recovery-code fallback for partial-auth completion. Used when the\nauthenticator app is unavailable. Single-use — the code is consumed on\nsuccessful match.","operationId":"login_complete_recovery_api_v1_auth_login_recovery_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRecoveryCompleteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/refresh":{"post":{"tags":["auth"],"summary":"Refresh","operationId":"refresh_api_v1_auth_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/auth/logout":{"post":{"tags":["auth"],"summary":"Logout","operationId":"logout_api_v1_auth_logout_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/forgot-password":{"post":{"tags":["auth"],"summary":"Forgot Password","description":"Always returns 200 — never reveals whether email exists.\n\nTenant is resolved from the email itself via a SECURITY DEFINER\nfunction (migration 0031) — the post-Q9 single-host prod\n(`app.limena.io`) carries no slug in the URL. When no active user\nmatches, the route returns the same 200 response without writing an\naudit row (no tenant_id exists to scope the row against; rate-limit\n+ Sentry signal still surfaces high-volume probing).\n\nCloses T1.2 + T2.10 (audit + IP/UA capture for password-reset request)\nfor the match case; the no-match audit-row pattern is deliberately\ndropped given the body-keyed resolution shape.","operationId":"forgot_password_api_v1_auth_forgot_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgotPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/reset-password":{"post":{"tags":["auth"],"summary":"Reset Password","description":"Complete a password reset using the token from the email link.\n\nTenant is resolved from the token hash via a SECURITY DEFINER function\n— the reset link does not carry slug information and the post-Q9\nsingle-host prod has no Host signal to derive one from. Token\nnot-found / already-used / expired are all surfaced uniformly as\n`INVALID_TOKEN` by the service layer's atomic mark-as-used UPDATE.","operationId":"reset_password_api_v1_auth_reset_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/me":{"get":{"tags":["auth"],"summary":"Me","operationId":"me_api_v1_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPublic"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/2fa/enrol":{"post":{"tags":["2fa"],"summary":"Enrol","description":"Begin TOTP enrolment. Stages a secret in Redis (10-min TTL); returns\nthe provisioning URI + a base64 PNG QR code. No DB write — that\nhappens at `POST /2fa/verify` once the user proves possession.","operationId":"enrol_api_v1_2fa_enrol_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpEnrolResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/2fa/verify":{"post":{"tags":["2fa"],"summary":"Verify Enrolment","description":"Complete TOTP enrolment. Persists the secret + 10 fresh recovery\ncodes, returns the plaintext codes ONCE (the user must\ndownload/copy/print them — they are bcrypt-hashed at rest and never\nretrievable again).","operationId":"verify_enrolment_api_v1_2fa_verify_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpVerifyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpCompletionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/2fa/step-up":{"post":{"tags":["2fa"],"summary":"Step Up Totp","description":"Submit a TOTP code to refresh the access token's `step_up_at` claim.\nReturns a fresh access token usable against any step-up-gated endpoint\nfor the next STEP_UP_WINDOW_SECONDS.","operationId":"step_up_totp_api_v1_2fa_step_up_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpVerifyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpStepUpResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/2fa/step-up/recovery":{"post":{"tags":["2fa"],"summary":"Step Up Recovery Code","description":"Recovery-code fallback for step-up when the authenticator app is\nunavailable. Single-use — the code is marked consumed on match.","operationId":"step_up_recovery_code_api_v1_2fa_step_up_recovery_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecoveryCodeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpStepUpResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/2fa":{"delete":{"tags":["2fa"],"summary":"Disable","description":"Disable TOTP entirely. Wipes the secret, all recovery codes, and all\ntrusted-device records. Returns 204 No Content.","operationId":"disable_api_v1_2fa_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/2fa/recovery-codes/regenerate":{"post":{"tags":["2fa"],"summary":"Regenerate Codes","description":"Replace all recovery codes with 10 fresh ones. Invalidates every\npreviously-issued code in one shot.","operationId":"regenerate_codes_api_v1_2fa_recovery_codes_regenerate_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpCompletionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/2fa/status":{"get":{"tags":["2fa"],"summary":"Status","description":"Drives the Settings → Security panel. Returns the cached\n`totp_enabled` flag, the `verified_at` of the enrolment (or None), and\nthe count of unused recovery codes remaining.","operationId":"status_api_v1_2fa_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpStatusResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/2fa/trusted-devices":{"get":{"tags":["2fa"],"summary":"List Trusted Devices","description":"Surfaces the user's unexpired trusted devices for the Security panel.\nCookie jti + UA fingerprint are hashed at rest (D4), so only created_at\n+ expires_at are returned — enough for a \"revoke\" affordance.","operationId":"list_trusted_devices_api_v1_2fa_trusted_devices_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TrustedDeviceResponse"},"type":"array","title":"Response List Trusted Devices Api V1 2Fa Trusted Devices Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/2fa/trusted-devices/{device_id}":{"delete":{"tags":["2fa"],"summary":"Revoke Trusted Device","description":"Hard-delete one trusted-device row. The browser carrying the\nassociated cookie will be re-prompted for TOTP on its next login.\nNot step-up-gated — revoking your own device is a defensive action\nthat shouldn't require a TOTP re-prompt; consistent with the\nsign-out-everywhere pattern from M21.5.","operationId":"revoke_trusted_device_api_v1_2fa_trusted_devices__device_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"device_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Device Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts":{"get":{"tags":["contacts"],"summary":"List Contacts","operationId":"list_contacts_api_v1_contacts_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"company_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"}},{"name":"owner_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"}},{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tag"}},{"name":"excluded_from_outreach","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Excluded From Outreach"}},{"name":"untouched","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Untouched"}},{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","default":"created_at","title":"Sort By"}},{"name":"sort_dir","in":"query","required":false,"schema":{"type":"string","default":"desc","title":"Sort Dir"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ContactListItem_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["contacts"],"summary":"Create Contact","operationId":"create_contact_api_v1_contacts_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/trash":{"get":{"tags":["contacts"],"summary":"List Deleted Contacts","description":"Trash view: soft-deleted contacts (purged 30 days after deletion).","operationId":"list_deleted_contacts_api_v1_contacts_trash_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","default":"created_at","title":"Sort By"}},{"name":"sort_dir","in":"query","required":false,"schema":{"type":"string","default":"desc","title":"Sort Dir"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ContactListItem_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/find-email":{"post":{"tags":["contacts"],"summary":"Find Contact Email","description":"Resolve an email for a person (name + company/domain) via the finder waterfall\n— the review step of \"find & add a reachable contact\" (M-Find-a). Returns\ncandidate(s) + provenance and creates nothing; the user adds the chosen one via\n``POST \"\"`` with the returned ``custom_fields`` (the email value goes on\n``email``). 409 when the finder is off for the tenant (deployment\n``HUNTER_API_KEY`` × the per-tenant ``email_finder_enabled`` opt-in); 422 when\nthe name or company signal is missing; 404 for a bad ``company_id``.","operationId":"find_contact_email_api_v1_contacts_find_email_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindEmailRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindEmailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/bulk/reassign":{"post":{"tags":["contacts"],"summary":"Bulk Reassign","operationId":"bulk_reassign_api_v1_contacts_bulk_reassign_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactBulkReassignRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/bulk/delete":{"post":{"tags":["contacts"],"summary":"Bulk Delete","operationId":"bulk_delete_api_v1_contacts_bulk_delete_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactBulkDeleteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/bulk/tags":{"post":{"tags":["contacts"],"summary":"Bulk Tags","operationId":"bulk_tags_api_v1_contacts_bulk_tags_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactBulkTagsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/bulk/status":{"post":{"tags":["contacts"],"summary":"Bulk Status","operationId":"bulk_status_api_v1_contacts_bulk_status_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactBulkStatusRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/bulk/add-to-campaign":{"post":{"tags":["contacts"],"summary":"Bulk Add To Campaign","operationId":"bulk_add_to_campaign_api_v1_contacts_bulk_add_to_campaign_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactBulkAddToCampaignRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/export":{"post":{"tags":["contacts"],"summary":"Export Contacts","description":"Synchronous export of the filtered/selected contacts (RLS scopes to the\ntenant — not admin-only). ``fmt`` selects the serialization: ``csv``\n(default) or ``xlsx`` for the branded workbook; both carry the tenant's\n`custom:<key>` columns. Caps at 1000 rows newest-first; on overflow it\nreturns the slice and sets `X-Export-Truncated: true` (OQ-B cap-with-notice,\nnever 422). The download filename is derived from the entity plus the date\nand set on `Content-Disposition` for direct/API callers. Writes a\n`contact.exported` audit row.","operationId":"export_contacts_api_v1_contacts_export_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"fmt","in":"query","required":false,"schema":{"enum":["csv","xlsx"],"type":"string","default":"csv","title":"Fmt"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactExportRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/{contact_id}":{"get":{"tags":["contacts"],"summary":"Get Contact","operationId":"get_contact_api_v1_contacts__contact_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["contacts"],"summary":"Update Contact","operationId":"update_contact_api_v1_contacts__contact_id__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["contacts"],"summary":"Patch Contact","operationId":"patch_contact_api_v1_contacts__contact_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["contacts"],"summary":"Delete Contact","operationId":"delete_contact_api_v1_contacts__contact_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/{contact_id}/similar":{"get":{"tags":["contacts"],"summary":"List Similar Contacts","description":"Contacts semantically nearest the seed (cosine KNN over the embeddings store).\n\n``results`` is empty when the seed has no neighbours; ``seed_embedded`` lets\nthe caller tell that apart from a seed that simply isn't embedded yet (e.g.\nnot backfilled). The seed itself is always excluded from the results.","operationId":"list_similar_contacts_api_v1_contacts__contact_id__similar_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimilarContactsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/{contact_id}/brief":{"get":{"tags":["contacts"],"summary":"Get Contact Brief","description":"The cached AI Brief for a contact (lazy-on-read + stale-while-revalidate).\n\nServes the cached brief instantly; a stale or absent brief enqueues a\nbackground regenerate (DA1/OQ3) — the read never blocks on, nor 500s from,\ngeneration (DA2/DA8). A missing/cross-tenant contact returns 404.","operationId":"get_contact_brief_api_v1_contacts__contact_id__brief_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordBriefResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/{contact_id}/enrich":{"post":{"tags":["contacts"],"summary":"Enrich Contact Record","description":"Start an async web-research enrichment pass for a contact (M-Enrich-b).\n\nReturns a ``job_id`` to poll via ``GET …/enrich/{job_id}`` — the pass (web\nsearch + page fetches + LLM synthesis) runs off the request path. 409 when web\nresearch is disabled for the tenant (Db5); 404 when the contact is\nmissing/cross-tenant. Proposals are read-only and cited; applying one is a\nseparate, audited PATCH the review card drives (no new write path).","operationId":"enrich_contact_record_api_v1_contacts__contact_id__enrich_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrichmentJobCreated"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/{contact_id}/enrich/{job_id}":{"get":{"tags":["contacts"],"summary":"Get Contact Enrichment","description":"Poll an in-flight contact enrichment job → ``{status, result}`` (M-Enrich-b).\n\nThe path contact (RLS-checked) + an entity-match on the result are the access\nboundary, not the opaque job id (the ARQ result store isn't tenant-scoped).","operationId":"get_contact_enrichment_api_v1_contacts__contact_id__enrich__job_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrichmentJobOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/{contact_id}/merge":{"post":{"tags":["contacts"],"summary":"Merge Contact","operationId":"merge_contact_api_v1_contacts__contact_id__merge_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactMergeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/{contact_id}/restore":{"post":{"tags":["contacts"],"summary":"Restore Contact","description":"Restore a soft-deleted contact from the trash.","operationId":"restore_contact_api_v1_contacts__contact_id__restore_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/{contact_id}/send-one-off":{"post":{"tags":["contacts"],"summary":"Send One Off","description":"Fire a one-off email to this contact.\n\nCreates an implicit one-off Campaign (hidden from /campaigns by\nUX-060/3a's list filter), pre-stages an approved AI-mode draft, and\ndelegates to the existing send pipeline. Subject + body land verbatim.","operationId":"send_one_off_api_v1_contacts__contact_id__send_one_off_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OneOffSendRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OneOffSendResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/{contact_id}/retry-one-off/{outreach_record_id}":{"post":{"tags":["contacts"],"summary":"Retry One Off","description":"Retry a failed one-off send to this contact (M-SendStatus-a).\n\nRe-arms the failed OutreachRecord + its implicit one-off campaign and re-runs\nthe existing send pipeline. Idempotent: a row that already sent (or is\nmid-send) is rejected, so a double-click can't double-send.","operationId":"retry_one_off_api_v1_contacts__contact_id__retry_one_off__outreach_record_id__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"outreach_record_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Outreach Record Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OneOffSendResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/{contact_id}/campaigns":{"get":{"tags":["contacts"],"summary":"List Contact Campaigns","description":"Campaigns this contact is an explicit member of (campaign_members join).\nDistinct from outreach history; this answers 'is this contact in this\ncampaign?' as a direct fact.","operationId":"list_contact_campaigns_api_v1_contacts__contact_id__campaigns_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"null"}]}},"title":"Response List Contact Campaigns Api V1 Contacts  Contact Id  Campaigns Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/{contact_id}/notes":{"get":{"tags":["contacts"],"summary":"List Contact Notes","operationId":"list_contact_notes_api_v1_contacts__contact_id__notes_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_NoteResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["contacts"],"summary":"Create Contact Note","operationId":"create_contact_note_api_v1_contacts__contact_id__notes_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contacts/{contact_id}/notes/{note_id}":{"patch":{"tags":["contacts"],"summary":"Update Contact Note","operationId":"update_contact_note_api_v1_contacts__contact_id__notes__note_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"note_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Note Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["contacts"],"summary":"Delete Contact Note","operationId":"delete_contact_note_api_v1_contacts__contact_id__notes__note_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"note_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Note Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies":{"get":{"tags":["companies"],"summary":"List Companies","operationId":"list_companies_api_v1_companies_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}},{"name":"owner_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"}},{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","default":"created_at","title":"Sort By"}},{"name":"sort_dir","in":"query","required":false,"schema":{"type":"string","default":"desc","title":"Sort Dir"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_CompanyListItem_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["companies"],"summary":"Create Company","operationId":"create_company_api_v1_companies_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/trash":{"get":{"tags":["companies"],"summary":"List Deleted Companies","description":"Trash view: soft-deleted companies (kept until manually restored).","operationId":"list_deleted_companies_api_v1_companies_trash_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","default":"created_at","title":"Sort By"}},{"name":"sort_dir","in":"query","required":false,"schema":{"type":"string","default":"desc","title":"Sort Dir"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_CompanyListItem_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/{company_id}/merge":{"post":{"tags":["companies"],"summary":"Merge Company","description":"Merge ``source_company_id`` into this company (the survivor).","operationId":"merge_company_api_v1_companies__company_id__merge_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Company Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyMergeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/bulk/reassign":{"post":{"tags":["companies"],"summary":"Bulk Reassign","operationId":"bulk_reassign_api_v1_companies_bulk_reassign_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyBulkReassignRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/bulk/delete":{"post":{"tags":["companies"],"summary":"Bulk Delete","operationId":"bulk_delete_api_v1_companies_bulk_delete_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyBulkDeleteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/bulk/tags":{"post":{"tags":["companies"],"summary":"Bulk Tags","operationId":"bulk_tags_api_v1_companies_bulk_tags_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyBulkTagsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/export":{"post":{"tags":["companies"],"summary":"Export Companies","description":"Synchronous export of the filtered/selected companies (RLS scopes to the\ntenant). ``fmt`` selects the serialization: ``csv`` (default) or ``xlsx``\nfor the branded workbook; both carry the tenant's `custom:<key>` columns.\nCaps at 1000 rows newest-first; on overflow returns the slice +\n`X-Export-Truncated: true` (OQ-B cap-with-notice). The download filename is\nderived from the entity plus the date and set on `Content-Disposition` for\ndirect/API callers. Writes a `company.exported` audit row.","operationId":"export_companies_api_v1_companies_export_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"fmt","in":"query","required":false,"schema":{"enum":["csv","xlsx"],"type":"string","default":"csv","title":"Fmt"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyExportRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/{company_id}":{"get":{"tags":["companies"],"summary":"Get Company","operationId":"get_company_api_v1_companies__company_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Company Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["companies"],"summary":"Update Company","operationId":"update_company_api_v1_companies__company_id__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Company Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["companies"],"summary":"Patch Company","operationId":"patch_company_api_v1_companies__company_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Company Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["companies"],"summary":"Delete Company","operationId":"delete_company_api_v1_companies__company_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Company Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/{company_id}/similar":{"get":{"tags":["companies"],"summary":"List Similar Companies","description":"Companies semantically nearest the seed (cosine KNN over the embeddings store).\n\n``results`` is empty when the seed has no neighbours; ``seed_embedded`` lets\nthe caller tell that apart from a seed that simply isn't embedded yet (e.g.\nnot backfilled). The seed itself is always excluded from the results.","operationId":"list_similar_companies_api_v1_companies__company_id__similar_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Company Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimilarCompaniesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/{company_id}/brief":{"get":{"tags":["companies"],"summary":"Get Company Brief","description":"The cached AI Brief for a company (lazy-on-read + stale-while-revalidate).\n\nServes the cached brief instantly; a stale or absent brief enqueues a\nbackground regenerate (DA1/OQ3) — the read never blocks on, nor 500s from,\ngeneration (DA2/DA8). A missing/cross-tenant company returns 404.","operationId":"get_company_brief_api_v1_companies__company_id__brief_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Company Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordBriefResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/{company_id}/enrich":{"post":{"tags":["companies"],"summary":"Enrich Company Record","description":"Start an async web-research enrichment pass for a company (M-Enrich-b).\n\nReturns a ``job_id`` to poll via ``GET …/enrich/{job_id}`` — the pass (web\nsearch + page fetches + LLM synthesis) runs off the request path. 409 when web\nresearch is disabled for the tenant (Db5); 404 when the company is\nmissing/cross-tenant. Proposals are read-only and cited; applying one is a\nseparate, audited PATCH the review card drives (no new write path).","operationId":"enrich_company_record_api_v1_companies__company_id__enrich_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Company Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrichmentJobCreated"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/{company_id}/enrich/{job_id}":{"get":{"tags":["companies"],"summary":"Get Company Enrichment","description":"Poll an in-flight company enrichment job → ``{status, result}`` (M-Enrich-b).\n\nThe path company (RLS-checked) + an entity-match on the result are the access\nboundary, not the opaque job id (the ARQ result store isn't tenant-scoped).","operationId":"get_company_enrichment_api_v1_companies__company_id__enrich__job_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Company Id"}},{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrichmentJobOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/{company_id}/restore":{"post":{"tags":["companies"],"summary":"Restore Company","description":"Restore a soft-deleted company from the trash.","operationId":"restore_company_api_v1_companies__company_id__restore_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Company Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/{company_id}/contacts":{"get":{"tags":["companies"],"summary":"List Company Contacts","operationId":"list_company_contacts_api_v1_companies__company_id__contacts_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Company Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ContactListItem_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/companies/{company_id}/notes":{"get":{"tags":["companies"],"summary":"List Company Notes","operationId":"list_company_notes_api_v1_companies__company_id__notes_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Company Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_NoteResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["companies"],"summary":"Create Company Note","operationId":"create_company_note_api_v1_companies__company_id__notes_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Company Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/deals/pipeline":{"get":{"tags":["deals"],"summary":"Get Pipeline","operationId":"get_pipeline_api_v1_deals_pipeline_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/deals/pipeline/insights":{"get":{"tags":["deals"],"summary":"Get Pipeline Insights Route","description":"Board-level woven AI for the pipeline (M-PipelineAI-a, advisory).\n\nReturns an advisory \"where revenue is stuck\" summary (Redis-cached on the board\nstate; ``null`` when the board is empty or a generation degraded — the line just\nhides) plus per-card next-step chips from each open deal's cached AI brief. Fetched\nout-of-band by the board, so a cold summary call never blocks the deterministic\n``/pipeline`` paint. NEVER 500s on a generation failure.","operationId":"get_pipeline_insights_route_api_v1_deals_pipeline_insights_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"company_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineInsightsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/deals":{"get":{"tags":["deals"],"summary":"List Deals","operationId":"list_deals_api_v1_deals_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"stage","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stage"}},{"name":"owner_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"}},{"name":"contact_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Contact Id"}},{"name":"company_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"}},{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","default":"created_at","title":"Sort By"}},{"name":"sort_dir","in":"query","required":false,"schema":{"type":"string","default":"desc","title":"Sort Dir"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_DealListItem_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["deals"],"summary":"Create Deal","operationId":"create_deal_api_v1_deals_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/deals/trash":{"get":{"tags":["deals"],"summary":"List Deleted Deals","description":"Trash view: soft-deleted deals, recoverable via restore.","operationId":"list_deleted_deals_api_v1_deals_trash_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","default":"created_at","title":"Sort By"}},{"name":"sort_dir","in":"query","required":false,"schema":{"type":"string","default":"desc","title":"Sort Dir"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_DealListItem_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/deals/bulk/reassign":{"post":{"tags":["deals"],"summary":"Bulk Reassign","operationId":"bulk_reassign_api_v1_deals_bulk_reassign_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealBulkReassignRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/deals/bulk/delete":{"post":{"tags":["deals"],"summary":"Bulk Delete","operationId":"bulk_delete_api_v1_deals_bulk_delete_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealBulkDeleteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/deals/{deal_id}":{"get":{"tags":["deals"],"summary":"Get Deal","operationId":"get_deal_api_v1_deals__deal_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"deal_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Deal Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["deals"],"summary":"Update Deal","operationId":"update_deal_api_v1_deals__deal_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"deal_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Deal Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["deals"],"summary":"Delete Deal","operationId":"delete_deal_api_v1_deals__deal_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"deal_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Deal Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/deals/{deal_id}/brief":{"get":{"tags":["deals"],"summary":"Get Deal Brief","description":"The cached AI Brief for a deal (lazy-on-read + stale-while-revalidate).\n\nServes the cached brief instantly; a stale or absent brief enqueues a\nbackground regenerate (DA1/OQ3) — the read never blocks on, nor 500s from,\ngeneration (DA2/DA8). A missing/cross-tenant deal returns 404.","operationId":"get_deal_brief_api_v1_deals__deal_id__brief_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"deal_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Deal Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordBriefResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/deals/{deal_id}/merge":{"post":{"tags":["deals"],"summary":"Merge Deal","description":"Merge ``source_deal_id`` into this deal (the survivor).","operationId":"merge_deal_api_v1_deals__deal_id__merge_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"deal_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Deal Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealMergeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/deals/{deal_id}/restore":{"post":{"tags":["deals"],"summary":"Restore Deal","description":"Restore a soft-deleted deal from the trash.","operationId":"restore_deal_api_v1_deals__deal_id__restore_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"deal_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Deal Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/outreach/summary":{"get":{"tags":["outreach"],"summary":"Get Outreach Summary","description":"Aggregated outreach statistics. Pass `bucket` to populate `by_period`.","operationId":"get_outreach_summary_api_v1_outreach_summary_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Campaign Id"}},{"name":"campaign_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Convenience: human-readable campaign name. Resolved to UUID server-side. campaign_id wins if both are supplied.","title":"Campaign Name"},"description":"Convenience: human-readable campaign name. Resolved to UUID server-side. campaign_id wins if both are supplied."},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date To"}},{"name":"bucket","in":"query","required":false,"schema":{"anyOf":[{"enum":["day","week","month"],"type":"string"},{"type":"null"}],"title":"Bucket"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutreachSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/outreach":{"get":{"tags":["outreach"],"summary":"List Outreach","operationId":"list_outreach_api_v1_outreach_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Contact Id"}},{"name":"campaign_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Campaign Id"}},{"name":"campaign_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Convenience: campaign name. campaign_id wins if both set.","title":"Campaign Name"},"description":"Convenience: campaign name. campaign_id wins if both set."},{"name":"channel","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Channel"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Filter to records logged by this user.","title":"User Id"},"description":"Filter to records logged by this user."},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date To"}},{"name":"response_received","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Response Received"}},{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","default":"sent_at","title":"Sort By"}},{"name":"sort_dir","in":"query","required":false,"schema":{"type":"string","default":"desc","title":"Sort Dir"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_OutreachRecordDetail_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["outreach"],"summary":"Log Outreach","description":"Log a single outreach event against a contact.","operationId":"log_outreach_api_v1_outreach_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutreachCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutreachRecordDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/outreach/bulk":{"post":{"tags":["outreach"],"summary":"Bulk Log Outreach","description":"Upload a CSV of outreach records. Returns a job for polling.","operationId":"bulk_log_outreach_api_v1_outreach_bulk_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Default campaign name for rows that don't specify one. Resolved (or get_or_created) to a Campaign UUID server-side.","title":"Campaign Name"},"description":"Default campaign name for rows that don't specify one. Resolved (or get_or_created) to a Campaign UUID server-side."},{"name":"channel","in":"query","required":false,"schema":{"type":"string","default":"email","title":"Channel"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_bulk_log_outreach_api_v1_outreach_bulk_post"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkOutreachJobStatus"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/outreach/bulk/{job_id}":{"get":{"tags":["outreach"],"summary":"Get Bulk Job Status","description":"Poll the status of a bulk outreach upload job.","operationId":"get_bulk_job_status_api_v1_outreach_bulk__job_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkOutreachJobStatus"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/outreach/threads":{"get":{"tags":["outreach"],"summary":"List Contact Conversation Threads","description":"Per-contact conversation feed (GAP-031): sent rows + their inbound reply\nchildren, grouped into threads and ordered most-recently-active first. The\ncontact's Outreach tab is a flat metadata list; this renders the actual\nback-and-forth with bodies. Read-only. 404 if the contact isn't in-tenant.","operationId":"list_contact_conversation_threads_api_v1_outreach_threads_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"contact_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Contact whose conversation threads to list. Required.","title":"Contact Id"},"description":"Contact whose conversation threads to list. Required."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Threads per page.","default":20,"title":"Limit"},"description":"Threads per page."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque keyset cursor — pass the previous page's next_cursor.","title":"Cursor"},"description":"Opaque keyset cursor — pass the previous page's next_cursor."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationThreadPage"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/outreach/{record_id}":{"get":{"tags":["outreach"],"summary":"Get Outreach Record","operationId":"get_outreach_record_api_v1_outreach__record_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"record_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Record Id"}},{"name":"include_thread","in":"query","required":false,"schema":{"type":"boolean","description":"M31b2.5 (UX-019). When true, the response includes a `thread` field with the sent row + inbound reply children, normalised around the sent row regardless of which row id was queried. Default off keeps the response byte-identical to pre-M31b2.5 callers.","default":false,"title":"Include Thread"},"description":"M31b2.5 (UX-019). When true, the response includes a `thread` field with the sent row + inbound reply children, normalised around the sent row regardless of which row id was queried. Default off keeps the response byte-identical to pre-M31b2.5 callers."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutreachRecordDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["outreach"],"summary":"Update Outreach Response","description":"Update mutable response-tracking fields. Immutable fields are rejected.","operationId":"update_outreach_response_api_v1_outreach__record_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"record_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Record Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutreachUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutreachRecordDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/outreach/{record_id}/outcome":{"post":{"tags":["outreach"],"summary":"Apply Outreach Outcome","description":"Apply a user-supplied outcome to an outreach record.\n\nWraps M27c's `apply_reply_outcome` orchestrator for the\nOutreachDetailPanel's quick-action buttons (Mark customer / Mark\ndisqualified / Unsubscribe). Looks up the record to confirm tenant\nmembership (cross-tenant access returns 404) + read `contact_id`,\nthen routes through the same orchestrator the ingestion path uses.\n\n`received_at` is set to now — the user is asserting an outcome at\ninvocation time, not back-dating to when the reply originally\narrived. `reply_message_id` is left null (the user isn't supplying\na Gmail message-id); the orchestrator's INSERT therefore writes a\nfresh inbound row each call. The frontend is responsible for\ndisabling the button after a successful response to avoid duplicate\nuser-override rows.","operationId":"apply_outreach_outcome_api_v1_outreach__record_id__outcome_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"record_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Record Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyOutcomeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyOutcomeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/grounded-drafts/compose":{"post":{"tags":["grounded-drafts"],"summary":"Compose Grounded Draft Route","description":"Compose a grounded email draft for a contact (review-before-send).\n\n`subject`/`body` are null when composition fails closed (malformed model\noutput after one retry) — the card shows `explanation` and the user falls back\nto the manual composer. 404 if the contact is not visible to this tenant.","operationId":"compose_grounded_draft_route_api_v1_grounded_drafts_compose_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroundedDraftRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroundedDraftResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/company-profile":{"get":{"tags":["company-profile"],"summary":"Get Company Profile","description":"Return the tenant's company profile + provenance, or an empty shell if unset.\n\nA never-derived tenant gets 200 with empty fields so the Settings surface shows an\nempty state with a \"Derive from the web\" trigger (not a 404).","operationId":"get_company_profile_api_v1_company_profile_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyProfileResponse"}}}}}},"patch":{"tags":["company-profile"],"summary":"Update Company Profile","description":"Save a user-edited company profile (whole-profile replace → ``source='edited'``).\n\nThe Settings UI holds the full profile and saves it back; ``ai_updates_enabled`` +\n``source_links`` ride along when present. Audit-logged (changed keys only).","operationId":"update_company_profile_api_v1_company_profile_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyProfileUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/company-profile/derive":{"post":{"tags":["company-profile"],"summary":"Derive Company Profile Route","description":"Start an async company-profile derivation from owner-supplied web sources + paste.\n\nReturns a ``job_id`` to poll via ``GET …/derive/{job_id}`` — the bounded multi-page\nfetch + LLM distill runs off the request path. 409 when web research is disabled\n(``COMPANY_PROFILE_WEB_SEARCH_DISABLED``) or no source was supplied\n(``COMPANY_PROFILE_NO_SOURCES``).","operationId":"derive_company_profile_route_api_v1_company_profile_derive_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyProfileDeriveRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyProfileDerivationJobCreated"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/company-profile/derive/{job_id}":{"get":{"tags":["company-profile"],"summary":"Get Company Profile Derivation","description":"Poll an in-flight company-profile derivation → ``{status, profile}``.\n\nThe caller's (RLS-authenticated) tenant + a tenant-match on the result envelope are\nthe access boundary, not the opaque job id (the ARQ result store isn't tenant-scoped).\n``done`` means the profile is already persisted; ``profile`` echoes it so the UI\nrenders without a second GET.","operationId":"get_company_profile_derivation_api_v1_company_profile_derive__job_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyProfileDerivationJobOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates/discover":{"post":{"tags":["prospect-candidates"],"summary":"Discover","description":"Start an async ICP-scored web discovery run (M-Prospect-c).\n\nReturns a ``job_id`` to poll via ``GET …/discover/{job_id}`` — the pass (web\nsearch → fetch → LLM scoring → coverage-dedup) runs off the request path. It is a\npure read: it returns a scored batch for review, never a write. 409 when web\nresearch is disabled (``FIND_PROSPECTS_WEB_SEARCH_DISABLED``) or there is no ICP\nto score against (``FIND_PROSPECTS_NO_ICP``).","operationId":"discover_api_v1_prospect_candidates_discover_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindProspectsRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindProspectsJobCreated"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates/discover/{job_id}":{"get":{"tags":["prospect-candidates"],"summary":"Get Discovery","description":"Poll an in-flight discovery run → ``{status, result}`` (M-Prospect-c).\n\nThe caller's (RLS-authenticated) tenant + a tenant-match on the result envelope\nare the access boundary, not the opaque job id (the ARQ result store isn't\ntenant-scoped). ``done`` carries the scored, deduped batch so the UI renders it\nwithout a second call.","operationId":"get_discovery_api_v1_prospect_candidates_discover__job_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindProspectsJobOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates/stage":{"post":{"tags":["prospect-candidates"],"summary":"Stage Prospects","operationId":"stage_prospects_api_v1_prospect_candidates_stage_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StageProspectsRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StageProspectsResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates/bulk-approve":{"post":{"tags":["prospect-candidates"],"summary":"Bulk Approve","operationId":"bulk_approve_api_v1_prospect_candidates_bulk_approve_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkApproveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkApproveResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates/promote":{"post":{"tags":["prospect-candidates"],"summary":"Promote","operationId":"promote_api_v1_prospect_candidates_promote_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromoteProspectsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromoteProspectsResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates/people/discover":{"post":{"tags":["prospect-candidates"],"summary":"Discover People","description":"Start an async people-discovery run (M-Prospecting-b).\n\nWeb-discovers role/title-targeted decision-makers at the target companies\n(in-review candidates or CRM companies) and scores them, with the same\ncode-enforced evidence discipline as company discovery. Returns a ``job_id``\nto poll via ``GET …/people/discover/{job_id}``. Pure read — the batch is\nstaged separately. 409 when web research is disabled\n(``FIND_PEOPLE_WEB_SEARCH_DISABLED``); 404 when a target doesn't resolve.","operationId":"discover_people_api_v1_prospect_candidates_people_discover_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindPeopleRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindPeopleJobCreated"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates/people/discover/{job_id}":{"get":{"tags":["prospect-candidates"],"summary":"Get People Discovery","description":"Poll an in-flight people-discovery run → ``{status, result}``.\n\nThe caller's (RLS-authenticated) tenant + a tenant-match on the result\nenvelope are the access boundary (the ARQ result store isn't tenant-scoped).\n``done`` carries the scored, deduped people batch.","operationId":"get_people_discovery_api_v1_prospect_candidates_people_discover__job_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindPeopleJobOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates/people/stage":{"post":{"tags":["prospect-candidates"],"summary":"Stage People","operationId":"stage_people_api_v1_prospect_candidates_people_stage_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StagePeopleRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StagePeopleResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates/people/bulk-approve":{"post":{"tags":["prospect-candidates"],"summary":"Bulk Approve People","operationId":"bulk_approve_people_api_v1_prospect_candidates_people_bulk_approve_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PeopleBulkApproveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PeopleBulkApproveResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates/people/promote":{"post":{"tags":["prospect-candidates"],"summary":"Promote People","description":"Promote the approved people in a batch to reachable CRM contacts.\n\nReachability is hybrid: rows holding an email (web-evidence capture or a\nreviewer-pasted address) promote without any finder call; rows without one\nrun the billed email-finder waterfall (409 ``EMAIL_FINDER_DISABLED`` up-front\nif the finder is off while any row needs it). Misses are recorded as\n``not_found`` and stay approved; a re-promote skips them.","operationId":"promote_people_api_v1_prospect_candidates_people_promote_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromotePeopleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromotePeopleResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates/people":{"get":{"tags":["prospect-candidates"],"summary":"List People","operationId":"list_people_api_v1_prospect_candidates_people_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"batch_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Batch Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"company_candidate_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Candidate Id"}},{"name":"company_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectPersonList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates/people/{person_id}":{"get":{"tags":["prospect-candidates"],"summary":"Get Person","operationId":"get_person_api_v1_prospect_candidates_people__person_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"person_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Person Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectPersonRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["prospect-candidates"],"summary":"Patch Person","operationId":"patch_person_api_v1_prospect_candidates_people__person_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"person_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Person Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectPersonPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectPersonRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates":{"get":{"tags":["prospect-candidates"],"summary":"List Candidates","operationId":"list_candidates_api_v1_prospect_candidates_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"batch_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Batch Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectCandidateList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prospect-candidates/{candidate_id}":{"get":{"tags":["prospect-candidates"],"summary":"Get Candidate","operationId":"get_candidate_api_v1_prospect_candidates__candidate_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"candidate_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Candidate Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectCandidateRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["prospect-candidates"],"summary":"Patch Candidate","operationId":"patch_candidate_api_v1_prospect_candidates__candidate_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"candidate_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Candidate Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectCandidatePatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectCandidateRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/product-analytics/events":{"post":{"tags":["product-analytics"],"summary":"Ingest Event","description":"Accept one client-originated product-analytics event (fire-and-forget).\n\nReturns 202 unconditionally on a well-formed, allowlisted request — including\nwhen analytics is globally disabled or the tenant has opted out (the facade /\ndelivery task no-op silently). A malformed or off-allowlist body is rejected\nwith 422 by ``ProductAnalyticsEventIn`` before reaching here.","operationId":"ingest_event_api_v1_product_analytics_events_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductAnalyticsEventIn"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/campaigns":{"get":{"tags":["campaigns"],"summary":"List Campaigns","description":"Paginated list of all campaigns for this tenant (newest first).\n\nUX-004 — rows include derived `member_count`, `sent_count`,\n`last_sent_at` so the index page renders without a per-row fetch.\nOptional `send_status` filter narrows to one funnel stage\n(`idle`, `generating`, `ready_to_send`, `sending`, `completed`, …).","operationId":"list_campaigns_api_v1_campaigns_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"send_status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Send Status"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_CampaignSummary_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["campaigns"],"summary":"Create Campaign","description":"Create a campaign by name. 409 if a campaign with that name already\nexists for this tenant.","operationId":"create_campaign_api_v1_campaigns_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}":{"get":{"tags":["campaigns"],"summary":"Get Campaign","description":"Detail view including derived member_count, outreach_count, last_sent_at.","operationId":"get_campaign_api_v1_campaigns__campaign_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["campaigns"],"summary":"Update Campaign","description":"Partial update. Mutable surfaces:\n  - `name` — 409 on collision; never explicit-null (column is non-null).\n  - `tracking_enabled` — M28 override; `null` means \"inherit tenant\".\n  - `compose_mode` — M29b; `null` clears, `'template'|'ai_generated'`\n    set the wizard mode.\n  - `description` — M29b AI brief; `null` clears.\n  - `system_prompt_override` — M29b per-campaign override of the\n    tenant's `ai_personalization.system_prompt`; `null` resets.\n\nAll nullable fields use the M26c `*_set: bool` pattern so omitting a\nkey leaves the column alone while sending `null` is a distinct\n\"clear\" write. Outreach attribution is preserved (FK is the UUID,\nnot the name).","operationId":"update_campaign_api_v1_campaigns__campaign_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["campaigns"],"summary":"Delete Campaign","description":"Delete a campaign. 409 if any outreach record still references it.\nMembers cascade automatically.","operationId":"delete_campaign_api_v1_campaigns__campaign_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/members":{"get":{"tags":["campaigns"],"summary":"List Members","description":"Paginated contacts that are members of this campaign.","operationId":"list_members_api_v1_campaigns__campaign_id__members_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_CampaignMemberContact_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["campaigns"],"summary":"Add Members","description":"Bulk-add contacts to a campaign. Idempotent — already-member contacts\nare skipped, not erroneous. Cross-tenant contact_ids surface in errors.","operationId":"add_members_api_v1_campaigns__campaign_id__members_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignMemberAdd"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMembersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/members/{contact_id}":{"delete":{"tags":["campaigns"],"summary":"Remove Member","description":"Remove a single contact from a campaign. Idempotent by default — a\nnon-member returns 204. Pass `?_strict=true` to surface 404 instead.","operationId":"remove_member_api_v1_campaigns__campaign_id__members__contact_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"_strict","in":"query","required":false,"schema":{"type":"boolean","description":"If true, 404 when not a member.","default":false,"title":" Strict"},"description":"If true, 404 when not a member."},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/recipients":{"get":{"tags":["campaigns"],"summary":"List Recipients","description":"Paginated per-contact recipients with multi-source attribution.","operationId":"list_recipients_api_v1_campaigns__campaign_id__recipients_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientsListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/recipients/summary":{"get":{"tags":["campaigns"],"summary":"Get Recipients Summary","description":"Header-line counts + per-source chip data. Faster than /recipients\nbecause no per-row enumeration.","operationId":"get_recipients_summary_api_v1_campaigns__campaign_id__recipients_summary_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientsSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/sources/lists":{"post":{"tags":["campaigns"],"summary":"Add List Source","description":"Attach a prospect list as a campaign-recipient source. Auto-promotes\neligible prospect-stage list items into contacts.","operationId":"add_list_source_api_v1_campaigns__campaign_id__sources_lists_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddListSourceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientAddResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/sources/segments":{"post":{"tags":["campaigns"],"summary":"Add Segment Source","description":"Attach a saved segment as a campaign-recipient source. Snapshots\nmatching contacts at add-time.","operationId":"add_segment_source_api_v1_campaigns__campaign_id__sources_segments_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddSegmentSourceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientAddResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/sources/contacts":{"post":{"tags":["campaigns"],"summary":"Add Contacts Source","description":"Manual contact selection. Capped at 500 ids per call (422 above that).","operationId":"add_contacts_source_api_v1_campaigns__campaign_id__sources_contacts_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddContactsSourceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientAddResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/sources/{kind}/{ref_id}":{"delete":{"tags":["campaigns"],"summary":"Remove Typed Source","description":"Remove every row attributed to this list-source or segment-source.\nMulti-source contacts survive via their other source-rows. For removing\nALL manual rows in one shot, use DELETE /sources/manual (no ref_id).","operationId":"remove_typed_source_api_v1_campaigns__campaign_id__sources__kind___ref_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"kind","in":"path","required":true,"schema":{"type":"string","title":"Kind"}},{"name":"ref_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Ref Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/sources/manual":{"delete":{"tags":["campaigns"],"summary":"Remove All Manual Sources","description":"Clear every manual-source row from the campaign in one shot.","operationId":"remove_all_manual_sources_api_v1_campaigns__campaign_id__sources_manual_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/recipients/{contact_id}":{"delete":{"tags":["campaigns"],"summary":"Remove Recipient","description":"Remove a single contact from the campaign — deletes ALL source-rows\nfor that contact (list/segment/manual). Independent of source. Idempotent:\na non-member returns 204.","operationId":"remove_recipient_api_v1_campaigns__campaign_id__recipients__contact_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Contact Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/send/preview":{"post":{"tags":["campaigns"],"summary":"Preview Campaign Send","description":"Read-only pre-flight maths for the compose-UI confirmation modal.\n\nReturns the auto-spread schedule the user would see (total\nrecipients, today's quota capacity, per-day overflow, estimated\ncompletion at). No DB writes, no audit rows, no enqueue.","operationId":"preview_campaign_send_api_v1_campaigns__campaign_id__send_preview_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignSendPreview"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignSendPlanResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/send/start":{"post":{"tags":["campaigns"],"summary":"Start Send","description":"Kick off the send pipeline.\n\nValidates that the campaign is in a startable state, flips it to\n'sending', writes the audit chain, and enqueues one\n`send_campaign_message` ARQ task per eligible recipient with\ntrickle pacing + daily-quota auto-spread.\n\nM29c: AI-mode campaigns are gated on every draft being approved.\nAn unapproved-draft breach surfaces an explicit 422 with\n`details = {reason: 'unapproved_drafts', count: N}` so the review\nUI can deep-link to the unapproved set.","operationId":"start_send_api_v1_campaigns__campaign_id__send_start_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignSendStart"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignSendPlanResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/send/test":{"post":{"tags":["campaigns"],"summary":"Send Test","description":"Send a fully-rendered test copy of the campaign to your own inbox (GAP-072).\n\nRuns the same render pipeline as a real send (merge vars, signature/logo,\ntracking, the unsubscribe footer + postal address) and delivers one message\nto the authenticated user's own email, synchronously. Side-effects are inert\n(tracking + unsubscribe bound to throwaway ids), so a self-test never touches\na real contact or the campaign's stats — no quota, no OutreachRecord, no new\ncapability. The service raises the ownership (403/404/409), validation (422),\nand provider (502) errors the global handler formats.","operationId":"send_test_api_v1_campaigns__campaign_id__send_test_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignTestSendRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignTestSendResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/send/pause":{"post":{"tags":["campaigns"],"summary":"Pause Send","description":"sending → paused. Deferred tasks self-skip on fire.","operationId":"pause_send_api_v1_campaigns__campaign_id__send_pause_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/send/resume":{"post":{"tags":["campaigns"],"summary":"Resume Send","description":"paused → sending. Re-enqueues members not yet sent.","operationId":"resume_send_api_v1_campaigns__campaign_id__send_resume_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignSendStart"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignSendPlanResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/cost-estimate":{"get":{"tags":["campaigns"],"summary":"Cost Estimate","description":"Read-only pre-flight cost estimate for the AI-mode compose UI.\n\nMirrors what `POST /generate-drafts` runs as its first step but\nnever touches `send_status` and never enqueues anything. The UI\npolls this whenever the campaign's description or member list\nchanges so the user sees a live \"≈ $X.XX for N drafts\" figure\nbefore committing.\n\nSurfaces:\n  - 404 if the campaign doesn't exist for this tenant.\n  - 422 if the campaign isn't in AI mode, has no description, or\n    has no members.","operationId":"cost_estimate_api_v1_campaigns__campaign_id__cost_estimate_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostEstimatePayload"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/generate-drafts":{"post":{"tags":["campaigns"],"summary":"Generate Drafts","description":"Kick off per-recipient AI-personalized body generation.\n\nCalls `personalization.start_batch_generation`, which atomically\ntransitions `send_status` from `idle → generating`, writes the\n`campaign.generation_started` audit row, and enqueues\n`app.tasks.personalization.run_batch_generation`.\n\nResponses:\n  - 200 on success (returns recipient count + cost estimate).\n  - 409 if another generation is already running (caught upstream\n    by the global `LimenaError` handler via\n    `BatchAlreadyInProgressError` → `ConflictError`).\n  - 422 if the cost cap would be exceeded — explicit JSONResponse\n    carries `details.estimate` so the UI can show the breach\n    amount inline (the global handler returns `details={}`, which\n    would lose the payload).\n  - 422 if the campaign isn't in AI mode, lacks a description, or\n    has no members (`ValidationError` via the global handler).\n  - 404 if the campaign doesn't exist for this tenant\n    (`NotFoundError` via the global handler).","operationId":"generate_drafts_api_v1_campaigns__campaign_id__generate_drafts_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateDraftsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/generation/reset":{"post":{"tags":["campaigns"],"summary":"Reset Generation","description":"Flip `send_status='generation_failed' → 'idle'` so the compose\nUI can let the user retry generation or edit the brief and start\nover. Only allowed from `generation_failed` — any other state\nsurfaces 409 to prevent corrupting a live send / completed\ngeneration.\n\nAtomic via `UPDATE … WHERE send_status='generation_failed' RETURNING\nid` for replay-idempotency under concurrent clicks (same precedent\nas M27b's webhook guard).","operationId":"reset_generation_api_v1_campaigns__campaign_id__generation_reset_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/send/cancel":{"post":{"tags":["campaigns"],"summary":"Cancel Send","description":"sending|paused → cancelled. Existing sends remain attributed.","operationId":"cancel_send_api_v1_campaigns__campaign_id__send_cancel_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/drafts":{"get":{"tags":["campaigns"],"summary":"List Drafts","description":"Paginated review queue for an AI-mode campaign.","operationId":"list_drafts_api_v1_campaigns__campaign_id__drafts_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"approved","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filter the queue. None = all drafts; true = approved only; false = unapproved only.","title":"Approved"},"description":"Filter the queue. None = all drafts; true = approved only; false = unapproved only."},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_DraftListItem_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/drafts/{record_id}":{"get":{"tags":["campaigns"],"summary":"Get Draft","description":"Full draft body for the inline editor.","operationId":"get_draft_api_v1_campaigns__campaign_id__drafts__record_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"record_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Record Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["campaigns"],"summary":"Patch Draft","description":"Inline edit + approve toggle.\n\n`body_full` and `subject` follow the M26c `*_set: bool` companion\npattern — Pydantic's `model_fields_set` distinguishes \"client sent\nnull\" (explicit clear) from \"client omitted the key\" (no-op).","operationId":"patch_draft_api_v1_campaigns__campaign_id__drafts__record_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"record_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Record Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/drafts/{record_id}/regenerate":{"post":{"tags":["campaigns"],"summary":"Regenerate Draft","description":"Re-run the LLM on one draft. Blocked at 409 while the campaign\nis mid-generation (Q8 lock).","operationId":"regenerate_draft_api_v1_campaigns__campaign_id__drafts__record_id__regenerate_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"record_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Record Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/drafts/approve-all":{"post":{"tags":["campaigns"],"summary":"Approve All Drafts","description":"Flip every unapproved draft to approved. Per-record audits +\none roll-up audit.","operationId":"approve_all_drafts_api_v1_campaigns__campaign_id__drafts_approve_all_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkApproveResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/send/approvals":{"get":{"tags":["campaigns"],"summary":"List Send Approvals","operationId":"list_send_approvals_api_v1_campaigns__campaign_id__send_approvals_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SendApprovalResponse"},"title":"Response List Send Approvals Api V1 Campaigns  Campaign Id  Send Approvals Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/send/approvals/{approval_id}/approve":{"post":{"tags":["campaigns"],"summary":"Approve Send Request","operationId":"approve_send_request_api_v1_campaigns__campaign_id__send_approvals__approval_id__approve_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"approval_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Approval Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendApprovalApproveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignSendPlanResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/campaigns/{campaign_id}/send/approvals/{approval_id}/reject":{"post":{"tags":["campaigns"],"summary":"Reject Send Request","operationId":"reject_send_request_api_v1_campaigns__campaign_id__send_approvals__approval_id__reject_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Campaign Id"}},{"name":"approval_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Approval Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendApprovalRejectRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendApprovalResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/lists":{"post":{"tags":["prospect-lists"],"summary":"Create Prospect List","description":"Upload a CSV or Excel (`.xlsx`/`.xls`) prospect list and enqueue dedup processing.\n\n`upload_intent` (M25) makes the post-processing flow a contract: when set,\n`run_intent_action` runs after dedup completes and either fires a\nnotification (`clean_only`), auto-promotes safe rows (`promote_to_contacts`),\nor creates a campaign + memberships (`use_for_outreach`). When NULL the\nlist lands in `status='ready'` and the user drives the next step manually\nvia the list-detail action bar.","operationId":"create_prospect_list_api_v1_lists_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_create_prospect_list_api_v1_lists_post"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectListDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["prospect-lists"],"summary":"List Prospect Lists","description":"Paginated list of all prospect lists for this tenant.","operationId":"list_prospect_lists_api_v1_lists_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ProspectListSummary_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/lists/preview":{"post":{"tags":["prospect-lists"],"summary":"Preview Prospect List","description":"Server-side preview of an uploaded list file (M-Ingest-c, GAP-164).\n\nReturns the detected (or `header_row`-overridden) header row + sample rows so\nthe upload modal can show + let the user correct which row is the header\nbefore processing. Read-only: the file is parsed in-memory, never stored.","operationId":"preview_prospect_list_api_v1_lists_preview_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_preview_prospect_list_api_v1_lists_preview_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListPreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/lists/preview/suggest-ai":{"post":{"tags":["prospect-lists"],"summary":"Suggest Prospect List Columns Ai","description":"Opt-in AI gap-fill for the Lists column mapping (M-Ingest-e).\n\nFills columns the deterministic alias + rapidfuzz floor left unmapped with\ntenant-LLM suggestions (contacts entity). Read-only + stateless — the file is\nre-parsed, never stored, and the human still confirms the mapping at upload.\nDegrades to the deterministic baseline (`ai_available=False`) when the tenant\nLLM is unavailable; never 500s on an AI failure. The Lists twin of the wizard's\nPOST /imports/{id}/suggest-ai.","operationId":"suggest_prospect_list_columns_ai_api_v1_lists_preview_suggest_ai_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_suggest_prospect_list_columns_ai_api_v1_lists_preview_suggest_ai_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListAiSuggestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/lists/trash":{"get":{"tags":["prospect-lists"],"summary":"List Deleted Prospect Lists","description":"Trash view: soft-deleted prospect lists (M-ListDelete-a, GAP-067).\n\nKept indefinitely (no purge) — restoring a list returns it to the live index\nwith its items + stored file intact.","operationId":"list_deleted_prospect_lists_api_v1_lists_trash_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ProspectListSummary_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/lists/{list_id}/export":{"get":{"tags":["prospect-lists"],"summary":"Export Prospect List","description":"Download a processed prospect list as CSV or a formatted ``.xlsx``.\n\n``disposition`` (repeatable) filters the exported rows. ``fmt`` selects the\nserialization: ``csv`` (default) or ``xlsx`` for the branded workbook. The\ndownload filename is derived from the list name plus the date (GAP-174), and\nset on ``Content-Disposition`` for direct/API callers.","operationId":"export_prospect_list_api_v1_lists__list_id__export_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"list_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"List Id"}},{"name":"disposition","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"title":"Disposition"}},{"name":"fmt","in":"query","required":false,"schema":{"enum":["csv","xlsx"],"type":"string","default":"csv","title":"Fmt"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/lists/{list_id}/items":{"get":{"tags":["prospect-lists"],"summary":"List Prospect List Items","description":"Paginated items for a prospect list, optionally filtered by disposition.\n\nThe `disposition` query param may be repeated to filter by multiple values\n(e.g. `?disposition=safe_to_email&disposition=duplicate`). Omit to return all.","operationId":"list_prospect_list_items_api_v1_lists__list_id__items_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"list_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"List Id"}},{"name":"disposition","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"title":"Disposition"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ProspectListItemDetail_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/lists/{list_id}/promote_to_contacts":{"post":{"tags":["prospect-lists"],"summary":"Promote List To Contacts Route","description":"Promote eligible (safe-to-email) items on this list to contacts.\n\nSelection: `scope='filter'` with optional disposition filter, or\n`scope='items'` with an explicit `item_ids` list. Already-existing contacts\nare reported as `already_existed`; non-eligible items as `skipped` with\nper-item reasons in `errors`.","operationId":"promote_list_to_contacts_route_api_v1_lists__list_id__promote_to_contacts_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"list_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"List Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromoteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromoteResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/lists/{list_id}/use_for_outreach":{"post":{"tags":["prospect-lists"],"summary":"Use List For Outreach Route","description":"Compose contact promotion with campaign membership in one transaction.\n\nThe campaign is resolved via get-or-create on `(tenant_id, campaign_name)`.\nMember rows reference the list via `added_via_list_id`. No outreach rows\nare written — sending happens later via the existing M7 outreach paths.","operationId":"use_list_for_outreach_route_api_v1_lists__list_id__use_for_outreach_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"list_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"List Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UseForOutreachRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UseForOutreachResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/lists/{list_id}/restore":{"post":{"tags":["prospect-lists"],"summary":"Restore Prospect List","description":"Restore a soft-deleted prospect list from the trash (M-ListDelete-a).","operationId":"restore_prospect_list_api_v1_lists__list_id__restore_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"list_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"List Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/lists/{list_id}":{"get":{"tags":["prospect-lists"],"summary":"Get Prospect List","description":"Get a prospect list by ID. Poll to track processing status.","operationId":"get_prospect_list_api_v1_lists__list_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"list_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"List Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectListDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["prospect-lists"],"summary":"Delete Prospect List","description":"Soft-delete a prospect list — send it to Trash (M-ListDelete-a, GAP-067).\n\nRecoverable from Settings → Trash. Deletable in any status (a stuck/failed/\nthrowaway upload can be cleared, not just a ``ready`` one).","operationId":"delete_prospect_list_api_v1_lists__list_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"list_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"List Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/segments/preview":{"post":{"tags":["segments"],"summary":"Preview Segment Route","description":"Return match count + the first 20 contacts for a given segment query.","operationId":"preview_segment_route_api_v1_segments_preview_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SegmentPreviewRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SegmentPreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/segments/build-list":{"post":{"tags":["segments"],"summary":"Build Segment List Route","description":"Materialise the matched contact set as a new ProspectList.\n\nSegment-built lists land in `status='ready'` immediately with\n`source='segment'` and `upload_intent=NULL`. The user picks an action\n(promote / use for outreach / export) from list-detail.","operationId":"build_segment_list_route_api_v1_segments_build_list_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SegmentBuildListRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SegmentBuildListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/segments/describe":{"post":{"tags":["segments"],"summary":"Describe Segment Route","description":"Compose a SegmentQuery from a natural-language description (GAP-057).\n\nReview-before-build: returns the structured query for the builder to render\nand the user to confirm into the editor — it does NOT run the segment or\nsave anything. `query` is null when the description can't be expressed with\nthe available contact fields.","operationId":"describe_segment_route_api_v1_segments_describe_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SegmentDescribeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SegmentDescribeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/segments":{"post":{"tags":["segments"],"summary":"Create Saved Segment Route","operationId":"create_saved_segment_route_api_v1_segments_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedSegmentCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedSegmentRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["segments"],"summary":"List Saved Segments Route","operationId":"list_saved_segments_route_api_v1_segments_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_SavedSegmentRead_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/segments/{segment_id}":{"get":{"tags":["segments"],"summary":"Get Saved Segment Route","operationId":"get_saved_segment_route_api_v1_segments__segment_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"segment_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Segment Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedSegmentRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["segments"],"summary":"Update Saved Segment Route","operationId":"update_saved_segment_route_api_v1_segments__segment_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"segment_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Segment Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedSegmentUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedSegmentRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["segments"],"summary":"Delete Saved Segment Route","operationId":"delete_saved_segment_route_api_v1_segments__segment_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"segment_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Segment Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/saved-table-views":{"post":{"tags":["saved-table-views"],"summary":"Create Saved Table View Route","operationId":"create_saved_table_view_route_api_v1_saved_table_views_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedTableViewCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedTableViewRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["saved-table-views"],"summary":"List Saved Table Views Route","operationId":"list_saved_table_views_route_api_v1_saved_table_views_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"entity_type","in":"query","required":false,"schema":{"anyOf":[{"enum":["contact","company","outreach"],"type":"string"},{"type":"null"}],"title":"Entity Type"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedTableViewList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/saved-table-views/{view_id}":{"get":{"tags":["saved-table-views"],"summary":"Get Saved Table View Route","operationId":"get_saved_table_view_route_api_v1_saved_table_views__view_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"view_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"View Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedTableViewRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["saved-table-views"],"summary":"Update Saved Table View Route","operationId":"update_saved_table_view_route_api_v1_saved_table_views__view_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"view_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"View Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedTableViewUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedTableViewRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["saved-table-views"],"summary":"Delete Saved Table View Route","operationId":"delete_saved_table_view_route_api_v1_saved_table_views__view_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"view_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"View Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/email-templates/describe":{"post":{"tags":["email-templates"],"summary":"Describe Template Route","description":"Compose an email-template draft from a natural-language description (GAP-057).\n\nReview-before-emit: returns the structured draft for the editor to render and\nthe user to confirm into the form — it does NOT save the template or send\nanything. `draft` is null when the description can't be turned into an email.","operationId":"describe_template_route_api_v1_email_templates_describe_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateDraftRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateDraftResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/email-templates":{"post":{"tags":["email-templates"],"summary":"Create Template Route","operationId":"create_template_route_api_v1_email_templates_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["email-templates"],"summary":"List Templates Route","operationId":"list_templates_route_api_v1_email_templates_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"folder","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by folder. Omit for all templates; pass `__uncategorised__` to filter to folder IS NULL; pass any other string for an exact match.","title":"Folder"},"description":"Filter by folder. Omit for all templates; pass `__uncategorised__` to filter to folder IS NULL; pass any other string for an exact match."},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_EmailTemplateListItem_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/email-templates/folders":{"get":{"tags":["email-templates"],"summary":"List Folders Route","operationId":"list_folders_route_api_v1_email_templates_folders_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"type":"string"},"type":"array","title":"Response List Folders Route Api V1 Email Templates Folders Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/email-templates/{template_id}":{"get":{"tags":["email-templates"],"summary":"Get Template Route","operationId":"get_template_route_api_v1_email_templates__template_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Template Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["email-templates"],"summary":"Update Template Route","operationId":"update_template_route_api_v1_email_templates__template_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Template Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["email-templates"],"summary":"Delete Template Route","operationId":"delete_template_route_api_v1_email_templates__template_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Template Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/email-templates/{template_id}/duplicate":{"post":{"tags":["email-templates"],"summary":"Duplicate Template Route","operationId":"duplicate_template_route_api_v1_email_templates__template_id__duplicate_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Template Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailTemplateRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workflows":{"post":{"tags":["workflows"],"summary":"Create Workflow Rule","operationId":"create_workflow_rule_api_v1_workflows_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowRuleCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowRuleRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["workflows"],"summary":"List Workflow Rules","operationId":"list_workflow_rules_api_v1_workflows_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"trigger_entity","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trigger Entity"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowRuleList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workflows/compile":{"post":{"tags":["workflows"],"summary":"Compile Workflow Rule","description":"Compile a natural-language request into an *unsaved* rule draft (DA3).\n\nDoes not persist: the human reviews/edits the returned draft and saves it via\nPOST /workflows. Failures + ambiguities come back as `warnings`, not errors.","operationId":"compile_workflow_rule_api_v1_workflows_compile_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompileRuleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompiledRuleDraftRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workflows/dry-run":{"post":{"tags":["workflows"],"summary":"Dry Run Workflow Draft","description":"Test an *unsaved* draft against one record without committing (UX-013).\n\nThe builder's test-before-save, mirroring /compile: the draft is validated as\na full rule (the request schema), then evaluated against `resource_id` (or the\nmost-recent record of its trigger entity). Nothing is mutated, sent, or logged.","operationId":"dry_run_workflow_draft_api_v1_workflows_dry_run_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DryRunDraftRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DryRunResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workflows/{rule_id}":{"get":{"tags":["workflows"],"summary":"Get Workflow Rule","operationId":"get_workflow_rule_api_v1_workflows__rule_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Rule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowRuleRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["workflows"],"summary":"Update Workflow Rule","operationId":"update_workflow_rule_api_v1_workflows__rule_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Rule Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowRuleUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowRuleRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["workflows"],"summary":"Delete Workflow Rule","operationId":"delete_workflow_rule_api_v1_workflows__rule_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Rule Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workflows/{rule_id}/runs":{"get":{"tags":["workflows"],"summary":"List Workflow Rule Runs","operationId":"list_workflow_rule_runs_api_v1_workflows__rule_id__runs_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Rule Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowRunLogPage"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workflows/{rule_id}/dry-run":{"post":{"tags":["workflows"],"summary":"Dry Run Workflow Rule","description":"Test a saved rule against one record without committing (UX-013).\n\nLoads the rule (404 on a cross-tenant/unknown id), then evaluates it against\n`resource_id` (or the most-recent record of its trigger entity) and reports\nwhat *would* happen — no mutation, no send, no run-log row.","operationId":"dry_run_workflow_rule_api_v1_workflows__rule_id__dry_run_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Rule Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DryRunRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DryRunResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/forms":{"post":{"tags":["forms"],"summary":"Create Form","operationId":"create_form_api_v1_forms_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["forms"],"summary":"List Forms","operationId":"list_forms_api_v1_forms_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FormSummary"},"title":"Response List Forms Api V1 Forms Get"}}}}}}},"/api/v1/forms/{form_id}":{"get":{"tags":["forms"],"summary":"Get Form","operationId":"get_form_api_v1_forms__form_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"form_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Form Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["forms"],"summary":"Update Form","operationId":"update_form_api_v1_forms__form_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"form_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Form Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["forms"],"summary":"Delete Form","operationId":"delete_form_api_v1_forms__form_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"form_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Form Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/forms/{form_id}/submissions":{"get":{"tags":["forms"],"summary":"List Form Submissions","operationId":"list_form_submissions_api_v1_forms__form_id__submissions_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"form_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Form Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Before"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FormSubmissionRead"},"title":"Response List Form Submissions Api V1 Forms  Form Id  Submissions Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tags":{"get":{"tags":["tags"],"summary":"List Tags","operationId":"list_tags_api_v1_tags_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagList"}}}}}},"post":{"tags":["tags"],"summary":"Create Tag","operationId":"create_tag_api_v1_tags_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tags/search":{"get":{"tags":["tags"],"summary":"Search Tags","description":"Ranked tag auto-suggest for the per-entity picker (M-Tags-c). Case-folded\ncontains-match over name/slug, ranked exact-slug → prefix → substring; an empty\n``q`` returns ``[]``. Static path — declared before ``/{tag_id}``.","operationId":"search_tags_api_v1_tags_search_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":100,"default":"","title":"Q"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TagRead"},"title":"Response Search Tags Api V1 Tags Search Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tags/merge":{"post":{"tags":["tags"],"summary":"Merge Tags","operationId":"merge_tags_api_v1_tags_merge_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagMergeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tags/assignments":{"get":{"tags":["tags"],"summary":"List Entity Tags","operationId":"list_entity_tags_api_v1_tags_assignments_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"entity_type","in":"query","required":true,"schema":{"enum":["contact","company","deal"],"type":"string","title":"Entity Type"}},{"name":"entity_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","title":"Entity Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TagRead"},"title":"Response List Entity Tags Api V1 Tags Assignments Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["tags"],"summary":"Assign","description":"Pin a tag to a contact|company|deal by ``tag_id`` (an existing tag) or\n``name`` (get-or-create — the picker's allow-create, M-Tags-c). Idempotent — a\nrepeat returns the existing assignment.","operationId":"assign_api_v1_tags_assignments_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagAssignmentCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagAssignmentRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["tags"],"summary":"Unassign","operationId":"unassign_api_v1_tags_assignments_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"tag_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","title":"Tag Id"}},{"name":"entity_type","in":"query","required":true,"schema":{"enum":["contact","company","deal"],"type":"string","title":"Entity Type"}},{"name":"entity_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","title":"Entity Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tags/{tag_id}":{"put":{"tags":["tags"],"summary":"Update Tag","operationId":"update_tag_api_v1_tags__tag_id__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"tag_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tag Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["tags"],"summary":"Delete Tag","operationId":"delete_tag_api_v1_tags__tag_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"tag_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tag Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tags/{tag_id}/assignments":{"get":{"tags":["tags"],"summary":"Assignment Count","operationId":"assignment_count_api_v1_tags__tag_id__assignments_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"tag_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tag Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagAssignmentCount"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/mine":{"get":{"tags":["tasks"],"summary":"My Tasks","operationId":"my_tasks_api_v1_tasks_mine_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"include_completed","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Completed"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(todo|in_progress|waiting|done)$"},{"type":"null"}],"title":"Status"}},{"name":"priority","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(low|normal|high)$"},{"type":"null"}],"title":"Priority"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_TaskListItem_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks":{"get":{"tags":["tasks"],"summary":"List Tasks","operationId":"list_tasks_api_v1_tasks_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"assignee_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assignee Id"}},{"name":"entity_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Type"}},{"name":"entity_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Entity Id"}},{"name":"include_completed","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Completed"}},{"name":"due_before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due Before"}},{"name":"overdue_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Overdue Only"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(todo|in_progress|waiting|done)$"},{"type":"null"}],"title":"Status"}},{"name":"priority","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(low|normal|high)$"},{"type":"null"}],"title":"Priority"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_TaskListItem_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["tasks"],"summary":"Create Task","operationId":"create_task_api_v1_tasks_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/{task_id}":{"get":{"tags":["tasks"],"summary":"Get Task","operationId":"get_task_api_v1_tasks__task_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["tasks"],"summary":"Update Task","operationId":"update_task_api_v1_tasks__task_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["tasks"],"summary":"Delete Task","operationId":"delete_task_api_v1_tasks__task_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/{task_id}/complete":{"post":{"tags":["tasks"],"summary":"Complete Task","operationId":"complete_task_api_v1_tasks__task_id__complete_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/{task_id}/reopen":{"post":{"tags":["tasks"],"summary":"Reopen Task","operationId":"reopen_task_api_v1_tasks__task_id__reopen_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/{task_id}/notes":{"get":{"tags":["tasks"],"summary":"List Task Notes","operationId":"list_task_notes_api_v1_tasks__task_id__notes_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_NoteResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["tasks"],"summary":"Create Task Note","operationId":"create_task_note_api_v1_tasks__task_id__notes_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/meetings":{"post":{"tags":["meetings"],"summary":"Create Meeting","operationId":"create_meeting_api_v1_meetings_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["meetings"],"summary":"List Meetings","operationId":"list_meetings_api_v1_meetings_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"entity_type","in":"query","required":true,"schema":{"type":"string","title":"Entity Type"}},{"name":"entity_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","title":"Entity Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/meetings/{meeting_id}":{"get":{"tags":["meetings"],"summary":"Get Meeting","operationId":"get_meeting_api_v1_meetings__meeting_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Meeting Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["meetings"],"summary":"Update Meeting","operationId":"update_meeting_api_v1_meetings__meeting_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Meeting Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["meetings"],"summary":"Delete Meeting","operationId":"delete_meeting_api_v1_meetings__meeting_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Meeting Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/activity":{"get":{"tags":["activity"],"summary":"Get Activity","operationId":"get_activity_api_v1_activity_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"entity_type","in":"query","required":true,"schema":{"type":"string","title":"Entity Type"}},{"name":"entity_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","title":"Entity Id"}},{"name":"types","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"title":"Types"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Before"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivityFeed"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notifications":{"get":{"tags":["notifications"],"summary":"List Notifications Route","operationId":"list_notifications_route_api_v1_notifications_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notifications/read-all":{"post":{"tags":["notifications"],"summary":"Mark All Notifications Read Route","operationId":"mark_all_notifications_read_route_api_v1_notifications_read_all_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"integer"},"title":"Response Mark All Notifications Read Route Api V1 Notifications Read All Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notifications/{notification_id}/read":{"post":{"tags":["notifications"],"summary":"Mark Notification Read Route","operationId":"mark_notification_read_route_api_v1_notifications__notification_id__read_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"notification_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Notification Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/insights":{"get":{"tags":["insights"],"summary":"List Insights Route","operationId":"list_insights_route_api_v1_insights_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by a single status; omitted returns the actionable feed.","title":"Status"},"description":"Filter by a single status; omitted returns the actionable feed."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsightList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/insights/autonomy":{"get":{"tags":["insights"],"summary":"List Autonomy Feed Route","description":"The recipient's backward \"Watch Limena work\" feed (M-Legible-b, GAP-041).\n\nThe `action_taken` autonomy rows — *what Limena did* — reverse-chronological +\nkeyset-paginated. This is the read the -a contract left to -b: the suggestion\nGET above is hardcoded to `category='suggestion'`. Same recipient scoping +\ngating as that feed (a read — no `RequireMember`/`RequireCSRF`); reversed rows\nstay visible. A malformed `cursor` is a 422 via the service.","operationId":"list_autonomy_feed_route_api_v1_insights_autonomy_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque keyset cursor — pass back the prior page's next_cursor.","title":"Cursor"},"description":"Opaque keyset cursor — pass back the prior page's next_cursor."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":30,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsightFeedPage"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/insights/synthesis":{"get":{"tags":["insights"],"summary":"Get Today Synthesis Route","description":"The morning brief's cross-signal synthesis (M-TodaySynth-a, AI-006, advisory).\n\nReturns an advisory \"here's your day\" line synthesized over the recipient's ranked insight\nfeed — the same set the board renders (Redis-cached on the live signal set; ``null`` when\nthe brief has fewer than two signals or a generation degraded, so the line just hides).\nFetched out-of-band by the board, so a cold synthesis call never blocks the deterministic\n``/today`` paint. A read (no ``RequireMember``/``RequireCSRF``), recipient-scoped via the\nfeed read; NEVER 500s on a generation failure.","operationId":"get_today_synthesis_route_api_v1_insights_synthesis_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TodaySynthesisResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/insights/{insight_id}":{"patch":{"tags":["insights"],"summary":"Update Insight Route","operationId":"update_insight_route_api_v1_insights__insight_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"insight_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Insight Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsightPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsightRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/insights/{insight_id}/undo":{"post":{"tags":["insights"],"summary":"Undo Insight Route","description":"Reverse one `action_taken` autonomy row (M-Legible-a, GAP-041).\n\nRe-applies the inverse from the row's `undo_payload` and stamps\n`reversed_at`/`reversed_by`, auditing the inverse as the acting user. The\n`gated` reversal (re-subscribe) needs `confirm=true`, else the service\nreturns `details.confirm_required` for the UI to surface a confirm step.","operationId":"undo_insight_route_api_v1_insights__insight_id__undo_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"insight_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Insight Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsightUndo"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsightRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/attachments":{"post":{"tags":["attachments"],"summary":"Upload Attachment","operationId":"upload_attachment_api_v1_attachments_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_attachment_api_v1_attachments_post"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["attachments"],"summary":"List Attachments","operationId":"list_attachments_api_v1_attachments_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"entity_type","in":"query","required":true,"schema":{"type":"string","title":"Entity Type"}},{"name":"entity_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","title":"Entity Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/attachments/{attachment_id}":{"get":{"tags":["attachments"],"summary":"Get Attachment","operationId":"get_attachment_api_v1_attachments__attachment_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Attachment Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["attachments"],"summary":"Delete Attachment","operationId":"delete_attachment_api_v1_attachments__attachment_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Attachment Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/attachments/{attachment_id}/download":{"get":{"tags":["attachments"],"summary":"Download Attachment","operationId":"download_attachment_api_v1_attachments__attachment_id__download_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Attachment Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/call-recordings":{"post":{"tags":["call-recordings"],"summary":"Initiate Call Recording","operationId":"initiate_call_recording_api_v1_call_recordings_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallRecordingCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallRecordingInitiateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["call-recordings"],"summary":"List Call Recordings","operationId":"list_call_recordings_api_v1_call_recordings_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"entity_type","in":"query","required":true,"schema":{"type":"string","title":"Entity Type"}},{"name":"entity_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Entity Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallRecordingList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/call-recordings/{recording_id}/audio":{"post":{"tags":["call-recordings"],"summary":"Upload Call Recording Audio","description":"Streamed-multipart audio drain (local-dev fallback) — store + finalize.","operationId":"upload_call_recording_audio_api_v1_call_recordings__recording_id__audio_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Recording Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_call_recording_audio_api_v1_call_recordings__recording_id__audio_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallRecordingRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/call-recordings/{recording_id}/uploaded":{"post":{"tags":["call-recordings"],"summary":"Finalize Call Recording Upload","description":"Presigned-upload finalize (prod) — confirm the object landed + hand off.","operationId":"finalize_call_recording_upload_api_v1_call_recordings__recording_id__uploaded_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Recording Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallRecordingRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/call-recordings/transcript":{"post":{"tags":["call-recordings"],"summary":"Create Pasted Call Transcript","description":"Create a recording from a pasted transcript (no audio; STT skipped).\n\nThe supplied-transcript shortcut into the manual-upload source — summarisation\nruns on the pasted text and is enqueued after the commit (no transcribe stage).","operationId":"create_pasted_call_transcript_api_v1_call_recordings_transcript_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallRecordingTranscriptCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallRecordingRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/call-recordings/bot":{"post":{"tags":["call-recordings"],"summary":"Schedule Call Recording Bot","description":"Send a Recall.ai bot to a meeting URL (per-meeting opt-in, no calendar OAuth).\n\nSchedule-time: creates a ``processing`` recording the Calls surface shows as\n\"Bot recording…\"; the account-level completion webhook (a later chunk) hydrates\nit. Nothing is enqueued here — the bot supplies the transcript and the webhook\ndrives ingest (unlike the audio/paste paths, which enqueue transcribe/summary).","operationId":"schedule_call_recording_bot_api_v1_call_recordings_bot_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallRecordingBotScheduleRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallRecordingRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/call-recordings/{recording_id}/tasks":{"post":{"tags":["call-recordings"],"summary":"Create Call Action Item Tasks","description":"One-click: turn a call's extracted action items into Tasks on its entity.\n\nEach item must be one of the recording's ``action_items``; a Task with the same\nsubject already open on the entity is reused, not duplicated (the dup guard).","operationId":"create_call_action_item_tasks_api_v1_call_recordings__recording_id__tasks_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Recording Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallActionItemTaskRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallActionItemTasksResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/call-recordings/{recording_id}/actions/apply":{"post":{"tags":["call-recordings"],"summary":"Review Call Suggested Actions","description":"Apply/dismiss a call's suggested actions in one reviewed click (M-Calls-e).\n\nApplied suggestions compose the existing task/note/deal services (their\nvalidation + audit); each id must be one of the recording's suggestions.\nIdempotent per item: re-applying is refused, an existing open task is\nreused, and a deal already in the requested stage is a no-op.","operationId":"review_call_suggested_actions_api_v1_call_recordings__recording_id__actions_apply_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Recording Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallSuggestedActionsReviewRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallSuggestedActionsReviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/call-recordings/{recording_id}":{"get":{"tags":["call-recordings"],"summary":"Get Call Recording","operationId":"get_call_recording_api_v1_call_recordings__recording_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Recording Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallRecordingDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["call-recordings"],"summary":"Delete Call Recording","description":"Remove a call recording (UX-041).\n\nThe in-product fix for an upload-orphaned \"Waiting for audio…\" row that would\notherwise spin forever, and a general remove for any owned recording. Soft-deletes\n(retained for audit, dropped from every read) + best-effort cleans up any transient\naudio object. 204 on success; 404 if not found / not this tenant's.","operationId":"delete_call_recording_api_v1_call_recordings__recording_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Recording Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me":{"get":{"tags":["users"],"summary":"Get Me","operationId":"get_me_api_v1_users_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPublic"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/me/activity":{"get":{"tags":["users"],"summary":"List My Activity","description":"Authenticated users — including members — read their own audit-log\nrows. Distinct from ``GET /tenants/me/audit-log`` which is admin-only and\nspans the whole tenant; this endpoint is tenant-scoped AND user-scoped so\na member cannot see another user's activity through it.","operationId":"list_my_activity_api_v1_users_me_activity_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_AuditLogEntry_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users":{"get":{"tags":["users"],"summary":"List Users","operationId":"list_users_api_v1_users_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_UserPublic_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/invite":{"post":{"tags":["users"],"summary":"Invite User","operationId":"invite_user_api_v1_users_invite_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInviteRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/invite/accept":{"post":{"tags":["users"],"summary":"Accept Invite","description":"Accept an invitation via the token from the email link.\n\nTenant is resolved from the token hash via a SECURITY DEFINER function\n(migration 0031) — the invite link does not carry slug information and\nthe post-Q9 single-host prod has no Host signal. Not-found vs.\nalready-accepted vs. expired are surfaced uniformly by the service\nlayer's atomic mark-as-accepted UPDATE.","operationId":"accept_invite_api_v1_users_invite_accept_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteAcceptRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/invitations":{"get":{"tags":["users"],"summary":"List Invitations","operationId":"list_invitations_api_v1_users_invitations_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/InvitationPublic"},"type":"array","title":"Response List Invitations Api V1 Users Invitations Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/invitations/{invitation_id}":{"delete":{"tags":["users"],"summary":"Revoke Invitation","operationId":"revoke_invitation_api_v1_users_invitations__invitation_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Invitation Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/invitations/{invitation_id}/resend":{"post":{"tags":["users"],"summary":"Resend Invitation","operationId":"resend_invitation_api_v1_users_invitations__invitation_id__resend_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Invitation Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/sign-out-everywhere":{"post":{"tags":["users"],"summary":"Sign Out Everywhere","description":"Revoke every active refresh-token family for the calling user.\n\nIncludes the calling session — auth cookies are cleared on the response\nand the next request will fail silent-refresh and bounce to login.","operationId":"sign_out_everywhere_api_v1_users_me_sign_out_everywhere_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/password":{"patch":{"tags":["users"],"summary":"Change Password","operationId":"change_password_api_v1_users_me_password_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordChangeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/email":{"patch":{"tags":["users"],"summary":"Change Email","description":"Update the calling user's email. M-Trust-a (D3) step-up gated:\ncurrent-password re-verification + a fresh TOTP code (for 2FA users).","operationId":"change_email_api_v1_users_me_email_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailChangeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/signature-logo":{"post":{"tags":["users"],"summary":"Upload Signature Logo","description":"Upload (or replace) the calling user's email-signature logo (M-SigLogo-a).\n\nSEC-014 byte-sniff + virus-scan + a ≤512 KB cap; image-only (PNG/JPEG/GIF).\nValidation errors (415 unsupported type, 413 too large, 422 bad dimensions)\npropagate to the global handler so their structured ``details`` reach the\nfrontend.","operationId":"upload_signature_logo_api_v1_users_me_signature_logo_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_signature_logo_api_v1_users_me_signature_logo_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPublic"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["users"],"summary":"Get Signature Logo","description":"Serve the calling user's logo bytes (auth-only).\n\n``nosniff`` + the sniffed image MIME; the in-app preview fetches this with the\nbearer token and renders via an object URL (a bare ``<img src>`` would not\ncarry the token). The email never hits this route — it attaches the bytes\ninline via CID.","operationId":"get_signature_logo_api_v1_users_me_signature_logo_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"delete":{"tags":["users"],"summary":"Delete Signature Logo","description":"Remove the calling user's email-signature logo (idempotent).","operationId":"delete_signature_logo_api_v1_users_me_signature_logo_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPublic"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/profile-doc":{"get":{"tags":["users"],"summary":"Get My Profile Doc","description":"Return the caller's personal AI system doc + provenance, or an empty shell if unset.\n\n**Owner-private** — scoped to the caller (M-Context-b); a never-synthesized user gets 200\nwith empty fields so ``/profile`` shows the first-run state, not a 404.","operationId":"get_my_profile_doc_api_v1_users_me_profile_doc_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}}}},"patch":{"tags":["users"],"summary":"Update My Profile Doc","description":"Save the caller's edited profile doc (whole-doc replace → ``source='edited'``).\n\nSelf-only (the actor's own row — no ``user_id`` parameter); ``auto_derived`` is always\nre-snapshotted server-side. Audit-logged (changed keys only).","operationId":"update_my_profile_doc_api_v1_users_me_profile_doc_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/profile-doc/synthesize":{"post":{"tags":["users"],"summary":"Synthesize My Profile Doc","description":"Structure the caller's free-text answers + account facts into their profile doc via a\nthin LLM pass, then auto-save (``source='derived'``).\n\nSelf-only. 409 (``USER_PROFILE_NO_ANSWERS``) when no answer carries text.","operationId":"synthesize_my_profile_doc_api_v1_users_me_profile_doc_synthesize_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileSynthesizeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/memory-facts":{"get":{"tags":["users"],"summary":"List My Memory Facts","description":"The caller's own learned facts, newest-first (owner-private, M-Memory-c).\n\nEmpty list (200, not 404) for a user the assistant has learned nothing about, so\n``/profile`` renders the first-run empty state.","operationId":"list_my_memory_facts_api_v1_users_me_memory_facts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/MemoryFactResponse"},"type":"array","title":"Response List My Memory Facts Api V1 Users Me Memory Facts Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/me/memory-facts/{fact_id}":{"patch":{"tags":["users"],"summary":"Update My Memory Fact","description":"Edit an own fact's content/category (flips ``source='human'`` — the AI never\nsilently re-writes a human-edited fact). 404 if the fact is not the caller's\n(owner-private). Audited (``memory_fact.edited``).","operationId":"update_my_memory_fact_api_v1_users_me_memory_facts__fact_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"fact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Fact Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryFactUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryFactResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["users"],"summary":"Delete My Memory Fact","description":"Remove an own fact (owner-private). 404 if not the caller's. Audited\n(``memory_fact.forgotten``).","operationId":"delete_my_memory_fact_api_v1_users_me_memory_facts__fact_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"fact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Fact Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/memory-settings":{"get":{"tags":["users"],"summary":"Get My Memory Settings","description":"The caller's auto-learning master switch (``users.memory_enabled``).","operationId":"get_my_memory_settings_api_v1_users_me_memory_settings_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemorySettingsResponse"}}}}}},"patch":{"tags":["users"],"summary":"Update My Memory Settings","description":"Flip the caller's auto-learning master switch. Off ⇒ the assistant stops\nlearning AND stops recalling (the whole feature goes dormant; existing facts stay\nlisted here so the owner can still purge them). Audited\n(``memory_fact.settings_updated``).","operationId":"update_my_memory_settings_api_v1_users_me_memory_settings_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemorySettingsUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemorySettingsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/memory-facts":{"get":{"tags":["users"],"summary":"List Tenant Memory Facts","description":"Learned facts for admin governance (M-Memory-c). ``scope=own`` (default, any\nmember) returns the caller's own facts; ``scope=tenant`` (admin/owner) returns every\nuser's, each enriched with the owning user's name + email for grouping. Remove-only\n— no edit affordance here (the owner keeps sole edit on ``/profile``).","operationId":"list_tenant_memory_facts_api_v1_users_memory_facts_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"scope","in":"query","required":false,"schema":{"enum":["own","tenant"],"type":"string","default":"own","title":"Scope"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminMemoryFactResponse"},"title":"Response List Tenant Memory Facts Api V1 Users Memory Facts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/memory-facts/{fact_id}":{"delete":{"tags":["users"],"summary":"Delete Tenant Memory Fact","description":"Purge a learned fact (M-Memory-c, remove-only). ``scope=own`` removes the caller's\nown fact (owner-private, ``memory_fact.forgotten``); ``scope=tenant`` (admin/owner)\npurges any user's fact tenant-wide (``memory_fact.admin_purged``, the admin recorded\nas actor). 404 if the fact is outside the allowed scope.","operationId":"delete_tenant_memory_fact_api_v1_users_memory_facts__fact_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"fact_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Fact Id"}},{"name":"scope","in":"query","required":false,"schema":{"enum":["own","tenant"],"type":"string","default":"own","title":"Scope"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{user_id}":{"patch":{"tags":["users"],"summary":"Patch User","operationId":"patch_user_api_v1_users__user_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["users"],"summary":"Deactivate User","operationId":"deactivate_user_api_v1_users__user_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["users"],"summary":"Get User","operationId":"get_user_api_v1_users__user_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPublic"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{user_id}/reactivate":{"post":{"tags":["users"],"summary":"Reactivate User","operationId":"reactivate_user_api_v1_users__user_id__reactivate_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me":{"get":{"tags":["tenants"],"summary":"Get Tenant","operationId":"get_tenant_api_v1_tenants_me_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantProfile"}}}}}},"patch":{"tags":["tenants"],"summary":"Update Tenant","operationId":"update_tenant_api_v1_tenants_me_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/ai-autonomy":{"get":{"tags":["tenants"],"summary":"Get Ai Autonomy","description":"The workspace AI autonomy policy: slider level (0 = confirm everything)\nplus per-capability level pins.","operationId":"get_ai_autonomy_api_v1_tenants_me_ai_autonomy_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiAutonomyRead"}}}}}},"put":{"tags":["tenants"],"summary":"Put Ai Autonomy","description":"Replace the workspace AI autonomy policy. Levels 0-2 require an admin;\nlevel 3 - and any per-capability level-3 opt-in - requires the workspace\nowner.","operationId":"put_ai_autonomy_api_v1_tenants_me_ai_autonomy_put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiAutonomyUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiAutonomyRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/api-keys":{"get":{"tags":["tenants"],"summary":"List Api Keys","operationId":"list_api_keys_api_v1_tenants_me_api_keys_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeySummary"},"title":"Response List Api Keys Api V1 Tenants Me Api Keys Get"}}}}}},"post":{"tags":["tenants"],"summary":"Create Api Key","operationId":"create_api_key_api_v1_tenants_me_api_keys_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/custom-fields/apply-template":{"post":{"tags":["tenants"],"summary":"Apply Custom Field Template","description":"Apply a named custom-fields template to the current tenant.\n\nIdempotent: re-running with the same template is a no-op on\nalready-merged keys, and any tenant-defined fields sharing a key\nwith the template are preserved untouched. Used for backfilling\ntenants that pre-date the auto-apply hook (Angus's prod tenant\nvia the milestone close-out) and for recovery.","operationId":"apply_custom_field_template_api_v1_tenants_me_custom_fields_apply_template_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyCustomFieldTemplateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyCustomFieldTemplateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/custom-fields/{entity}":{"get":{"tags":["tenants"],"summary":"List Custom Fields","operationId":"list_custom_fields_api_v1_tenants_me_custom_fields__entity__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"entity","in":"path","required":true,"schema":{"type":"string","title":"Entity"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["tenants"],"summary":"Add Custom Field","operationId":"add_custom_field_api_v1_tenants_me_custom_fields__entity__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"entity","in":"path","required":true,"schema":{"type":"string","title":"Entity"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomFieldCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/custom-fields/{entity}/{key}":{"patch":{"tags":["tenants"],"summary":"Update Custom Field Route","operationId":"update_custom_field_route_api_v1_tenants_me_custom_fields__entity___key__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"entity","in":"path","required":true,"schema":{"type":"string","title":"Entity"}},{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomFieldUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["tenants"],"summary":"Delete Custom Field Route","operationId":"delete_custom_field_route_api_v1_tenants_me_custom_fields__entity___key__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"entity","in":"path","required":true,"schema":{"type":"string","title":"Entity"}},{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/custom-fields/{entity}/reorder":{"put":{"tags":["tenants"],"summary":"Reorder Custom Fields Route","operationId":"reorder_custom_fields_route_api_v1_tenants_me_custom_fields__entity__reorder_put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"entity","in":"path","required":true,"schema":{"type":"string","title":"Entity"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomFieldReorder"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/api-keys/{key_id}":{"delete":{"tags":["tenants"],"summary":"Revoke Api Key","operationId":"revoke_api_key_api_v1_tenants_me_api_keys__key_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/audit-log":{"get":{"tags":["tenants"],"summary":"Get Audit Log","operationId":"get_audit_log_api_v1_tenants_me_audit_log_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"action","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}},{"name":"actor_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Type"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Date To"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_AuditLogEntry_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/audit-log/export":{"get":{"tags":["tenants"],"summary":"Export Audit Log","description":"Download the audit log as CSV with the same filters as `/me/audit-log`.\n\nMirrors the `routes/lists.py` synchronous-CSV precedent (M19b). The\naudit-log row recording this export is written by the service so it\nappears in subsequent reads.","operationId":"export_audit_log_api_v1_tenants_me_audit_log_export_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"action","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}},{"name":"actor_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Type"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Date To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/stages":{"post":{"tags":["tenants"],"summary":"Add Stage","operationId":"add_stage_api_v1_tenants_me_stages_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineStageCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/stages/reorder":{"put":{"tags":["tenants"],"summary":"Reorder Stages","operationId":"reorder_stages_api_v1_tenants_me_stages_reorder_put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineStageReorder"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/stages/{key}":{"patch":{"tags":["tenants"],"summary":"Rename Stage","operationId":"rename_stage_api_v1_tenants_me_stages__key__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineStageUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["tenants"],"summary":"Delete Stage","operationId":"delete_stage_api_v1_tenants_me_stages__key__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/statuses":{"post":{"tags":["tenants"],"summary":"Add Contact Status","operationId":"add_contact_status_api_v1_tenants_me_statuses_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactStatusCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/statuses/reorder":{"put":{"tags":["tenants"],"summary":"Reorder Contact Statuses","operationId":"reorder_contact_statuses_api_v1_tenants_me_statuses_reorder_put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactStatusReorder"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/statuses/{key}":{"patch":{"tags":["tenants"],"summary":"Update Contact Status","operationId":"update_contact_status_api_v1_tenants_me_statuses__key__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactStatusUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["tenants"],"summary":"Delete Contact Status","operationId":"delete_contact_status_api_v1_tenants_me_statuses__key__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/ai-config":{"get":{"tags":["tenants"],"summary":"Get Ai Config","description":"Return the tenant's LLM provider configuration, or null if unset.\n\nUnset is normal state — AI calls fall back to platform defaults. The\nplaintext api_key is never returned; `config.api_key_set` indicates\nwhether one is persisted.","operationId":"get_ai_config_api_v1_tenants_me_ai_config_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantAIConfigResponse"}}}}}},"patch":{"tags":["tenants"],"summary":"Upsert Ai Config","description":"Replace-or-create the tenant's LLM provider configuration.","operationId":"upsert_ai_config_api_v1_tenants_me_ai_config_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantAIConfigUpsert"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantAIConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["tenants"],"summary":"Delete Ai Config","description":"Delete the tenant's LLM provider configuration — AI calls revert to\nplatform defaults. No-op if no config exists.","operationId":"delete_ai_config_api_v1_tenants_me_ai_config_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/ai-config/available-models":{"get":{"tags":["tenants"],"summary":"List Available Models","description":"List installed models on a tenant's local LLM endpoint.\n\nCurrently Ollama-only — proxies to `<endpoint_url>/api/tags`. Lets\nthe /settings/ai Combobox suggest models the user has actually\npulled rather than forcing them to type the identifier.\n\n`endpoint_url` is accepted as a query param (not read from the saved\nrow) so the picker works during initial setup — before the row\nexists. Server-to-server fetch so it works when Limena runs in\nDocker and Ollama lives on the host.","operationId":"list_available_models_api_v1_tenants_me_ai_config_available_models_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"provider","in":"query","required":true,"schema":{"type":"string","title":"Provider"}},{"name":"endpoint_url","in":"query","required":true,"schema":{"type":"string","title":"Endpoint Url"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailableModelsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/ai-config/test":{"post":{"tags":["tenants"],"summary":"Test Ai Config","description":"Fire a single ping prompt against the test config.\n\nDoes not persist. When `api_key` is omitted the service uses the saved\nconfig's stored key (lets a tenant re-test without retyping). Always\nwrites a `tenant.ai_config_tested` audit row with success/error.","operationId":"test_ai_config_api_v1_tenants_me_ai_config_test_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantAIConfigTestRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionTestResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/ai-personalization":{"get":{"tags":["tenants"],"summary":"Get Ai Personalization Settings","description":"Return the tenant's resolved AI-personalization settings.\n\nResolved = tenant overrides applied on top of the shipped baseline\n(system prompt) and the fixed defaults (cost cap, concurrency). Use\nthe `system_prompt_is_custom` flag to surface \"Reset to default\" in\nthe UI without fetching the default separately.","operationId":"get_ai_personalization_settings_api_v1_tenants_me_ai_personalization_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiPersonalizationSettingsRead"}}}}}},"patch":{"tags":["tenants"],"summary":"Update Ai Personalization Settings","description":"Partial update of the tenant's AI-personalization settings.\n\nExplicit `null` on `system_prompt` or `voice_snippet` resets that\nfield. Omitted keys leave the corresponding setting unchanged.","operationId":"update_ai_personalization_settings_api_v1_tenants_me_ai_personalization_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiPersonalizationSettingsPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiPersonalizationSettingsRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/icp":{"get":{"tags":["tenants"],"summary":"Get Icp Profile","description":"Return the tenant's ICP profile + provenance, or an empty shell if unset.\n\nA never-derived tenant gets 200 with all-null fields so the Settings surface\nshows an empty state with a \"Derive from won deals\" trigger (not a 404).","operationId":"get_icp_profile_api_v1_tenants_me_icp_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IcpProfileResponse"}}}}}},"patch":{"tags":["tenants"],"summary":"Update Icp Profile","description":"Save a user-edited ICP (whole-profile replace → ``source='edited'``).\n\nThe Settings UI holds the full profile and saves it back; audit-logged.","operationId":"update_icp_profile_api_v1_tenants_me_icp_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IcpProfile-Input"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IcpProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/icp/derive":{"post":{"tags":["tenants"],"summary":"Derive Icp Profile","description":"Start an async ICP derivation from the tenant's won deals (M-Prospect-b).\n\nReturns a ``job_id`` to poll via ``GET …/icp/derive/{job_id}`` — the pass (won\ndeals → companies' firmographics + notes → bounded web research + LLM synthesis)\nruns off the request path. 409 when web research is disabled (``ICP_WEB_SEARCH_\nDISABLED``) or there are no won deals to derive from (``ICP_NO_WON_DEALS``).","operationId":"derive_icp_profile_api_v1_tenants_me_icp_derive_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IcpDerivationJobCreated"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/icp/derive/{job_id}":{"get":{"tags":["tenants"],"summary":"Get Icp Derivation","description":"Poll an in-flight ICP derivation → ``{status, profile}`` (M-Prospect-b).\n\nThe caller's (RLS-authenticated) tenant + a tenant-match on the result envelope\nare the access boundary, not the opaque job id (the ARQ result store isn't\ntenant-scoped). ``done`` means the profile is already persisted; ``profile``\nechoes it so the UI renders without a second GET.","operationId":"get_icp_derivation_api_v1_tenants_me_icp_derive__job_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IcpDerivationJobOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/me/deletion":{"post":{"tags":["tenants"],"summary":"Initiate Tenant Deletion","operationId":"initiate_tenant_deletion_api_v1_tenants_me_deletion_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantDeletionInitiate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantDeletionRequestRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["tenants"],"summary":"Get Tenant Deletion","operationId":"get_tenant_deletion_api_v1_tenants_me_deletion_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/TenantDeletionRequestRead"},{"type":"null"}],"title":"Response Get Tenant Deletion Api V1 Tenants Me Deletion Get"}}}}}}},"/api/v1/tenants/me/deletion/{deletion_id}":{"delete":{"tags":["tenants"],"summary":"Cancel Tenant Deletion","operationId":"cancel_tenant_deletion_api_v1_tenants_me_deletion__deletion_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"deletion_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Deletion Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantDeletionRequestRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/onboarding/status":{"get":{"tags":["onboarding"],"summary":"Get Status","description":"First-run activation status for the current tenant.\n\nAny authenticated user may read it (the frontend gates *display* of the\nchecklist to owner/admin). Derived live from real data — no stored\nprogress (DO3).","operationId":"get_status_api_v1_onboarding_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingStatus"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/onboarding/dismiss":{"post":{"tags":["onboarding"],"summary":"Dismiss Checklist","description":"Dismiss the dashboard getting-started checklist for the tenant (admin/owner).","operationId":"dismiss_checklist_api_v1_onboarding_dismiss_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingStatus"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/gmail/authorize":{"get":{"tags":["integrations"],"summary":"Gmail Authorize","description":"Build the signed OAuth state and return the Google consent URL.","operationId":"gmail_authorize_api_v1_integrations_gmail_authorize_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GmailAuthorizeResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/integrations/gmail/callback":{"post":{"tags":["integrations"],"summary":"Gmail Callback","description":"Verify state + finish OAuth exchange + persist the connected account.","operationId":"gmail_callback_api_v1_integrations_gmail_callback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GmailCallbackPayload"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GmailCallbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/integrations/gmail/accounts":{"get":{"tags":["integrations"],"summary":"List Gmail Accounts","description":"List gmail-connected accounts — the acting user's own by default;\n`scope=all` is the owner/admin oversight view.\n\nPre-M-Outlook-a this route returned every email account regardless of\nprovider (gmail was the only provider, so the filter was implicit).\nM-Outlook-a added the outlook path, so the filter is now explicit —\nconsistent with /outlook/accounts. Behaviour change is invisible to\npre-M-Outlook-a callers who never had non-gmail rows to surface.","operationId":"list_gmail_accounts_api_v1_integrations_gmail_accounts_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"scope","in":"query","required":false,"schema":{"enum":["own","all"],"type":"string","default":"own","title":"Scope"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccountList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/gmail/webhook":{"post":{"tags":["integrations"],"summary":"Gmail Webhook","description":"Public Pub/Sub push endpoint. Auth = Google-signed JWT in the Authorization header (verified against Google's JWKS with pinned `aud` + service-account `email` claims). Not callable by regular users — see docs/development/m27-pubsub-setup.md.","operationId":"gmail_webhook_api_v1_integrations_gmail_webhook_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/gmail/accounts/{account_id}/tracking":{"patch":{"tags":["integrations"],"summary":"Update Gmail Account Tracking","description":"M28 — toggle open + click tracking on the connected account.\n\nOFF by default at account-create time (M26a). Setting `enabled=true`\nsurfaces the GDPR opt-in banner in the frontend before this PATCH\nfires — the route trusts the frontend's consent gate and just records\nthe audit row.","operationId":"update_gmail_account_tracking_api_v1_integrations_gmail_accounts__account_id__tracking_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Account Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccountTrackingUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccount"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/accounts/{account_id}/background-scan":{"patch":{"tags":["integrations"],"summary":"Update Account Background Scan","description":"M-SelfBuild-a (GAP-060) — toggle the daily background correspondent sync.\n\nProvider-agnostic (Gmail + Outlook both support discovery): the account id is\nglobally unique + tenant-scoped via RLS, so no provider segment is needed.\nDefault off at connect time; enabling opts the mailbox into a daily delta scan\nthat auto-imports newly-reciprocal correspondents (the first run stages the\nbackfill, DA1). Owner-or-admin only (SEC-006, enforced in the service).","operationId":"update_account_background_scan_api_v1_integrations_accounts__account_id__background_scan_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Account Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccountBackgroundScanUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccount"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/gmail/accounts/{account_id}":{"delete":{"tags":["integrations"],"summary":"Disconnect Gmail Account","operationId":"disconnect_gmail_account_api_v1_integrations_gmail_accounts__account_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Account Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/outlook/authorize":{"get":{"tags":["integrations"],"summary":"Outlook Authorize","description":"Build the signed OAuth state and return the Microsoft consent URL.\n\nMirrors gmail_authorize. The signed state binds the OAuth round-trip\nto the (tenant_id, tenant_slug, user_id) at authorize time so the\ncallback can defence-in-depth verify the JWT-bound user matches.","operationId":"outlook_authorize_api_v1_integrations_outlook_authorize_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutlookAuthorizeResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/integrations/outlook/callback":{"post":{"tags":["integrations"],"summary":"Outlook Callback","description":"Verify state + finish Microsoft OAuth exchange + persist the account.\n\nMirrors gmail_callback exactly. The frontend's `/auth/outlook/callback`\npage receives Microsoft's top-level redirect (code + state on the URL),\nshows a branded \"Connecting…\" spinner immediately, then POSTs\n`{code, state}` to this route with the user's bearer JWT — the user\nis back on Limena's origin by then so the bearer is available.\n\nPivot rationale (2026-05-18): the original M-Outlook-a chunk-D\nimplementation made this a GET callback with state-only auth so\nMicrosoft could redirect directly to the backend, but the UX cost\n(~300–500ms of \"dead page\" while the backend processed the OAuth\nexchange before its 303 fired) outweighed the architectural\nsimplicity. Mirroring Gmail's frontend-callback pattern gives the\nuser a branded loading state and a smooth transition back to\n`/settings`. The state-only-auth pattern still applies to scenarios\nwhere no frontend page can serve as the callback target (mobile\nnative, CLI, webhook-only flows) — see Standing Decisions for the\nrefined wording.","operationId":"outlook_callback_api_v1_integrations_outlook_callback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutlookCallbackPayload"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutlookCallbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/integrations/outlook/accounts":{"get":{"tags":["integrations"],"summary":"List Outlook Accounts","description":"List outlook-connected accounts — the acting user's own by default;\n`scope=all` is the owner/admin oversight view.\n\nMirrors list_gmail_accounts. The service-level list_email_accounts\nreturns all providers; this route filters to provider='outlook' so\na frontend that only knows about outlook can render cleanly. The\n/gmail/accounts variant filters to gmail. A future provider-agnostic\n`/accounts` route could merge both — deferred until a UI needs it.","operationId":"list_outlook_accounts_api_v1_integrations_outlook_accounts_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"scope","in":"query","required":false,"schema":{"enum":["own","all"],"type":"string","default":"own","title":"Scope"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAccountList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/outlook/accounts/{account_id}":{"delete":{"tags":["integrations"],"summary":"Disconnect Outlook Account","description":"Disconnect an outlook account. Same service entry-point as gmail —\n`disconnect_email_account` provider-branches internally based on the\naccount's `provider` column. Cross-tenant or missing → 404 via the\nNotFoundError that bubbles up from get_email_account.","operationId":"disconnect_outlook_account_api_v1_integrations_outlook_accounts__account_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Account Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/google-calendar/authorize":{"get":{"tags":["integrations"],"summary":"Google Calendar Authorize","description":"Build the signed OAuth state + return the Google Calendar consent URL.","operationId":"google_calendar_authorize_api_v1_integrations_google_calendar_authorize_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarAuthorizeResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/integrations/google-calendar/callback":{"post":{"tags":["integrations"],"summary":"Google Calendar Callback","description":"Verify state + finish OAuth exchange + persist the connected Google calendar.","operationId":"google_calendar_callback_api_v1_integrations_google_calendar_callback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarCallbackPayload"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarCallbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/integrations/microsoft-calendar/authorize":{"get":{"tags":["integrations"],"summary":"Microsoft Calendar Authorize","description":"Build the signed OAuth state + return the Microsoft calendar consent URL.","operationId":"microsoft_calendar_authorize_api_v1_integrations_microsoft_calendar_authorize_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarAuthorizeResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/integrations/microsoft-calendar/callback":{"post":{"tags":["integrations"],"summary":"Microsoft Calendar Callback","description":"Verify state + finish OAuth exchange + persist the connected Microsoft calendar.","operationId":"microsoft_calendar_callback_api_v1_integrations_microsoft_calendar_callback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarCallbackPayload"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarCallbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/integrations/calendar-accounts":{"get":{"tags":["integrations"],"summary":"List Calendar Accounts","description":"List the tenant's connected calendar accounts (both providers).","operationId":"list_calendar_accounts_api_v1_integrations_calendar_accounts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarAccountList"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/integrations/calendar-accounts/{account_id}":{"delete":{"tags":["integrations"],"summary":"Disconnect Calendar Account","description":"Disconnect a calendar account. Per-user ownership is enforced in the\nservice (NotFoundError → 404 for a non-owned / cross-tenant row).","operationId":"disconnect_calendar_account_api_v1_integrations_calendar_accounts__account_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Account Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/mailbox-scans":{"post":{"tags":["integrations","integrations"],"summary":"Create Scan","description":"Start (or rejoin an in-flight) mailbox scan for a connected account.","operationId":"create_scan_api_v1_integrations_mailbox_scans_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailboxScanJobOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/mailbox-scans/self-build-reveal":{"get":{"tags":["integrations","integrations"],"summary":"Self Build Reveal","description":"The ``/today`` self-build reveal: the latest staged cold-start backfill\nawaiting one-tap acceptance, or ``null`` when there's nothing to reveal\n(M-SelfBuild-b). Declared before ``/{job_id}`` so this literal path isn't\nshadowed by the UUID route.","operationId":"self_build_reveal_api_v1_integrations_mailbox_scans_self_build_reveal_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/SelfBuildRevealOut"},{"type":"null"}],"title":"Response Self Build Reveal Api V1 Integrations Mailbox Scans Self Build Reveal Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/integrations/mailbox-scans/{job_id}":{"get":{"tags":["integrations","integrations"],"summary":"Get Scan","description":"Poll one scan's status + progress counters.","operationId":"get_scan_api_v1_integrations_mailbox_scans__job_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailboxScanJobOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/mailbox-scans/{job_id}/candidates":{"get":{"tags":["integrations","integrations"],"summary":"List Candidates","description":"The reviewable candidate page, highest-score-first.","operationId":"list_candidates_api_v1_integrations_mailbox_scans__job_id__candidates_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Job Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanCandidatePage"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/mailbox-scans/{job_id}/commit":{"post":{"tags":["integrations","integrations"],"summary":"Commit Scan","description":"Import the opt-in subset → create/link contacts (user batch audit).","operationId":"commit_scan_api_v1_integrations_mailbox_scans__job_id__commit_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Job Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanCommitRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanCommitResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/mailbox-scans/{job_id}/commit-all":{"post":{"tags":["integrations","integrations"],"summary":"Commit All","description":"Bulk-accept the reveal's high-confidence batch (M-SelfBuild-b): commit the\n``included`` candidates of a scan job in one tap. The ids are resolved\nserver-side, so the client never round-trips the full cold-start backfill.","operationId":"commit_all_api_v1_integrations_mailbox_scans__job_id__commit_all_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Job Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanCommitResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/import":{"post":{"tags":["import"],"summary":"Upload Import File","description":"Upload a CSV or Excel file and receive auto-suggested field mappings.","operationId":"upload_import_file_api_v1_import_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"entity_type","in":"query","required":false,"schema":{"type":"string","default":"contacts","title":"Entity Type"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_import_file_api_v1_import_post"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["import"],"summary":"List Import Jobs","description":"List all import jobs for the current tenant, newest first.","operationId":"list_import_jobs_api_v1_import_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ImportJobListItem_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/import/{import_job_id}/suggest-ai":{"post":{"tags":["import"],"summary":"Suggest Ai Field Mappings","description":"AI-assist the mapping step (M-Migrate-a): fill columns the deterministic\nmapper + vendor preset left unmapped with tenant-LLM suggestions.\n\nRead-only — the human still confirms via PUT /{id}/mappings (the confirm\ngate). `sheet` selects the Excel worksheet (GAP-138; None = active);\n`header_row` selects the header row (GAP-164; None = auto-detect). Degrades\nto the deterministic baseline (`ai_available=False`) when the tenant LLM is\nunavailable; never 500s on an AI failure.","operationId":"suggest_ai_field_mappings_api_v1_import__import_job_id__suggest_ai_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"import_job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Import Job Id"}},{"name":"sheet","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sheet"}},{"name":"header_row","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Header Row"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiSuggestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/import/{import_job_id}/columns":{"get":{"tags":["import"],"summary":"Get Import File Columns","description":"Re-derive the deterministic column suggestions for a chosen Excel sheet\n(GAP-138) or header row (GAP-164) — what the wizard renders when the user picks\na different sheet or nudges the header-row corrector. Read-only; the choice\nlands at confirm (PUT /{id}/mappings).","operationId":"get_import_file_columns_api_v1_import__import_job_id__columns_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"import_job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Import Job Id"}},{"name":"sheet","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sheet"}},{"name":"header_row","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Header Row"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/import/{import_job_id}/mappings":{"put":{"tags":["import"],"summary":"Confirm Field Mappings","description":"Confirm field mappings and receive a validation preview of the first 10 rows.","operationId":"confirm_field_mappings_api_v1_import__import_job_id__mappings_put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"import_job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Import Job Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FieldMappingsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/import/{import_job_id}/execute":{"post":{"tags":["import"],"summary":"Execute Import","description":"Kick off the background import task. Returns immediately.","operationId":"execute_import_api_v1_import__import_job_id__execute_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"import_job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Import Job Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/import/{import_job_id}":{"get":{"tags":["import"],"summary":"Get Import Job","description":"Poll the current status and progress counters of an import job.","operationId":"get_import_job_api_v1_import__import_job_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"import_job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Import Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportJobDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/import/{import_job_id}/errors":{"get":{"tags":["import"],"summary":"Download Error Report","description":"Download the error report CSV for a completed import that had validation failures.","operationId":"download_error_report_api_v1_import__import_job_id__errors_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"import_job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Import Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/migrations":{"post":{"tags":["migrations"],"summary":"Upload Migration","description":"Upload a ZIP or multiple CSV/Excel files and receive the classified migration job.\n\nEach file is unpacked (ZIPs), classified to an entity (companies | contacts |\ndeals) by filename + header fingerprint, and stored. Unparseable files are\nreported per-file, never fatal. The human confirms entity + mappings next.","operationId":"upload_migration_api_v1_migrations_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_migration_api_v1_migrations_post"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrationJobDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["migrations"],"summary":"List Migration Jobs","description":"List all migration jobs for the current tenant, newest first.","operationId":"list_migration_jobs_api_v1_migrations_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_MigrationJobListItem_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/migrations/{migration_job_id}/files/{file_id}/suggest-ai":{"post":{"tags":["migrations"],"summary":"Suggest Migration File Mappings","description":"AI-assist one file's column mapping (M-Migrate-b): fill the columns the\ndeterministic mapper + vendor preset left unmapped with tenant-LLM suggestions.\n\nRead-only — the human still confirms via PUT …/files/{file_id}/mappings (the\nconfirm gate). The ``entity_type`` query param overrides the classifier so the\nwizard can suggest for an unclassified file before confirming its entity; it\nfalls back to the file's classified type. ``sheet`` selects the Excel worksheet\n(GAP-138; None = active) and ``header_row`` the header row (GAP-164; None =\nauto-detect) — both must match the pick the columns view was rendered at, so the\nAI suggestions describe the same grid the user is mapping. Degrades to the\ndeterministic baseline (``ai_available=False``) when the tenant LLM is\nunavailable; never 500s on an AI failure.","operationId":"suggest_migration_file_mappings_api_v1_migrations__migration_job_id__files__file_id__suggest_ai_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"migration_job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Migration Job Id"}},{"name":"file_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"File Id"}},{"name":"entity_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Type"}},{"name":"sheet","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sheet"}},{"name":"header_row","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Header Row"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrationFileSuggestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/migrations/{migration_job_id}/files/{file_id}/mappings":{"put":{"tags":["migrations"],"summary":"Confirm Migration File Mappings","description":"Confirm one file's entity type + field mapping; returns a 10-row preview.\n\nThe human-override gate: correct the classifier (or place an unclassified file)\nand confirm the data-column mapping. The relationship/FK columns are resolved by\nthe executor through the in-job remap, not mapped here. ``value_mappings``\ntranslates source values onto the tenant's vocabulary (v1: deals' ``stage`` —\nthe M-Migrate-c stage-mapping affordance).","operationId":"confirm_migration_file_mappings_api_v1_migrations__migration_job_id__files__file_id__mappings_put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"migration_job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Migration Job Id"}},{"name":"file_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"File Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrationFileMappingsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrationFilePreview"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/migrations/{migration_job_id}/execute":{"post":{"tags":["migrations"],"summary":"Execute Migration","description":"Kick off the background dependency-order migration (companies → contacts →\ndeals, resolving cross-file FKs through the in-job remap). Returns immediately.","operationId":"execute_migration_api_v1_migrations__migration_job_id__execute_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"migration_job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Migration Job Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrationExecuteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/migrations/{migration_job_id}/files/{file_id}/columns":{"get":{"tags":["migrations"],"summary":"Get Migration File Columns","description":"The deterministic mapping baseline for one file (M-Migrate-c) — preset +\nrapidfuzz, no LLM. The wizard's mapping panel renders this; the AI assist\n(POST …/suggest-ai) stays a separate human-triggered call. ``entity_type``\noverrides the classifier (required for an unclassified file). ``sheet`` selects\nthe Excel worksheet (GAP-138; None = active); ``header_row`` selects the header\nrow (GAP-164; None = auto-detect).","operationId":"get_migration_file_columns_api_v1_migrations__migration_job_id__files__file_id__columns_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"migration_job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Migration Job Id"}},{"name":"file_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"File Id"}},{"name":"entity_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Type"}},{"name":"sheet","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sheet"}},{"name":"header_row","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Header Row"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrationFileColumns"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/migrations/{migration_job_id}/files/{file_id}/distinct-values":{"get":{"tags":["migrations"],"summary":"Get Migration File Distinct Values","description":"Distinct raw values of one source column, first-appearance order, capped\n(M-Migrate-c) — the wizard's stage-mapping affordance reads the deals file's\nactual stage labels through this. ``column`` must be one of the file's headers.\n``sheet`` selects the Excel worksheet (GAP-138; None = active) — pass the same\npick the columns view was rendered at so the labels come from that grid (GAP-166).","operationId":"get_migration_file_distinct_values_api_v1_migrations__migration_job_id__files__file_id__distinct_values_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"migration_job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Migration Job Id"}},{"name":"file_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"File Id"}},{"name":"column","in":"query","required":true,"schema":{"type":"string","title":"Column"}},{"name":"sheet","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sheet"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrationFileDistinctValues"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/migrations/{migration_job_id}/files/{file_id}/error-report":{"get":{"tags":["migrations"],"summary":"Download Migration File Error Report","description":"Download one migration file's per-row error CSV (M-Migrate-c; mirrors the\nsingle-file import's error report).","operationId":"download_migration_file_error_report_api_v1_migrations__migration_job_id__files__file_id__error_report_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"migration_job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Migration Job Id"}},{"name":"file_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"File Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/migrations/{migration_job_id}":{"get":{"tags":["migrations"],"summary":"Get Migration Job","description":"Poll a migration job's status + its classified per-file children.","operationId":"get_migration_job_api_v1_migrations__migration_job_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"migration_job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Migration Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrationJobDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/outreach/draft":{"post":{"tags":["ai"],"summary":"Generate Outreach Draft","description":"Generate a personalised outreach email draft for a contact.","operationId":"generate_outreach_draft_api_v1_ai_outreach_draft_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutreachDraftRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutreachDraftResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/ai/analytics/save-chart":{"post":{"tags":["ai"],"summary":"Save Agent Chart","description":"Save an agent-generated ChartSpec to a dashboard as a new tile.\n\nDelegates placement + tile creation + audit to the dashboards service\n(which writes `dashboard.tile_added` + `analytics.agent_chart_saved`).","operationId":"save_agent_chart_api_v1_ai_analytics_save_chart_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveChartRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Save Agent Chart Api V1 Ai Analytics Save Chart Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/ai/chat":{"post":{"tags":["ai"],"summary":"Chat Stream","description":"Conversational AI assistant via Server-Sent Events.\n\nRuns the ChatAgent tool-use loop with real-time event surfacing:\n  - `thinking` events stream the model's reasoning content (Qwen3\n    `<think>` blocks, Anthropic extended thinking) live.\n  - `tool_start` / `tool_end` events bracket every tool execution so\n    the UI can render which data the model is reaching for.\n  - `token` events stream the final answer's content as it's generated\n    (not chunked-after-the-fact like the previous implementation).\n  - `conversation` is emitted first, carrying the conversation id (minted\n    when the request omits one, D5) so the client can associate the stream.\n  - `done` carries token usage and model id.\n  - `error` on provider failures.\n\nPersistence (M-Memory-a, D4/D5): the incoming user turn is persisted before\nstreaming; the assistant's final answer (with tool-call metadata) is persisted\nonce the stream completes.\n\nHistory (M-Stabilize-c, s1): the prompt's prior turns are rebuilt here from\nthe persisted transcript — newest turns kept within `truncate_history`'s\ncharacter budget, oldest dropped first. The client sends only the new turn,\nso a stored assistant reply of any length can never wedge the conversation\nat request validation again.","operationId":"chat_stream_api_v1_ai_chat_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/chat/confirm":{"post":{"tags":["ai"],"summary":"Chat Confirm","description":"Execute a write the assistant proposed in chat, after the user confirms it\n(M-Assistant-b, D7; consumes the durable action row since M-Actions-a, GAP-176).\n\nThe proposal was surfaced as a `proposal` SSE event carrying `action_id`; the\nfrontend posts that id back. The `agent_actions` row is consumed via a\nstatus-guarded atomic claim (`proposed → applied`) — a replayed confirm, an\nalready-decided action, or an expired one refuses with 409 before any\ncapability machinery runs, and the executed args are the row's stored\nvalidated echo, never a client payload. Dispatch still goes through\n`ChatAgent.execute_confirmed_write`, which re-validates those args against the\ncapability's args model (D4), re-runs the preflight, and runs the impl as the\nauthenticated user — whose service-layer audit fires. The claim, the write,\nand the result stamp share one transaction: an execution failure rolls all of\nit back, so the action stays `proposed` and the card's retry keeps working.\nThe model does **not** re-narrate the outcome here (active re-narration stays\nM-AgentLoop-a); instead the trimmed result — the new record's id + label — is\npersisted so the *next* turn observes it (M-Observe-a).","operationId":"chat_confirm_api_v1_ai_chat_confirm_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatConfirmRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Chat Confirm Api V1 Ai Chat Confirm Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/chat/reject":{"post":{"tags":["ai"],"summary":"Chat Reject","description":"Decline a proposed action (M-Actions-a, GAP-176) — the persisted Cancel.\n\nFlips the durable row `proposed → rejected` (audited), so a declined proposal\nis real data and can never be confirmed afterwards. Idempotent: a double-\nclicked Cancel returns 200 with the already-rejected state; an action that was\napplied first 409s; an unknown or another user's action 404s uniformly.","operationId":"chat_reject_api_v1_ai_chat_reject_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRejectRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Chat Reject Api V1 Ai Chat Reject Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/actions":{"get":{"tags":["ai"],"summary":"List Agent Actions","description":"The current user's reviewable agent actions (M-Actions-b, GAP-176): pending\nproposals newest-first plus a recently-decided tail. Owner-scoped — proposals\nare private to the proposing user (the v1 approver), like conversations.\nPast-TTL pending rows surface as `expired`, never as approvable. Pending M33d\ncampaign-send approvals are federated in read-only (tenant-visible, per that\nqueue's own semantics); deciding them stays the campaign endpoints.","operationId":"list_agent_actions_api_v1_ai_actions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentActionListResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/ai/actions/{action_id}/approve":{"post":{"tags":["ai"],"summary":"Approve Agent Action","description":"Approve one pending action from the /approvals surface (M-Actions-b).\n\nIdentical semantics to `/ai/chat/confirm` — both call the one shared\nclaim → execute → stamp path, so a replayed approve 409s, an expired one\n409s + flips, and the executed args are the row's stored validated echo.\nThe write receipt still lands on the originating conversation's transcript\nwhen there is one, so the chat thread shows the APPLIED card next visit.\n\nApproving an `origin='background'` action also resumes its run's\nconversation with one worker turn (M-Runs-b, Q5 — chat's confirm→continue\nparity; best-effort, deduped by the one-active-run index). Chat-origin\napproves never continue: the live chat flow owns its own auto-advance.","operationId":"approve_agent_action_api_v1_ai_actions__action_id__approve_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"action_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Action Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentActionDecisionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/actions/{action_id}/reject":{"post":{"tags":["ai"],"summary":"Reject Agent Action","description":"Reject one pending action from the /approvals surface (M-Actions-b) —\nthe same persisted, idempotent decline as `/ai/chat/reject`.","operationId":"reject_agent_action_api_v1_ai_actions__action_id__reject_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"action_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Action Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentActionDecisionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/actions/{action_id}":{"patch":{"tags":["ai"],"summary":"Edit Agent Action","description":"Edit a pending action's args before approving it (M-Actions-b).\n\nServer-side only: the args are re-validated against the capability's model,\nthe preflight re-runs (a refusal 422s here instead of at execute time), and\nthe summary + preview are recomputed by the same code a fresh proposal uses —\nthe returned card material can't drift from what approve will execute. The\nrow stays `proposed`; editing a decided or expired action 409s. Audited with\nthe args before/after.","operationId":"edit_agent_action_api_v1_ai_actions__action_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"action_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Action Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentActionEditRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentActionOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/actions/apply":{"post":{"tags":["ai"],"summary":"Apply Agent Actions Batch","description":"Apply all N pending actions (M-Actions-b) — the batch form of approve.\n\nEach action goes through the same shared claim → execute → stamp path as a\nsingle approve, committed and audited **per item**, in request order: one\nitem's failure never rolls back or blocks the others (the M-Calls-e\nreviewed-batch precedent). The response reports each row's real resulting\nstate; a failed execution leaves that row `proposed` (still pending,\nretryable), never half-consumed.\n\nApplied `origin='background'` items resume their run's conversation — ONE\ncontinuation per affected conversation, after the whole batch (M-Runs-b,\nQ5: batch-collapsed; the one-active-run index is the race floor).","operationId":"apply_agent_actions_batch_api_v1_ai_actions_apply_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentActionBatchApplyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentActionBatchApplyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/runs":{"post":{"tags":["ai"],"summary":"Create Agent Run","description":"Start a background agent run (M-Runs-a): persist the user turn, create the\nrun, commit, enqueue the executor — 202 with the handle to poll.\n\nMirrors `/ai/chat`'s conversation semantics (reuse 404s cross-tenant/cross-\nuser; absent mints one titled from the message). One active run per\nconversation — a second 409s (the partial unique index is the race floor\nbeneath the service pre-check). The queue is checked BEFORE anything is\ncreated, so a queue outage can never strand a `queued` row that would block\nthe conversation's active slot.","operationId":"create_agent_run_api_v1_ai_runs_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRunCreateRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRunCreateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["ai"],"summary":"List Agent Runs","description":"The current user's runs, newest first (owner-scoped, like conversations).\n`?active=true` returns only queued/running — the global working indicator's\nquery.","operationId":"list_agent_runs_api_v1_ai_runs_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"active","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Active"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRunListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/runs/{run_id}":{"get":{"tags":["ai"],"summary":"Get Agent Run","description":"One run's lifecycle view (owner-scoped; uniform 404 otherwise). While the\nrun is active the M-Feedback-b progress snapshot rides along — tenant-bound\nat read, absent once a terminal status is stamped.","operationId":"get_agent_run_api_v1_ai_runs__run_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRunOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/runs/{run_id}/cancel":{"post":{"tags":["ai"],"summary":"Cancel Agent Run","description":"Cancel a queued or running run (cooperative: the executor observes the\nflip at its next tool boundary and stops). Idempotent on repeat; 409 once\ncompleted/failed; uniform 404 cross-user.","operationId":"cancel_agent_run_api_v1_ai_runs__run_id__cancel_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Run Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRunOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/run-schedules":{"get":{"tags":["ai"],"summary":"List Run Schedules","description":"The scheduled-work catalog (M-Runs-b): every code-defined run template\nmerged with the tenant's per-template row. Admin-gated like the autonomy\nroutes — schedules spend tokens on a cadence, so configuring them is\nworkspace policy, not personal preference.","operationId":"list_run_schedules_api_v1_ai_run_schedules_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunScheduleListResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/ai/run-schedules/{template_key}":{"put":{"tags":["ai"],"summary":"Upsert Run Schedule","description":"Enable/disable a template or change its cadence (admin+, audited). The\ncaller becomes the run-as user: the schedule's runs execute as — and\nnotify — whoever last configured it. 404 on an unknown template key.","operationId":"upsert_run_schedule_api_v1_ai_run_schedules__template_key__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"template_key","in":"path","required":true,"schema":{"type":"string","title":"Template Key"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunScheduleUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunScheduleOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/chat/continue":{"post":{"tags":["ai"],"summary":"Chat Continue","description":"Continue the agent's plan after a confirmed write (M-AgentLoop-c).\n\nThe frontend calls this immediately after `/chat/confirm` resolves and the\nproposal card flips to APPLIED. It re-enters the ChatAgent loop over the\nconversation's persisted transcript — which now ends in the just-applied\n`[applied …]` receipt (M-Observe-a) — with NO new user message, plus a\nsynthetic continuation steer standing in for the \"continue\" the user used to\nhave to type by hand. The model observes the new record and either proposes\nthe next step (its own confirm card) or reports the request complete. This is\nthe active half of the confirm flow that M-Assistant-b/M-AgentLoop-a deferred.\n\nIdempotent + best-effort by construction: `chat_stream` only ever *proposes*\n(writes stay confirm-gated), so this path can never re-execute the confirmed\nwrite, and a continuation that fails (provider / quota / budget) never implies\nthe write failed — the write already committed in `/chat/confirm`. Per-write\nconfirm is preserved: the continuation stops at the NEXT write's proposal\ncard; it does not auto-apply downstream writes.\n\nFull `/chat` parity: the agent is built with the same prompt_context, role,\nplan, and web-search gate, so the continuation inherits the M-Tier-b1 plan\nrender-filter + adapter gate and the delegate backstop (GAP-054) unchanged.","operationId":"chat_continue_api_v1_ai_chat_continue_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatContinueRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/conversations":{"get":{"tags":["ai"],"summary":"List Conversations","description":"A keyset page of the current user's own conversations, newest-active first\n(M-Memory-a). Pass `next_cursor` back verbatim to page (404-free; an invalid\ncursor 422s).","operationId":"list_conversations_api_v1_ai_conversations_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationListPage"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/conversations/{conversation_id}":{"get":{"tags":["ai"],"summary":"Get Conversation","description":"One conversation with its full message log, chronological ascending\n(M-Memory-a). 404 if missing, soft-deleted, in another tenant, or owned by\nanother user.","operationId":"get_conversation_api_v1_ai_conversations__conversation_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Conversation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["ai"],"summary":"Rename Conversation","description":"Rename a conversation (M-Memory-b, D8). 404 if missing, soft-deleted, in\nanother tenant, or owned by another user.","operationId":"rename_conversation_api_v1_ai_conversations__conversation_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Conversation Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationRenameRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["ai"],"summary":"Delete Conversation","description":"Soft-delete a conversation (M-Memory-b, D8) — clears it from the list\nwithout losing the audit trail. 404 if already deleted / missing /\ncross-tenant / owned by another user.","operationId":"delete_conversation_api_v1_ai_conversations__conversation_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Conversation Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/usage":{"get":{"tags":["ai"],"summary":"Get Ai Usage","description":"AI usage summary — totals + by-task_type + by-model with read-time\nestimated cost (USD). Defaults to the caller's own usage; `scope=tenant`\nreturns tenant-wide totals and requires an admin role (DA3).","operationId":"get_ai_usage_api_v1_ai_usage_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"scope","in":"query","required":false,"schema":{"enum":["own","tenant"],"type":"string","default":"own","title":"Scope"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIUsageStats"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/usage/timeseries":{"get":{"tags":["ai"],"summary":"Get Ai Usage Timeseries","description":"Calls / tokens / estimated cost bucketed by UTC day or month (DA5).\nSame scope semantics as `/usage`.","operationId":"get_ai_usage_timeseries_api_v1_ai_usage_timeseries_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"grain","in":"query","required":false,"schema":{"enum":["daily","monthly"],"type":"string","default":"daily","title":"Grain"}},{"name":"scope","in":"query","required":false,"schema":{"enum":["own","tenant"],"type":"string","default":"own","title":"Scope"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageTimeseriesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/usage/by-user":{"get":{"tags":["ai"],"summary":"Get Ai Usage By User","description":"Tenant-wide per-user usage breakdown, busiest first (admin-only, DA3).\nUser rows are enriched with name + email for display.","operationId":"get_ai_usage_by_user_api_v1_ai_usage_by_user_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUsageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/usage/export":{"get":{"tags":["ai"],"summary":"Export Ai Usage","description":"Synchronous CSV export of the per-(task_type, model) breakdown honouring\nthe current scope + date filters (DA4). Writes an `ai_usage.exported` audit\nrow. Same scope semantics as `/usage`.","operationId":"export_ai_usage_api_v1_ai_usage_export_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"scope","in":"query","required":false,"schema":{"enum":["own","tenant"],"type":"string","default":"own","title":"Scope"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ai/usage/quota":{"get":{"tags":["ai"],"summary":"Get Ai Usage Quota","description":"The caller's tenant AI-token allowance for the current UTC month — tokens\nused, the plan ceiling, tokens remaining, and whether the monthly quota is\ntripped. Tenant-scoped: every member sees the shared tenant meter (the quota\nis per-tenant, not per-user), so no scope/admin split (GAP-074).","operationId":"get_ai_usage_quota_api_v1_ai_usage_quota_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIQuotaStatus"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/ai/config":{"get":{"tags":["ai"],"summary":"Get Ai Config","description":"Get the tenant's current AI configuration.","operationId":"get_ai_config_api_v1_ai_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Ai Config Api V1 Ai Config Get"}}}}},"security":[{"HTTPBearer":[]}]},"patch":{"tags":["ai"],"summary":"Update Ai Config","description":"Update AI configuration — post-MVP feature.","operationId":"update_ai_config_api_v1_ai_config_patch","responses":{"501":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Update Ai Config Api V1 Ai Config Patch"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/analytics/query":{"post":{"tags":["analytics"],"summary":"Query Chart Route","description":"Execute a ChartSpec and return the result envelope.\n\nThe request body IS the `ChartSpec` (no wrapper) so the chart\nbuilder can serialise the spec it's editing directly without an\nextra `{\"spec\": ...}` indirection. The response IS the\n`ChartResult` from `analytics_query.executor` — same contract the\ncache stores.","operationId":"query_chart_route_api_v1_analytics_query_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChartSpec"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChartResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/analytics/field-registry":{"get":{"tags":["analytics"],"summary":"Field Registry Route","description":"Return the merged static + tenant-custom field registry.\n\nStatic fields ride the TS mirror at `frontend/src/types/analytics-\nquery.ts`; this endpoint exists because tenant-defined custom\nfields (stored in `tenant.settings.custom_field_schema`) need to\nbe merged in per-request. The frontend renders the chart-builder\npicker from this merged tree.","operationId":"field_registry_route_api_v1_analytics_field_registry_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Field Registry Route Api V1 Analytics Field Registry Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/dashboards":{"get":{"tags":["dashboards"],"summary":"List Dashboards Route","description":"List every dashboard visible to the caller, with tile counts.\n\nVisibility is handled by RLS on `dashboards` — the M32a\n`tenant_visibility` policy returns the union of tenant_shared rows\n+ the caller's own private drafts. Ordering: system presets first\n(alphabetical), then custom (alphabetical) so the picker shows the\neight curated dashboards before any user-built ones.\n\nDual-decorator pattern (`\"\"` + `\"/\"`) per the MAP.md live-pattern:\nNext dev rewrites trailing-slash variants and the Authorization\nheader gets dropped on a 307 redirect to an absolute backend URL.\nRegistering both keeps the slash form first-class.","operationId":"list_dashboards_route_api_v1_dashboards_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardListResponse"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["dashboards"],"summary":"Create Dashboard Route","description":"Create a new user-owned dashboard.\n\n`owner_user_id` is sourced from the JWT, never the body. The\nresponse is the fresh `DashboardRead` with `tiles=[]` — the M32c2\nchart builder fills tiles in via separate endpoints.","operationId":"create_dashboard_route_api_v1_dashboards_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/dashboards/{dashboard_id}":{"get":{"tags":["dashboards"],"summary":"Get Dashboard Route","description":"Fetch a single dashboard + its tiles ordered by `(position_y, position_x)`.\n\nReturns 404 — never 403 — for dashboards the caller can't see.\nRLS filters out invisible rows at the SQL layer, so the service\nlayer can't distinguish \"doesn't exist\" from \"you don't have\naccess\" and the API surface deliberately maps both to 404. This\nmatches the M5 contact lookup pattern (and ~every other\ntenant-scoped fetch in the codebase) — cross-tenant existence\nprobing is not a feature.","operationId":"get_dashboard_route_api_v1_dashboards__dashboard_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["dashboards"],"summary":"Update Dashboard Route","description":"Rename / re-describe / re-publish a dashboard.\n\nMembers can mutate dashboards they own; admin/owner role can\nmutate any in the tenant. System dashboards are immutable — PATCH\nreturns 403 with explicit reason. Cross-tenant + unknown ids\nreturn 404 uniformly (no existence leakage).\n\n`description` uses the explicit-null-vs-omitted pattern. The\nPydantic model accepts `null` but defaults to `None` when omitted;\nwe use `exclude_unset` on the model to distinguish.","operationId":"update_dashboard_route_api_v1_dashboards__dashboard_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["dashboards"],"summary":"Delete Dashboard Route","description":"Hard-delete a user-created dashboard. Tiles cascade via FK.\n\nSystem dashboards refuse deletion (403). Members can only delete\ndashboards they own; admin/owner can delete any in the tenant.","operationId":"delete_dashboard_route_api_v1_dashboards__dashboard_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboards/{dashboard_id}/clone":{"post":{"tags":["dashboards"],"summary":"Clone Dashboard Route","description":"Clone a visible dashboard + all of its tiles into a new copy.\n\nAny role can clone any *visible* dashboard (own private or\ntenant_shared or system). The copy is `is_system=false`,\n`visibility='private'`, owner = caller. Auto-names follow the\nM26c slot-search pattern (`\"{name} (copy)\"` / `\"... (copy 2)\"` /\n...) unless `new_name` is supplied.","operationId":"clone_dashboard_route_api_v1_dashboards__dashboard_id__clone_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardClone"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboards/{dashboard_id}/tiles":{"post":{"tags":["dashboards"],"summary":"Add Tile Route","description":"Add a tile to a dashboard.\n\nQ5 RBAC: members can add tiles to their own dashboards; admin/owner\ncan add to any. System dashboards refuse (403).","operationId":"add_tile_route_api_v1_dashboards__dashboard_id__tiles_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TileCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardTileRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboards/{dashboard_id}/tiles/{tile_id}":{"patch":{"tags":["dashboards"],"summary":"Update Tile Route","description":"Update a tile's name, chart spec, or position.\n\nQ5 RBAC same as dashboard-level mutations. System dashboards\nrefuse (403).","operationId":"update_tile_route_api_v1_dashboards__dashboard_id__tiles__tile_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}},{"name":"tile_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tile Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TileUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardTileRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["dashboards"],"summary":"Delete Tile Route","description":"Remove a tile from a dashboard.\n\nQ5 RBAC same as dashboard-level mutations. System dashboards\nrefuse (403).","operationId":"delete_tile_route_api_v1_dashboards__dashboard_id__tiles__tile_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}},{"name":"tile_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tile Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboards/{dashboard_id}/layout":{"patch":{"tags":["dashboards"],"summary":"Update Layout Route","description":"Bulk-update tile positions after a drag-end.\n\nThe editor sends every tile's `(x, y, w, h)` on each layout commit.\nServer stores the full snapshot so concurrent edits don't\ninterleave partial states.","operationId":"update_layout_route_api_v1_dashboards__dashboard_id__layout_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TileLayoutUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboards/{dashboard_id}/exports":{"get":{"tags":["exports"],"summary":"List Exports","operationId":"list_exports_api_v1_dashboards__dashboard_id__exports_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledExportListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["exports"],"summary":"Create Export","operationId":"create_export_api_v1_dashboards__dashboard_id__exports_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledExportCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledExportRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboards/{dashboard_id}/exports/{export_id}":{"get":{"tags":["exports"],"summary":"Get Export","operationId":"get_export_api_v1_dashboards__dashboard_id__exports__export_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}},{"name":"export_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Export Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledExportRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["exports"],"summary":"Update Export","operationId":"update_export_api_v1_dashboards__dashboard_id__exports__export_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}},{"name":"export_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Export Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledExportUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledExportRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["exports"],"summary":"Delete Export","operationId":"delete_export_api_v1_dashboards__dashboard_id__exports__export_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}},{"name":"export_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Export Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboards/{dashboard_id}/exports/{export_id}/runs":{"get":{"tags":["exports"],"summary":"List Export Runs","operationId":"list_export_runs_api_v1_dashboards__dashboard_id__exports__export_id__runs_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}},{"name":"export_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Export Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportRunListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboards/{dashboard_id}/alerts":{"get":{"tags":["alerts"],"summary":"List Alerts","operationId":"list_alerts_api_v1_dashboards__dashboard_id__alerts_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardAlertListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["alerts"],"summary":"Create Alert","operationId":"create_alert_api_v1_dashboards__dashboard_id__alerts_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardAlertCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardAlertRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboards/{dashboard_id}/alerts/{alert_id}":{"get":{"tags":["alerts"],"summary":"Get Alert","operationId":"get_alert_api_v1_dashboards__dashboard_id__alerts__alert_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}},{"name":"alert_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Alert Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardAlertRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["alerts"],"summary":"Update Alert","operationId":"update_alert_api_v1_dashboards__dashboard_id__alerts__alert_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}},{"name":"alert_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Alert Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardAlertUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardAlertRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["alerts"],"summary":"Delete Alert","operationId":"delete_alert_api_v1_dashboards__dashboard_id__alerts__alert_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}},{"name":"alert_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Alert Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboards/{dashboard_id}/alerts/{alert_id}/triggers":{"get":{"tags":["alerts"],"summary":"List Alert Triggers","operationId":"list_alert_triggers_api_v1_dashboards__dashboard_id__alerts__alert_id__triggers_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}},{"name":"alert_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Alert Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertTriggerListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/digests/presets":{"get":{"tags":["digests"],"summary":"List Digest Presets","description":"Return the preset dashboards available for digest subscription.","operationId":"list_digest_presets_api_v1_digests_presets_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"additionalProperties":{"type":"string"},"type":"object"},"type":"array","title":"Response List Digest Presets Api V1 Digests Presets Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/digests":{"get":{"tags":["digests"],"summary":"List Subscriptions","operationId":"list_subscriptions_api_v1_digests_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestSubscriptionListResponse"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["digests"],"summary":"Create Subscription","operationId":"create_subscription_api_v1_digests_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestSubscriptionCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestSubscriptionRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/digests/{subscription_id}":{"patch":{"tags":["digests"],"summary":"Update Subscription","operationId":"update_subscription_api_v1_digests__subscription_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Subscription Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestSubscriptionUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestSubscriptionRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["digests"],"summary":"Delete Subscription","operationId":"delete_subscription_api_v1_digests__subscription_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Subscription Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/data-exports":{"post":{"tags":["data-exports"],"summary":"Create Export","description":"Enqueue a new tenant data export.","operationId":"create_export_api_v1_data_exports_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataExportJobRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["data-exports"],"summary":"List Exports","description":"List all export jobs for the current tenant.","operationId":"list_exports_api_v1_data_exports_get","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataExportJobListResponse"}}}}}}},"/api/v1/data-exports/{export_id}/download":{"get":{"tags":["data-exports"],"summary":"Download Export","description":"Download the export ZIP. Single-use; step-up required.","operationId":"download_export_api_v1_data_exports__export_id__download_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"export_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Export Id"}},{"name":"token","in":"query","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/tools":{"get":{"tags":["mcp"],"summary":"List Mcp Tools","description":"Return the curated MCP tool catalogue.\n\nRead-only — no tenant context required, the catalogue is identical across\nall tenants and contains no secrets. Auth-gated so we don't leak the tool\nsurface to unauthenticated callers (defence-in-depth, not load-bearing).","operationId":"list_mcp_tools_api_v1_mcp_tools_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpToolsResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/search":{"get":{"tags":["search"],"summary":"Global Search","operationId":"global_search_api_v1_search_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":100,"default":"","title":"Q"}},{"name":"per_type","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"default":5,"title":"Per Type"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlobalSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/usage":{"get":{"tags":["admin"],"summary":"List Tenant Usage","description":"Per-tenant usage rollup for the dashboard main table.","operationId":"list_tenant_usage_api_v1_admin_usage_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"month","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Month"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TenantUsageRowOut"},"title":"Response List Tenant Usage Api V1 Admin Usage Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/usage/totals":{"get":{"tags":["admin"],"summary":"Get Platform Totals Endpoint","description":"Header-strip totals: gross tokens, est. cost, top-3 tenants.","operationId":"get_platform_totals_endpoint_api_v1_admin_usage_totals_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"month","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Month"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformTotalsOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/usage/tenant/{tenant_id}":{"get":{"tags":["admin"],"summary":"Get Tenant Trend","description":"6-month (default) trend chart for the drill-down side sheet.","operationId":"get_tenant_trend_api_v1_admin_usage_tenant__tenant_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}},{"name":"months","in":"query","required":false,"schema":{"type":"integer","maximum":24,"minimum":1,"default":6,"title":"Months"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MonthlyUsageRowOut"},"title":"Response Get Tenant Trend Api V1 Admin Usage Tenant  Tenant Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/tenants":{"get":{"tags":["admin"],"summary":"List Tenant Directory","description":"Cross-tenant directory for the /admin console: EVERY onboarded tenant with\nhealth-at-a-glance columns (active seats, CRM volume, connected mailboxes, last\nactivity, this-month AI usage + quota). Unlike /usage, the list is NOT filtered\nby this-month AI activity — a tenant that logged no AI calls still appears.\n`month` scopes only the AI-tokens column.","operationId":"list_tenant_directory_api_v1_admin_tenants_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"month","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Month"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":500,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TenantDirectoryRowOut"},"title":"Response List Tenant Directory Api V1 Admin Tenants Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["admin"],"summary":"Create Tenant Endpoint","description":"Provision a brand-new tenant + its first owner (M-Admin-d).\n\nThe one privileged cross-tenant WRITE on the admin surface. A new tenant has\nno RLS context and `tenants` is FORCE-RLS (migration 0066), so the seed runs\non the BYPASSRLS maintenance DSN (SD-M-Admin-d-tenant-provisioning-under-rls),\nreusing the same `tenant_provisioning.provision_tenant` core as\n`scripts/create_tenant.py`. The owner is created password-less and emailed a\n72h set-password link (welcome-email mode). Duplicate slug/email → 409;\nmalformed slug/plan → 422.","operationId":"create_tenant_endpoint_api_v1_admin_tenants_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTenantRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTenantResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/tenants/{tenant_id}":{"get":{"tags":["admin"],"summary":"Get Tenant Detail Endpoint","description":"Full per-tenant oversight profile for the /admin/tenants/[id] page: identity +\nplan/quota, the users list, connected + revoked mailboxes, non-deleted CRM counts,\nagent-activity figures (actions by status, runs, enabled schedules), and the recent\naudit tail. 404 for an unknown tenant id. The AI trend is NOT here — the page reuses\nGET /admin/usage/tenant/{id}.","operationId":"get_tenant_detail_endpoint_api_v1_admin_tenants__tenant_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantDetailOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/usage/tenant/{tenant_id}/reset-quota":{"post":{"tags":["admin"],"summary":"Reset Tenant Quota Endpoint","description":"Ops \"Reset quota\" action — flips `ai_quota_exceeded` to FALSE on the\ntarget tenant + writes the standard `tenant.ai_quota_reset` audit row.\nIdempotent (no-op on already-not-tripped).","operationId":"reset_tenant_quota_endpoint_api_v1_admin_usage_tenant__tenant_id__reset_quota_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/usage/tenant/{tenant_id}/limit":{"patch":{"tags":["admin"],"summary":"Update Tenant Limit Endpoint","description":"Ops \"Adjust limit\" action — per-tenant override of the 20M default.\nWrites a `tenant.ai_monthly_limit_updated` audit row with before/after.","operationId":"update_tenant_limit_endpoint_api_v1_admin_usage_tenant__tenant_id__limit_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMonthlyLimitRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMonthlyLimitResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/usage/tenant/{tenant_id}/plan":{"patch":{"tags":["admin"],"summary":"Set Tenant Plan Endpoint","description":"Ops \"Set plan\" action (GAP-054) — sets `tenant.plan` and seeds\n`ai_monthly_token_limit` from the plan matrix. Writes a `tenant.plan_updated`\naudit row with before/after. The inert entitlement layer (M-Tier-a) reads the\nplan nowhere yet; enforcement arrives with M-Tier-b.","operationId":"set_tenant_plan_endpoint_api_v1_admin_usage_tenant__tenant_id__plan_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTenantPlanRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTenantPlanResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/tenants/{tenant_id}/deletion":{"post":{"tags":["admin"],"summary":"Schedule Tenant Deletion","description":"Grace-schedule deletion of an EXISTING tenant from the /admin console (M-Admin-e).\n\nA per-tenant mutation, so it runs on THIS request session under the target\ntenant's RLS context (SD-M-Admin-a), NOT the maintenance engine (that was\nM-Admin-d's create-only exception). Flows through the same 30-day grace, the\npending-deletions panel, and the auto `execute_deletion` purge as an owner\nself-serve deletion; the row records `initiation_actor_type='platform_admin'`.\n409 when a deletion is already pending for the tenant.","operationId":"schedule_tenant_deletion_api_v1_admin_tenants__tenant_id__deletion_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantDeletionInitiate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantDeletionRequestRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/tenant-deletions":{"get":{"tags":["admin"],"summary":"List Pending Deletions","operationId":"list_pending_deletions_api_v1_admin_tenant_deletions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TenantDeletionRequestRead"},"type":"array","title":"Response List Pending Deletions Api V1 Admin Tenant Deletions Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/admin/tenant-deletions/{deletion_id}":{"delete":{"tags":["admin"],"summary":"Admin Cancel Deletion","operationId":"admin_cancel_deletion_api_v1_admin_tenant_deletions__deletion_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"deletion_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Deletion Id"}},{"name":"X-CSRF-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Csrf-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantDeletionRequestRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/demo-requests":{"post":{"tags":["demo-requests"],"summary":"Submit Demo Request","operationId":"submit_demo_request_api_v1_demo_requests_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoRequestCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"boolean"},"type":"object","title":"Response Submit Demo Request Api V1 Demo Requests Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/render/dashboards/{dashboard_id}":{"get":{"tags":["render"],"summary":"Get Render Dashboard","operationId":"get_render_dashboard_api_v1_render_dashboards__dashboard_id__get","parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}},{"name":"token","in":"query","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardRead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/render/dashboards/{dashboard_id}/tiles/{tile_id}/data":{"get":{"tags":["render"],"summary":"Get Render Tile Data","operationId":"get_render_tile_data_api_v1_render_dashboards__dashboard_id__tiles__tile_id__data_get","parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Dashboard Id"}},{"name":"tile_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tile Id"}},{"name":"token","in":"query","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChartResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/webhooks/outlook/notifications":{"post":{"tags":["webhooks"],"summary":"Outlook Notifications","description":"Public Microsoft Graph subscription endpoint. Two modes: (1) validation-token handshake — Graph POSTs with `?validationToken=<token>` during subscription create; we echo the token back as `text/plain` 200 within 10s. (2) notification delivery — Graph POSTs JSON `{value: [...]}`; we verify `clientState` byte-for-byte against `OUTLOOK_WEBHOOK_CLIENT_STATE` and enqueue one `handle_outlook_notification` ARQ task per entry.","operationId":"outlook_notifications_api_v1_webhooks_outlook_notifications_post","parameters":[{"name":"validationToken","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Validationtoken"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/webhooks/resend":{"post":{"tags":["webhooks"],"summary":"Resend Events","description":"Public Resend webhook endpoint (M-Stabilize-b s6, visibility-only per D2). Verifies the svix-scheme signature (`svix-id` / `svix-timestamp` / `svix-signature` headers, HMAC-SHA256 keyed with `RESEND_WEBHOOK_SIGNING_SECRET`) over the raw body, then hands the event to `services/resend_webhook.process_resend_event` — structured log always, Sentry event + audit row for bounce/complaint/failure. No data mutation.","operationId":"resend_events_api_v1_webhooks_resend_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/webhooks/inbound":{"post":{"tags":["webhooks"],"summary":"Inbound Events","description":"Public inbound-email capture endpoint (M-Inbound-a, platform-audit D1). Verifies the svix-scheme signature against `INBOUND_WEBHOOK_SIGNING_SECRET` (separate from the system-email `RESEND_WEBHOOK_SIGNING_SECRET`), parses a Resend `email.received` payload, recovers the owning tenant from the HMAC self-describing inbound-address token, dedupes on the Message-ID, and enqueues `handle_inbound_email`. Capture is opt-in per tenant + known-contacts only — the heavy work is in the ARQ task.","operationId":"inbound_events_api_v1_webhooks_inbound_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/webhooks/calls/transcription/{token}":{"post":{"tags":["webhooks"],"summary":"Deepgram Transcription Callback","description":"Public Deepgram async-transcription callback (M-Calls-a, GAP-050). Deepgram POSTs the transcript here when async STT completes. Deepgram callbacks carry no provider signature, so the per-job HMAC token in the URL path is the credential (design §4): it binds `{call_recording_id, tenant_id}` and is verified before the payload is accepted. Dedupes on the recording id, then enqueues `persist_call_transcript` (the heavy persist + transcribe-then-delete is in the ARQ task). Shared, identical shape with the later Recall.ai meeting-bot completion webhook (M-Calls-c).","operationId":"deepgram_transcription_callback_api_v1_webhooks_calls_transcription__token__post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/webhooks/calls/recall":{"post":{"tags":["webhooks"],"summary":"Recall Bot Webhook","description":"Public Recall.ai meeting-bot completion webhook (M-Calls-c, GAP-050). Recall's ACCOUNT-level webhook is Svix-signed (the same scheme as the Resend / inbound webhooks) — the signature authenticates Recall; the per-recording binding rides the bot `metadata` token (`data.bot.metadata.token`), NOT the URL (unlike the per-job Deepgram callback). On `transcript.done` it verifies the token, dedupes, and enqueues `ingest_meeting_bot_recording` (fetch artifacts → hydrate the schedule-time stub → summarise → delete media); on `bot.fatal` or `transcript.failed` it enqueues `fail_meeting_bot_recording`. Every other event is a 200 no-op.","operationId":"recall_bot_webhook_api_v1_webhooks_calls_recall_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/track/open/{token}":{"get":{"tags":["tracking"],"summary":"Track Open","description":"1×1 transparent GIF. Records first-open on the matching OutreachRecord.\n\nAlways returns the pixel — token errors are silently ignored so we\ndon't surface \"this token is invalid\" diagnostics to a recipient's\nmail client. Real diagnostics surface in structured logs.","operationId":"track_open_track_open__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/track/click/{token}":{"get":{"tags":["tracking"],"summary":"Track Click","description":"Records the click and 302s to the destination URL.\n\nPer the M28 contract: multiple clicks each captured, latest\n`clicked_url` retained. Tampered tokens 400 — unlike the pixel\nendpoint, a forged click is a real navigation attempt and surfacing\nthe failure to the user is better than a silent redirect to a URL\nthey didn't intend to visit.","operationId":"track_click_track_click__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","maxLength":2048,"description":"Destination URL","title":"To"},"description":"Destination URL"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/u/digest":{"get":{"tags":["digest"],"summary":"Digest Unsubscribe Page","description":"Confirmation landing page — a POST button, never a GET-side mutation.","operationId":"digest_unsubscribe_page_u_digest_get","parameters":[{"name":"token","in":"query","required":false,"schema":{"type":"string","default":"","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["digest"],"summary":"Digest Unsubscribe","description":"RFC 8058 one-click + confirm-form submit. Flips cadence off; benign 200.","operationId":"digest_unsubscribe_u_digest_post","parameters":[{"name":"token","in":"query","required":false,"schema":{"type":"string","default":"","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/u/unsubscribe":{"get":{"tags":["unsubscribe"],"summary":"Contact Unsubscribe Page","description":"Confirmation landing page — a POST button, never a GET-side mutation.","operationId":"contact_unsubscribe_page_u_unsubscribe_get","parameters":[{"name":"token","in":"query","required":false,"schema":{"type":"string","default":"","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["unsubscribe"],"summary":"Contact Unsubscribe","description":"Footer confirm-form submit OR RFC 8058 one-click POST. Flips is_subscribed\noff; benign 200, no oracle.","operationId":"contact_unsubscribe_u_unsubscribe_post","parameters":[{"name":"token","in":"query","required":false,"schema":{"type":"string","default":"","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/f/{token}":{"get":{"tags":["public-forms"],"summary":"Get Public Form","operationId":"get_public_form_f__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicFormView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["public-forms"],"summary":"Submit Public Form","operationId":"submit_public_form_f__token__post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicFormSubmit"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicFormSubmitResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/health":{"get":{"tags":["health"],"summary":"Health","operationId":"health_api_v1_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Health Api V1 Health Get"}}}}}}},"/api/v1/health/ready":{"get":{"tags":["health"],"summary":"Health Ready","operationId":"health_ready_api_v1_health_ready_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AICondition":{"properties":{"kind":{"type":"string","const":"ai","title":"Kind"},"source":{"type":"string","enum":["latest_reply","record"],"title":"Source"},"criterion":{"type":"string","maxLength":500,"minLength":1,"title":"Criterion"}},"additionalProperties":false,"type":"object","required":["kind","source","criterion"],"title":"AICondition","description":"An LLM-evaluated semantic condition (M-FlowAI-c, GAP-044).\n\nDiscriminated from the deterministic `{field, op, value}` predicate by\n`kind:\"ai\"`. The deterministic engine stays the execution floor: this is\nevaluated in the ARQ worker (async LLM, never the inline dispatch hot path),\nANDed with the deterministic predicates — the rule fires only if every\ncondition holds. Pure read: the worker loads `source` text for the triggering\nrecord and asks the model whether it satisfies `criterion`."},"AIProvider":{"type":"string","enum":["anthropic","openai","azure","ollama","litellm_custom","limena_managed"],"title":"AIProvider"},"AIQuotaStatus":{"properties":{"tokens_used":{"type":"integer","title":"Tokens Used"},"token_limit":{"type":"integer","title":"Token Limit"},"tokens_remaining":{"type":"integer","title":"Tokens Remaining"},"quota_exceeded":{"type":"boolean","title":"Quota Exceeded"},"plan":{"type":"string","title":"Plan"},"month":{"type":"string","title":"Month"}},"type":"object","required":["tokens_used","token_limit","tokens_remaining","quota_exceeded","plan","month"],"title":"AIQuotaStatus","description":"The tenant's current-month AI-token allowance (GAP-074).\n\nBacks the Settings token meter + the `AIQuotaBanner` near-cap escalation.\n`tokens_used` is the Redis enforcement counter (the value the monthly ceiling\nis checked against); `token_limit` is the tenant's real positive ceiling\n(`tenants.ai_monthly_token_limit`, admin-overridable). This 429\n`AI_QUOTA_EXCEEDED` surface stays DISTINCT from the 402 `PLAN_UPGRADE_REQUIRED`\nfeature gate — separate code, surface, and copy."},"AIUsageStats":{"properties":{"total_requests":{"type":"integer","title":"Total Requests"},"total_input_tokens":{"type":"integer","title":"Total Input Tokens"},"total_output_tokens":{"type":"integer","title":"Total Output Tokens"},"estimated_cost_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Estimated Cost Usd"},"by_task_type":{"additionalProperties":{"$ref":"#/components/schemas/UsageBucketOut"},"type":"object","title":"By Task Type"},"by_model":{"additionalProperties":{"$ref":"#/components/schemas/UsageBucketOut"},"type":"object","title":"By Model"}},"type":"object","required":["total_requests","total_input_tokens","total_output_tokens","estimated_cost_usd","by_task_type","by_model"],"title":"AIUsageStats"},"ActionSpec":{"properties":{"type":{"type":"string","enum":["assign_owner","update_field","create_task","notify","send_email","fire_webhook","ai_draft_email","ai_summarize_task"],"title":"Type"},"params":{"additionalProperties":true,"type":"object","title":"Params"}},"additionalProperties":false,"type":"object","required":["type"],"title":"ActionSpec","description":"One `{type, params}` action."},"ActivityFeed":{"properties":{"events":{"items":{"$ref":"#/components/schemas/TimelineEvent"},"type":"array","title":"Events"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["events"],"title":"ActivityFeed","description":"A keyset page of the feed. ``next_cursor`` is an opaque composite cursor\n``(occurred_at, id)`` to pass back verbatim as ``before`` for the next page;\n``None`` once the feed is exhausted. Treat it as opaque — do not parse it."},"ActorSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"full_name":{"type":"string","title":"Full Name"},"email":{"type":"string","title":"Email"}},"type":"object","required":["id","full_name","email"],"title":"ActorSummary","description":"The user who performed the event. Nullable — system / import events have none."},"AddContactsSourceRequest":{"properties":{"contact_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Contact Ids"}},"additionalProperties":false,"type":"object","required":["contact_ids"],"title":"AddContactsSourceRequest"},"AddListSourceRequest":{"properties":{"list_id":{"type":"string","format":"uuid","title":"List Id"},"dispositions":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Dispositions"}},"additionalProperties":false,"type":"object","required":["list_id"],"title":"AddListSourceRequest"},"AddMembersResponse":{"properties":{"added":{"type":"integer","title":"Added"},"skipped":{"type":"integer","title":"Skipped"},"errors":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Errors","default":[]}},"type":"object","required":["added","skipped"],"title":"AddMembersResponse"},"AddSegmentSourceRequest":{"properties":{"segment_id":{"type":"string","format":"uuid","title":"Segment Id"}},"additionalProperties":false,"type":"object","required":["segment_id"],"title":"AddSegmentSourceRequest"},"AdminMemoryFactResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"content":{"type":"string","title":"Content"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"},"source":{"type":"string","enum":["ai","human"],"title":"Source"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"user_name":{"type":"string","title":"User Name"},"user_email":{"type":"string","title":"User Email"}},"type":"object","required":["id","content","source","created_at","updated_at","user_id","user_name","user_email"],"title":"AdminMemoryFactResponse","description":"One learned fact as returned to an admin governing the tenant (``scope=tenant``) —\nthe fact plus the owning user's identity for grouping. Remove-only: the admin surface\nrenders no edit affordance (governance, not authorship — the owner keeps sole edit)."},"AgentActionBatchApplyRequest":{"properties":{"action_ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":100,"minItems":1,"title":"Action Ids"}},"type":"object","required":["action_ids"],"title":"AgentActionBatchApplyRequest","description":"Apply-all-N: the pending actions to approve, applied individually in\nrequest order (no all-or-nothing transaction — partial failure is honest)."},"AgentActionBatchApplyResponse":{"properties":{"results":{"items":{"$ref":"#/components/schemas/AgentActionBatchItem"},"type":"array","title":"Results"},"applied_count":{"type":"integer","title":"Applied Count"}},"type":"object","required":["results","applied_count"],"title":"AgentActionBatchApplyResponse"},"AgentActionBatchItem":{"properties":{"action_id":{"type":"string","format":"uuid","title":"Action Id"},"status":{"type":"string","title":"Status"},"result":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Result"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["action_id","status"],"title":"AgentActionBatchItem","description":"One batch item's outcome: `status` is the row's real resulting state\n(`applied`; or on failure e.g. `rejected`/`expired`/`proposed` — the latter\nmeaning execution failed and the action is still pending + retryable — or\n`not_found`), with `error` carrying the reason."},"AgentActionDecisionResponse":{"properties":{"action_id":{"type":"string","format":"uuid","title":"Action Id"},"status":{"type":"string","title":"Status"},"result":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Result"},"continuation_run_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Continuation Run Id"}},"type":"object","required":["action_id","status"],"title":"AgentActionDecisionResponse","description":"Outcome of a single approve/reject on the actions surface. `result` is the\ntrimmed write result on approve (id + label), absent on reject.\n`continuation_run_id` (M-Runs-b): the background run the approve triggered\nwhen the action came from one (`origin='background'`) — None on reject,\nchat-origin approves, or when the conversation already has an active run."},"AgentActionEditRequest":{"properties":{"args":{"additionalProperties":true,"type":"object","title":"Args"}},"type":"object","required":["args"],"title":"AgentActionEditRequest","description":"Edit-before-approve (M-Actions-b): the full replacement args for a pending\naction. Validated server-side against the capability's args model, preflight\nre-run, summary + preview recomputed — the row stays `proposed` and the\napprove that follows executes these stored args. Never a client-side liberty:\n-a removed args from the confirm body precisely so edits go through here."},"AgentActionListResponse":{"properties":{"pending":{"items":{"$ref":"#/components/schemas/AgentActionOut"},"type":"array","title":"Pending"},"recently_decided":{"items":{"$ref":"#/components/schemas/AgentActionOut"},"type":"array","title":"Recently Decided"},"pending_count":{"type":"integer","title":"Pending Count"},"send_approvals":{"items":{"$ref":"#/components/schemas/FederatedSendApprovalOut"},"type":"array","title":"Send Approvals"}},"type":"object","required":["pending","recently_decided","pending_count","send_approvals"],"title":"AgentActionListResponse","description":"The /approvals inbox: pending actions newest-first plus a recently-decided\ntail for context. `pending_count` is the badge number for agent actions\n(uncapped, unlike the `pending` list); `send_approvals` federates the M33d\npending sends and counts toward the surface's total review debt."},"AgentActionOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"conversation_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Conversation Id"},"conversation_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Title"},"origin":{"type":"string","title":"Origin"},"capability":{"type":"string","title":"Capability"},"args":{"additionalProperties":true,"type":"object","title":"Args"},"summary":{"type":"string","title":"Summary"},"preview":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Preview"},"side_effect":{"type":"string","title":"Side Effect"},"status":{"type":"string","title":"Status"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Decided At"},"applied_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Applied At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"result":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Result"}},"type":"object","required":["id","conversation_id","conversation_title","origin","capability","args","summary","preview","side_effect","status","created_at","decided_at","applied_at","expires_at","result"],"title":"AgentActionOut","description":"One reviewable agent action on the /approvals surface (M-Actions-b) — the\ndurable row's display material plus its lifecycle state. `status` is the\n*effective* status: a `proposed` row past `expires_at` reads `expired` here\neven before a claim has flipped it in the DB (lazy enforcement, D4)."},"AgentRunCreateRequest":{"properties":{"message":{"type":"string","maxLength":4000,"minLength":1,"title":"Message"},"conversation_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Conversation Id"}},"type":"object","required":["message"],"title":"AgentRunCreateRequest","description":"Start a background agent run (M-Runs-a, GAP-177): one full agent turn\nexecuted server-side, so the work survives the tab. Same message contract as\n`ChatRequest`; `conversation_id` absent mints a conversation exactly as\n`/ai/chat` does. (No view anchor on runs in v1 — the run's recall is keyed on\nthe objective itself.)"},"AgentRunCreateResponse":{"properties":{"run_id":{"type":"string","format":"uuid","title":"Run Id"},"conversation_id":{"type":"string","format":"uuid","title":"Conversation Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["run_id","conversation_id","status"],"title":"AgentRunCreateResponse"},"AgentRunListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AgentRunOut"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"AgentRunListResponse"},"AgentRunOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"conversation_id":{"type":"string","format":"uuid","title":"Conversation Id"},"objective":{"type":"string","title":"Objective"},"status":{"type":"string","title":"Status"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"stats":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Stats"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Finished At"},"progress":{"anyOf":[{"$ref":"#/components/schemas/JobProgress"},{"type":"null"}]}},"type":"object","required":["id","conversation_id","objective","status","error","stats","created_at","started_at","finished_at"],"title":"AgentRunOut","description":"One background run's lifecycle view. `progress` is the ephemeral\nM-Feedback-b snapshot, attached only while the run is active (a terminal\nstatus is the truth once stamped — the transcript carries the content)."},"Aggregation":{"type":"string","enum":["count","sum","avg","distinct_count","min","max"],"title":"Aggregation","description":"Aggregation functions allowed on a `Measure`.\n\n`COUNT` is \"count of rows in the result set\" — no field is required.\nEvery other aggregation requires a field whose `aggregatable_as`\ntuple includes the chosen aggregation.\n\n`RATE` is deliberately NOT here as a standalone aggregation — rates\nare modelled as derived numeric fields in the registry (whose\n`sql_expression` does the CASE WHEN / division). Users select\n`aggregation=AVG` on those derived fields to get the rate value, or\npre-bound charts via the M32b preset pack reference them by key.\nKeeps the grammar bounded and the compiler simple."},"AiAutonomyDestructiveCapability":{"properties":{"name":{"type":"string","title":"Name"},"label":{"type":"string","title":"Label"}},"type":"object","required":["name","label"],"title":"AiAutonomyDestructiveCapability","description":"One destructive capability offered as an L3 per-capability opt-in — served\nfrom the registry so the Settings UI never hardcodes (and drifts from) the\ndestructive set."},"AiAutonomyRead":{"properties":{"level":{"type":"integer","title":"Level"},"overrides":{"additionalProperties":{"type":"integer"},"type":"object","title":"Overrides"},"destructive_capabilities":{"items":{"$ref":"#/components/schemas/AiAutonomyDestructiveCapability"},"type":"array","title":"Destructive Capabilities"}},"type":"object","required":["level","overrides","destructive_capabilities"],"title":"AiAutonomyRead","description":"The tenant autonomy policy (M-Autonomy-a): slider level + per-capability pins."},"AiAutonomyUpdate":{"properties":{"level":{"type":"integer","maximum":3.0,"minimum":0.0,"title":"Level"},"overrides":{"additionalProperties":{"type":"integer"},"type":"object","title":"Overrides"}},"type":"object","required":["level"],"title":"AiAutonomyUpdate","description":"Whole-policy replacement. Range-checked here; capability-name validity and\nthe owner-only level-3 governance are enforced in the service."},"AiPersonalizationSettingsPatch":{"properties":{"system_prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"System Prompt"},"voice_snippet":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Voice Snippet"},"cost_cap_per_100_usd":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Cost Cap Per 100 Usd"},"generation_concurrency":{"anyOf":[{"type":"integer","maximum":20.0,"minimum":1.0},{"type":"null"}],"title":"Generation Concurrency"}},"additionalProperties":false,"type":"object","title":"AiPersonalizationSettingsPatch","description":"Partial-update payload. Field semantics:\n\n- `system_prompt`: explicit `null` resets to the shipped default. Omit\n  to leave unchanged. A blank string is treated as null.\n- `voice_snippet`: explicit `null` clears it. Omit to leave unchanged.\n- `cost_cap_per_100_usd`: monetary, must be in `[0.50, 500.00]`. Omit\n  to leave unchanged. No explicit-null path — to \"reset\", PATCH with\n  the default `5.00`.\n- `generation_concurrency`: integer in `[1, 20]`. Omit to leave\n  unchanged. No explicit-null path."},"AiPersonalizationSettingsRead":{"properties":{"system_prompt":{"type":"string","title":"System Prompt"},"system_prompt_default":{"type":"string","title":"System Prompt Default"},"system_prompt_is_custom":{"type":"boolean","title":"System Prompt Is Custom"},"voice_snippet":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Voice Snippet"},"cost_cap_per_100_usd":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Cost Cap Per 100 Usd"},"generation_concurrency":{"type":"integer","title":"Generation Concurrency"}},"type":"object","required":["system_prompt","system_prompt_default","system_prompt_is_custom","voice_snippet","cost_cap_per_100_usd","generation_concurrency"],"title":"AiPersonalizationSettingsRead","description":"Resolved per-tenant settings — defaults are already substituted.\n\n`system_prompt_default` echoes the shipped baseline alongside the\nresolved value so the UI can render a \"reset queued\" preview without a\nsecond round-trip."},"AiSuggestResponse":{"properties":{"import_job_id":{"type":"string","format":"uuid","title":"Import Job Id"},"entity_type":{"type":"string","title":"Entity Type"},"columns":{"items":{"$ref":"#/components/schemas/ColumnSuggestion"},"type":"array","title":"Columns"},"ai_available":{"type":"boolean","title":"Ai Available"}},"type":"object","required":["import_job_id","entity_type","columns","ai_available"],"title":"AiSuggestResponse","description":"AI-assisted column-mapping suggestions (M-Migrate-a). `columns` carries the\nfull deterministic + preset baseline with AI filling the previously-unmapped\ngaps; `ai_available` is False when the LLM degraded (columns are then\ndeterministic-only — the wizard still works)."},"AlertDeliveryConfig":{"properties":{"email":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Email"},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url"},"in_app":{"type":"boolean","title":"In App","default":true}},"additionalProperties":false,"type":"object","title":"AlertDeliveryConfig"},"AlertTriggerListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AlertTriggerRead"},"type":"array","title":"Items"}},"additionalProperties":false,"type":"object","required":["items"],"title":"AlertTriggerListResponse"},"AlertTriggerRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"alert_id":{"type":"string","format":"uuid","title":"Alert Id"},"observed_value":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Observed Value"},"delivered":{"type":"boolean","title":"Delivered"},"delivery_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Delivery Error"},"triggered_at":{"type":"string","format":"date-time","title":"Triggered At"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"additionalProperties":false,"type":"object","required":["id","alert_id","observed_value","delivered","delivery_error","triggered_at","created_at"],"title":"AlertTriggerRead"},"AntiIcpItem":{"properties":{"reason":{"type":"string","maxLength":2000,"minLength":1,"title":"Reason"}},"type":"object","required":["reason"],"title":"AntiIcpItem","description":"One disqualifier — a candidate matching it is excluded (mirrors the harness\n``anti_icp``)."},"ApiKeyCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"scopes":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Scopes"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["name","scopes"],"title":"ApiKeyCreate"},"ApiKeySummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"key_prefix":{"type":"string","title":"Key Prefix"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"is_active":{"type":"boolean","title":"Is Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","key_prefix","scopes","is_active","created_at"],"title":"ApiKeySummary"},"ApplyCustomFieldTemplateRequest":{"properties":{"template":{"type":"string","maxLength":100,"minLength":1,"title":"Template"}},"type":"object","required":["template"],"title":"ApplyCustomFieldTemplateRequest","description":"Body for POST /tenants/me/custom-fields/apply-template (M-Prospect-a)."},"ApplyCustomFieldTemplateResponse":{"properties":{"custom_field_schema":{"additionalProperties":true,"type":"object","title":"Custom Field Schema"}},"type":"object","required":["custom_field_schema"],"title":"ApplyCustomFieldTemplateResponse","description":"Response carries the post-merge custom_field_schema."},"ApplyOutcomeRequest":{"properties":{"sentiment":{"type":"string","title":"Sentiment"},"summary":{"type":"string","maxLength":200,"title":"Summary"},"body_full":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Full"},"reply_subject":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reply Subject"},"unsubscribe_signals":{"items":{"type":"string"},"type":"array","title":"Unsubscribe Signals","default":[]}},"type":"object","required":["sentiment","summary"],"title":"ApplyOutcomeRequest","description":"M31b2.5 (UX-019) — payload for `POST /outreach/{id}/outcome`.\n\nThe OutreachDetailPanel's quick-action buttons funnel into this\nshape. `sentiment` drives the apply_reply_outcome orchestrator's\nrecord-side `target_status` + contact-side mutation; `summary` is\nthe audit-trail line the user is implicitly asserting (server-\ncapped at 200 chars to match the AI-generated summary contract).\n`body_full` is optional — supplied when the panel echoes back the\nreply body the user just read; null when the button represents a\npure status decision unconnected to a specific reply line."},"ApplyOutcomeResponse":{"properties":{"record_updated":{"type":"boolean","title":"Record Updated"},"target_status":{"type":"string","title":"Target Status"},"sentiment":{"type":"string","title":"Sentiment"},"contact_promoted":{"type":"boolean","title":"Contact Promoted"},"contact_marked_invalid":{"type":"boolean","title":"Contact Marked Invalid"},"contact_unsubscribed":{"type":"boolean","title":"Contact Unsubscribed"},"reply_record_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Reply Record Id"}},"type":"object","required":["record_updated","target_status","sentiment","contact_promoted","contact_marked_invalid","contact_unsubscribed"],"title":"ApplyOutcomeResponse","description":"M31b2.5 — mirrors `services.reply_ingestion.ReplyOutcomeResult`.\n\n`record_updated=False` is the idempotent no-op path (sent row\nmissing, or a synthetic-id idempotency miss). Contact-side fields\nare False on that path too — apply_reply_outcome's contract is\n\"don't touch the contact when the record-side mutation didn't\napply.\" `reply_record_id` is the new inbound OutreachRecord id\ncreated for this outcome (NULL on no-op)."},"AttachmentList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AttachmentResponse"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["items","total"],"title":"AttachmentList"},"AttachmentResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"entity_type":{"type":"string","title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"},"filename":{"type":"string","title":"Filename"},"mime_type":{"type":"string","title":"Mime Type"},"size_bytes":{"type":"integer","title":"Size Bytes"},"uploaded_by_user_id":{"type":"string","format":"uuid","title":"Uploaded By User Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","entity_type","entity_id","filename","mime_type","size_bytes","uploaded_by_user_id","created_at"],"title":"AttachmentResponse"},"AuditLogEntry":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"user":{"anyOf":[{"$ref":"#/components/schemas/UserSummary"},{"type":"null"}]},"action":{"type":"string","title":"Action"},"resource_type":{"type":"string","title":"Resource Type"},"resource_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Resource Id"},"diff":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Diff"},"ip_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ip Address"},"actor_type":{"type":"string","title":"Actor Type","default":"user"},"actor_metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Actor Metadata"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","action","resource_type","created_at"],"title":"AuditLogEntry"},"AutoDerivedFacts":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Name"},"email":{"anyOf":[{"type":"string","maxLength":320},{"type":"null"}],"title":"Email"},"role":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Role"},"created_at":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Created At"}},"type":"object","title":"AutoDerivedFacts","description":"The facts Limena already knows from the user's account — a snapshot of\nname / email / role / join date, refreshed on every (re)synthesis *and* every edit so\nit never drifts from the account. Read-only in the UI (a user can't edit \"Limena knows\nyour email\"); the service always re-snapshots it server-side and never trusts a\nclient-supplied value. The AI reads these as ground truth."},"AvailableModelsResponse":{"properties":{"models":{"items":{"$ref":"#/components/schemas/OllamaModelEntry"},"type":"array","title":"Models"}},"type":"object","required":["models"],"title":"AvailableModelsResponse","description":"GET /tenants/me/ai-config/available-models response."},"Body_bulk_log_outreach_api_v1_outreach_bulk_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_bulk_log_outreach_api_v1_outreach_bulk_post"},"Body_create_prospect_list_api_v1_lists_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"},"name":{"type":"string","maxLength":255,"title":"Name"},"source":{"type":"string","title":"Source"},"upload_intent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Upload Intent"},"default_campaign_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Default Campaign Name"},"header_row":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Header Row"},"email_column":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Email Column"},"field_mappings":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Field Mappings"}},"type":"object","required":["file","name","source"],"title":"Body_create_prospect_list_api_v1_lists_post"},"Body_preview_prospect_list_api_v1_lists_preview_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"},"header_row":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Header Row"}},"type":"object","required":["file"],"title":"Body_preview_prospect_list_api_v1_lists_preview_post"},"Body_suggest_prospect_list_columns_ai_api_v1_lists_preview_suggest_ai_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"},"header_row":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Header Row"}},"type":"object","required":["file"],"title":"Body_suggest_prospect_list_columns_ai_api_v1_lists_preview_suggest_ai_post"},"Body_upload_attachment_api_v1_attachments_post":{"properties":{"entity_type":{"type":"string","title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"},"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["entity_type","entity_id","file"],"title":"Body_upload_attachment_api_v1_attachments_post"},"Body_upload_call_recording_audio_api_v1_call_recordings__recording_id__audio_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_call_recording_audio_api_v1_call_recordings__recording_id__audio_post"},"Body_upload_import_file_api_v1_import_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_import_file_api_v1_import_post"},"Body_upload_migration_api_v1_migrations_post":{"properties":{"files":{"items":{"type":"string","contentMediaType":"application/octet-stream"},"type":"array","title":"Files"}},"type":"object","required":["files"],"title":"Body_upload_migration_api_v1_migrations_post"},"Body_upload_signature_logo_api_v1_users_me_signature_logo_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_signature_logo_api_v1_users_me_signature_logo_post"},"BulkActionResult":{"properties":{"matched":{"type":"integer","title":"Matched"},"affected":{"type":"integer","title":"Affected"},"skipped":{"type":"integer","title":"Skipped"}},"type":"object","required":["matched","affected","skipped"],"title":"BulkActionResult","description":"`matched` = rows the selection resolved to; `affected` = rows actually\nmutated; `skipped` = matched rows that were a no-op (already that owner/tag/\nstatus). One audited batch per request regardless of counts (D5)."},"BulkApproveRequest":{"properties":{"batch_id":{"type":"string","format":"uuid","title":"Batch Id"}},"type":"object","required":["batch_id"],"title":"BulkApproveRequest","description":"Approve every pending candidate in a batch."},"BulkApproveResponse":{"properties":{"campaign_id":{"type":"string","format":"uuid","title":"Campaign Id"},"flipped_count":{"type":"integer","title":"Flipped Count"},"total_count":{"type":"integer","title":"Total Count"}},"type":"object","required":["campaign_id","flipped_count","total_count"],"title":"BulkApproveResponse","description":"Result of `POST /api/v1/campaigns/{id}/drafts/approve-all`.\n\n`flipped_count` is the number of rows the bulk-approve actually\ntouched (already-approved rows are no-ops). Tested clients display\n\"N drafts approved\" — already-approved rows shouldn't be counted."},"BulkApproveResult":{"properties":{"approved":{"type":"integer","title":"Approved"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["approved","total"],"title":"BulkApproveResult","description":"Outcome of a bulk-approve over a batch's pending rows."},"BulkOutreachJobStatus":{"properties":{"job_id":{"type":"string","format":"uuid","title":"Job Id"},"status":{"type":"string","title":"Status"},"total_rows":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Rows"},"logged_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Logged Count"},"error_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Count"},"failure_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failure Reason"},"unmatched_created_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Unmatched Created Count"},"unmatched_emails":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Unmatched Emails"}},"type":"object","required":["job_id","status"],"title":"BulkOutreachJobStatus"},"BuyingSignal":{"properties":{"type":{"type":"string","maxLength":100,"minLength":1,"title":"Type"},"detected_at":{"type":"string","format":"date-time","title":"Detected At"},"source_url":{"anyOf":[{"type":"string","maxLength":2083,"minLength":1,"format":"uri"},{"type":"null"}],"title":"Source Url"},"summary":{"type":"string","maxLength":2000,"minLength":1,"title":"Summary"}},"additionalProperties":false,"type":"object","required":["type","detected_at","summary"],"title":"BuyingSignal","description":"One detected signal that a target company may be in-market.\n\n`type` is tenant-defined free text (e.g. \"bdr_hire\", \"ai_pressure\",\n\"funding_round\"). The vocabulary is configuration, not schema —\ndifferent tenants will care about different signal kinds."},"BuyingSignalVocabItem":{"properties":{"type":{"type":"string","maxLength":100,"minLength":1,"title":"Type"},"description":{"type":"string","maxLength":2000,"minLength":1,"title":"Description"}},"type":"object","required":["type","description"],"title":"BuyingSignalVocabItem","description":"One canonical buying-signal type + what it means (the switching triggers).\n\n``type`` is the value that lands in ``company.custom_fields.buying_signals[].type``\n— tenant-defined vocabulary, universal field name (mirrors the harness\n``buying_signal_vocab``)."},"CalendarAccountList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/CalendarAccountResponse"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"CalendarAccountList"},"CalendarAccountResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"provider":{"type":"string","title":"Provider"},"email_address":{"type":"string","title":"Email Address"},"calendar_id":{"type":"string","title":"Calendar Id"},"last_synced_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Synced At"},"connected_at":{"type":"string","format":"date-time","title":"Connected At"}},"type":"object","required":["id","user_id","provider","email_address","calendar_id","last_synced_at","connected_at"],"title":"CalendarAccountResponse","description":"A connected read-only calendar account (no secrets — the encrypted\nrefresh token + sync cursor never leave the backend)."},"CalendarAuthorizeResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"}},"type":"object","required":["auth_url"],"title":"CalendarAuthorizeResponse","description":"The provider consent URL the frontend redirects the browser to."},"CalendarCallbackPayload":{"properties":{"code":{"type":"string","title":"Code"},"state":{"type":"string","title":"State"}},"type":"object","required":["code","state"],"title":"CalendarCallbackPayload","description":"Posted by the /auth/<provider>-calendar/callback page after the redirect."},"CalendarCallbackResponse":{"properties":{"account":{"$ref":"#/components/schemas/CalendarAccountResponse"}},"type":"object","required":["account"],"title":"CalendarCallbackResponse"},"CallActionItemTaskRequest":{"properties":{"action_items":{"items":{"type":"string"},"type":"array","maxItems":50,"minItems":1,"title":"Action Items"}},"type":"object","required":["action_items"],"title":"CallActionItemTaskRequest","description":"One-click action-items→Tasks (M-Calls-b §7 lock). Each item must be one of\nthe recording's extracted ``action_items`` (the service validates membership)."},"CallActionItemTasksResponse":{"properties":{"tasks":{"items":{"$ref":"#/components/schemas/TaskListItem"},"type":"array","title":"Tasks"},"created_count":{"type":"integer","title":"Created Count"}},"type":"object","required":["tasks","created_count"],"title":"CallActionItemTasksResponse","description":"The Tasks created from a call's action items — or reused, when an open Task\nwith the same subject already exists on the entity (the dup guard)."},"CallParticipantRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"contact_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Contact Id"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role"}},"type":"object","required":["id"],"title":"CallParticipantRead"},"CallRecordingBotScheduleRequest":{"properties":{"entity_type":{"type":"string","enum":["contact","company","deal","internal"],"title":"Entity Type"},"entity_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Entity Id"},"title":{"type":"string","maxLength":255,"minLength":1,"title":"Title"},"meeting_url":{"type":"string","maxLength":2048,"minLength":1,"title":"Meeting Url"},"occurred_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Occurred At"},"consent_confirmed":{"type":"boolean","title":"Consent Confirmed"},"consent_basis":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Consent Basis"},"meeting_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Meeting Id"}},"type":"object","required":["entity_type","title","meeting_url","consent_confirmed"],"title":"CallRecordingBotScheduleRequest","description":"Send a meeting bot to a call (M-Calls-c, Option B).\n\nThe per-meeting-opt-in trigger: a meeting-join URL (pasted, or read off a\n``Meeting`` record by the client) the Recall.ai bot dials into — no calendar\nOAuth. ``meeting_id`` optionally LINKS the resulting call to an existing\n``Meeting``. Same consent posture as the other capture paths: recording is never\nfrictionless (design §5), so the UI must attest consent explicitly."},"CallRecordingCreate":{"properties":{"entity_type":{"type":"string","enum":["contact","company","deal","internal"],"title":"Entity Type"},"entity_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Entity Id"},"title":{"type":"string","maxLength":255,"minLength":1,"title":"Title"},"occurred_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Occurred At"},"content_type":{"type":"string","maxLength":100,"minLength":1,"title":"Content Type"},"consent_confirmed":{"type":"boolean","title":"Consent Confirmed"},"consent_basis":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Consent Basis"}},"type":"object","required":["entity_type","title","content_type","consent_confirmed"],"title":"CallRecordingCreate","description":"Initiate a manual-upload call recording (the audio is uploaded next)."},"CallRecordingDetail":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"entity_type":{"type":"string","title":"Entity Type"},"entity_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Entity Id"},"source":{"type":"string","title":"Source"},"title":{"type":"string","title":"Title"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"duration_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Seconds"},"transcript_status":{"type":"string","title":"Transcript Status"},"transcript_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transcript Language"},"summary_status":{"type":"string","title":"Summary Status"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary"},"sentiment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sentiment"},"action_items":{"items":{},"type":"array","title":"Action Items"},"suggested_next_step":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suggested Next Step"},"suggested_actions":{"items":{"$ref":"#/components/schemas/SuggestedActionRead"},"type":"array","title":"Suggested Actions"},"consent_confirmed":{"type":"boolean","title":"Consent Confirmed"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"transcript_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transcript Text"},"participants":{"items":{"$ref":"#/components/schemas/CallParticipantRead"},"type":"array","title":"Participants"},"segments":{"items":{"$ref":"#/components/schemas/CallTranscriptSegmentRead"},"type":"array","title":"Segments"}},"type":"object","required":["id","entity_type","source","title","occurred_at","transcript_status","summary_status","consent_confirmed","created_at"],"title":"CallRecordingDetail","description":"Detail view — adds the transcript text + diarized turns + participants."},"CallRecordingInitiateResponse":{"properties":{"recording":{"$ref":"#/components/schemas/CallRecordingRead"},"upload":{"$ref":"#/components/schemas/UploadInstructionResponse"}},"type":"object","required":["recording","upload"],"title":"CallRecordingInitiateResponse","description":"The initiate response: the created (pending) record + upload instruction."},"CallRecordingList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/CallRecordingRead"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["items","total"],"title":"CallRecordingList"},"CallRecordingRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"entity_type":{"type":"string","title":"Entity Type"},"entity_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Entity Id"},"source":{"type":"string","title":"Source"},"title":{"type":"string","title":"Title"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"duration_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Seconds"},"transcript_status":{"type":"string","title":"Transcript Status"},"transcript_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transcript Language"},"summary_status":{"type":"string","title":"Summary Status"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary"},"sentiment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sentiment"},"action_items":{"items":{},"type":"array","title":"Action Items"},"suggested_next_step":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suggested Next Step"},"suggested_actions":{"items":{"$ref":"#/components/schemas/SuggestedActionRead"},"type":"array","title":"Suggested Actions"},"consent_confirmed":{"type":"boolean","title":"Consent Confirmed"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","entity_type","source","title","occurred_at","transcript_status","summary_status","consent_confirmed","created_at"],"title":"CallRecordingRead","description":"List/summary view — no audio key (internal, transient)."},"CallRecordingTranscriptCreate":{"properties":{"entity_type":{"type":"string","enum":["contact","company","deal","internal"],"title":"Entity Type"},"entity_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Entity Id"},"title":{"type":"string","maxLength":255,"minLength":1,"title":"Title"},"occurred_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Occurred At"},"transcript_text":{"type":"string","maxLength":200000,"minLength":1,"title":"Transcript Text"},"consent_confirmed":{"type":"boolean","title":"Consent Confirmed"},"consent_basis":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Consent Basis"}},"type":"object","required":["entity_type","title","transcript_text","consent_confirmed"],"title":"CallRecordingTranscriptCreate","description":"Create a call recording from a user-pasted transcript (no audio upload).\n\nA supplied-transcript shortcut into the manual-upload source (M-Calls-b §7\nlock): STT is skipped, summarization runs on the pasted text. Same consent\nposture as an audio upload — recording is never frictionless (design §5)."},"CallSuggestedActionsReviewRequest":{"properties":{"apply_ids":{"items":{"type":"string"},"type":"array","maxItems":50,"title":"Apply Ids"},"dismiss_ids":{"items":{"type":"string"},"type":"array","maxItems":50,"title":"Dismiss Ids"}},"type":"object","title":"CallSuggestedActionsReviewRequest","description":"One review decision over a call's suggested actions (M-Calls-e §D3): the\nsingle \"Apply N actions\" POST. ``apply_ids`` compose the real mutations\nthrough the existing task/note/deal services; ``dismiss_ids`` just flip\nstate. Disjoint, and at least one id total."},"CallSuggestedActionsReviewResponse":{"properties":{"results":{"items":{"$ref":"#/components/schemas/SuggestedActionResult"},"type":"array","title":"Results"},"recording":{"$ref":"#/components/schemas/CallRecordingRead"}},"type":"object","required":["results","recording"],"title":"CallSuggestedActionsReviewResponse","description":"The per-item outcomes plus the recording with its suggestion states\nupdated (the client swaps it in rather than refetching)."},"CallTranscriptSegmentRead":{"properties":{"seq":{"type":"integer","title":"Seq"},"speaker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Speaker"},"start_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Start Ms"},"end_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"End Ms"},"text":{"type":"string","title":"Text"}},"type":"object","required":["seq","text"],"title":"CallTranscriptSegmentRead"},"CampaignCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"append_signature":{"type":"boolean","title":"Append Signature","default":true}},"type":"object","required":["name"],"title":"CampaignCreate"},"CampaignDetail":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"compose_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Compose Mode"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"system_prompt_override":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"System Prompt Override"},"send_status":{"type":"string","title":"Send Status","default":"idle"},"tracking_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Tracking Enabled"},"append_signature":{"type":"boolean","title":"Append Signature","default":true},"scheduled_send_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled Send At"},"schedule_in_recipient_tz":{"type":"boolean","title":"Schedule In Recipient Tz","default":false},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"member_count":{"type":"integer","title":"Member Count","default":0},"outreach_count":{"type":"integer","title":"Outreach Count","default":0},"last_sent_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Sent At"},"pending_approval_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Pending Approval Id"},"failure_recipient":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failure Recipient"},"failure_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failure Reason"},"failure_retry_safe":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Failure Retry Safe"}},"type":"object","required":["id","name","created_by","created_at","updated_at"],"title":"CampaignDetail","description":"Includes derived counts. The route handler populates member_count and\noutreach_count via aggregate queries — they aren't on the ORM.\n\n`compose_mode`, `description`, `send_status` land in M26b; M26d wires\nthe compose UI and send pipeline that read and mutate them."},"CampaignMemberAdd":{"properties":{"contact_ids":{"items":{"type":"string","format":"uuid"},"type":"array","minItems":1,"title":"Contact Ids"},"added_via_list_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Added Via List Id"}},"type":"object","required":["contact_ids"],"title":"CampaignMemberAdd"},"CampaignMemberContact":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"},"job_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Title"},"company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"}},"type":"object","required":["id","first_name","last_name","email"],"title":"CampaignMemberContact","description":"Contact projection for the campaign-members listing."},"CampaignSendPlanResponse":{"properties":{"total_recipients":{"type":"integer","title":"Total Recipients"},"today_capacity":{"type":"integer","title":"Today Capacity"},"overflow_per_day":{"items":{"type":"integer"},"type":"array","title":"Overflow Per Day","default":[]},"estimated_completion_at":{"type":"string","format":"date-time","title":"Estimated Completion At"},"send_status":{"type":"string","title":"Send Status"}},"type":"object","required":["total_recipients","today_capacity","estimated_completion_at","send_status"],"title":"CampaignSendPlanResponse","description":"Output of preview + start + resume. `sendable_contact_ids` is\nomitted from the response on purpose — it can be hundreds of UUIDs,\nand the UI doesn't need them; the audit row in `actor_metadata`\ncarries the count, which is the auditable quantity."},"CampaignSendPreview":{"properties":{"account_id":{"type":"string","format":"uuid","title":"Account Id"},"scheduled_send_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled Send At"}},"type":"object","required":["account_id"],"title":"CampaignSendPreview","description":"Body for `POST /campaigns/{id}/send/preview` — pre-flight maths\nwithout writing or enqueuing. The compose UI's Step 4 modal renders\n`total_recipients`, `today_capacity`, `overflow_per_day`, and\n`estimated_completion_at` so the user sees the auto-spread schedule\nbefore clicking Send."},"CampaignSendStart":{"properties":{"account_id":{"type":"string","format":"uuid","title":"Account Id"},"template_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Template Id"},"from_send_as":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From Send As","description":"Optional alias email to send AS. Must be one of the account's verified send-as aliases — anything else falls back to the account's primary email_address. Visible in compose UI only when 2+ aliases exist."},"scheduled_send_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled Send At"}},"type":"object","required":["account_id"],"title":"CampaignSendStart","description":"Body for `POST /campaigns/{id}/send/start` (and `/resume`, which\nreuses the same fields so a paused campaign can pick a different\naccount or template if the original is gone).\n\n`template_id` is optional from M29c onward — AI-mode campaigns\n(`compose_mode='ai_generated'`) ignore it and consume the body\nlocked into the per-recipient OutreachRecord draft row. Template-\nmode sends still require it; the service-layer validation surfaces\na 422 if it's missing in template mode.\n\n`scheduled_send_at` is optional — when provided and in the future,\nthe send is scheduled rather than started immediately (M-Outbound)."},"CampaignSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"send_status":{"type":"string","title":"Send Status","default":"idle"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"member_count":{"type":"integer","title":"Member Count","default":0},"sent_count":{"type":"integer","title":"Sent Count","default":0},"last_sent_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Sent At"},"has_pending_approval":{"type":"boolean","title":"Has Pending Approval","default":false}},"type":"object","required":["id","name","created_at","updated_at"],"title":"CampaignSummary"},"CampaignTestSendRequest":{"properties":{"account_id":{"type":"string","format":"uuid","title":"Account Id"},"template_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Template Id"},"from_send_as":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From Send As","description":"Optional verified send-as alias. Same shape as the equivalent field on `CampaignSendStart`."}},"type":"object","required":["account_id"],"title":"CampaignTestSendRequest","description":"Body for `POST /campaigns/{id}/send/test`.\n\nDelivers ONE fully-rendered copy of the campaign message (merge vars +\nsignature/logo + tracking + unsubscribe footer) to the current user's own\ninbox before a blast. `account_id` picks the mailbox to send from — it must\nbe one the acting user owns (SD-M-Mailbox-a). `template_id` is required for\ntemplate-mode campaigns and ignored for AI-mode (same contract as\n`CampaignSendStart`). The recipient is always the authenticated user's own\nemail, resolved server-side — never a caller-supplied address."},"CampaignTestSendResponse":{"properties":{"to_address":{"type":"string","title":"To Address"},"subject":{"type":"string","title":"Subject"},"status":{"type":"string","title":"Status","default":"sent"}},"type":"object","required":["to_address","subject"],"title":"CampaignTestSendResponse","description":"Response from a successful test send — the address it was delivered to and\nthe rendered subject, for the compose UI's confirmation toast."},"CampaignUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"tracking_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Tracking Enabled"},"compose_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Compose Mode"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"system_prompt_override":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"System Prompt Override"},"append_signature":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Append Signature"}},"type":"object","title":"CampaignUpdate","description":"Partial update. Pydantic v2 `model_fields_set` at the route boundary\ndistinguishes \"client omitted the key\" from \"client sent `null`\" — the\npattern locked in by the M26c PATCH refactor. `tracking_enabled=null`\nis a real distinct write meaning \"inherit from tenant\"; omitting the\nkey leaves the column alone. Same shape applies to `compose_mode`,\n`description`, and `system_prompt_override` (all nullable, all can\ntake an explicit-null clear)."},"ChartDisplay":{"properties":{"x_axis_label":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"X Axis Label"},"y_axis_label":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Y Axis Label"},"sort_by":{"anyOf":[{"$ref":"#/components/schemas/SortDirection"},{"type":"null"}]},"top_n":{"anyOf":[{"type":"integer","maximum":1000.0,"minimum":1.0},{"type":"null"}],"title":"Top N"},"number_format":{"anyOf":[{"$ref":"#/components/schemas/NumberFormat"},{"type":"null"}]},"color_palette":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Color Palette"}},"additionalProperties":false,"type":"object","title":"ChartDisplay","description":"Renderer-side display tuning.\n\nDefaults are all `None` — the TileRenderer falls back to the\nchart-type registry's per-type defaults (locked in M32b). A spec\nthat doesn't customise anything still renders correctly.\n\n`top_n` clips the result to the highest-`n` (or lowest, depending on\nsort) categorical-axis buckets and pivots the remainder into a\nsingle \"Other\" bucket. Numeric/time axes ignore `top_n`."},"ChartResult":{"properties":{"rows":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Rows"},"cache_hit":{"type":"boolean","title":"Cache Hit"},"data_freshness_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Data Freshness Seconds"},"snapshot":{"type":"boolean","title":"Snapshot"}},"additionalProperties":false,"type":"object","required":["rows","cache_hit","data_freshness_seconds","snapshot"],"title":"ChartResult","description":"Result envelope returned by `run_chart`.\n\n`rows` is the list-of-dicts shape the compiler produces (keys are\ncolumn labels — `dimension`, `breakdown`, `value` — set by\n`compiler._compile_dimension` / `_compile_measure`).\n\n`cache_hit` is True when the result was served from Redis without\nre-executing the SQL. `data_freshness_seconds` is the age of the\ncached payload at read time (0 on a miss; >= 0 on a hit). `snapshot`\nis True when served from the `analytics_snapshots` snapshot table\n(Q17 lock, wired in chunk 9)."},"ChartSpec":{"properties":{"entity":{"$ref":"#/components/schemas/Entity"},"measure":{"$ref":"#/components/schemas/Measure"},"dimension":{"anyOf":[{"$ref":"#/components/schemas/Dimension"},{"type":"null"}]},"breakdown":{"anyOf":[{"$ref":"#/components/schemas/Dimension"},{"type":"null"}]},"filter":{"anyOf":[{"$ref":"#/components/schemas/SegmentQuery-Input"},{"type":"null"}]},"time_range":{"anyOf":[{"$ref":"#/components/schemas/TimeRange"},{"type":"null"}]},"chart_type":{"$ref":"#/components/schemas/ChartType"},"comparison_mode":{"$ref":"#/components/schemas/ComparisonMode","default":"none"},"display":{"$ref":"#/components/schemas/ChartDisplay"}},"additionalProperties":false,"type":"object","required":["entity","measure","chart_type"],"title":"ChartSpec","description":"Top-level Chart AST.\n\nRoundtrips through JSON cleanly (Pydantic v2 `model_dump_json` /\n`model_validate_json`). The compiler treats this as immutable;\ncallers wanting variations create new instances.\n\nCross-field semantic rules enforced at the validator layer:\n  - measure.field, dimension.field, breakdown.field, and any field\n    referenced inside filter must exist in\n    `FIELD_REGISTRY[entity]`. Cross-entity references via FK joins\n    are allowed (see field_registry.FieldEntry.joinable_to).\n  - measure.aggregation must be in the field's `aggregatable_as` tuple.\n  - dimension.time_grain is required iff the field's `kind == TIME`.\n  - chart_type imposes shape constraints (e.g. funnel requires\n    `len(filter.conditions) >= 2`; cohort requires a time dimension).\n  - comparison_mode is incompatible with chart_type=COHORT (cohorts\n    already encode a time dimension internally)."},"ChartType":{"type":"string","enum":["single_value","line","bar","stacked_bar","pie","funnel","cohort","heatmap","table"],"title":"ChartType","description":"Renderable chart-type variants.\n\nLocked set — adding a new chart-type requires both a backend update\nhere AND a renderer in `frontend/src/components/analytics/dashboards/\nTileRenderer.tsx`. Q11 lock (2026-05-22) removed `geographic_map` —\nrecharts has no map and the dep cost wasn't worth it for the actual\nuser need. If a real customer asks, treat it as a follow-on tile-pack\nmilestone with its own justification."},"ChatConfirmRequest":{"properties":{"action_id":{"type":"string","format":"uuid","title":"Action Id"},"conversation_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Conversation Id"}},"type":"object","required":["action_id"],"title":"ChatConfirmRequest","description":"The user confirming a write the assistant proposed in chat (M-Assistant-b;\nreshaped by M-Actions-a, GAP-176).\n\n`action_id` names the durable `agent_actions` row the `proposal` SSE frame\ncarried. The capability + args come from that stored row — the client can no\nlonger supply either, which closes both the confirm-replay hole (the row is\nconsumed by an atomic status claim; a second POST 409s) and args substitution\n(the executed payload is the server-persisted validated echo). The adapter\nstill re-validates the stored args + re-runs the preflight at execute time (D4)."},"ChatContinueRequest":{"properties":{"conversation_id":{"type":"string","format":"uuid","title":"Conversation Id"}},"type":"object","required":["conversation_id"],"title":"ChatContinueRequest","description":"Continue the agent's plan after a confirmed write (M-AgentLoop-c).\n\nThe frontend posts this immediately after `/chat/confirm` resolves and the\nproposal card flips to APPLIED. Unlike `/chat`, it carries NO user message —\nthe continuation resumes the persisted transcript, which now ends in the\njust-applied `[applied …]` receipt (M-Observe-a). `conversation_id` is\nREQUIRED: without a transcript there is nothing to continue."},"ChatRejectRequest":{"properties":{"action_id":{"type":"string","format":"uuid","title":"Action Id"}},"type":"object","required":["action_id"],"title":"ChatRejectRequest","description":"The user declining a proposed action (M-Actions-a). The Cancel that used to\nbe a client-side state flip now persists — `proposed → rejected` — so a\ndeclined proposal is real data (the §4.8 outcome-capture seam) and can never\nbe confirmed later."},"ChatRequest":{"properties":{"message":{"type":"string","maxLength":4000,"minLength":1,"title":"Message"},"conversation_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Conversation Id"},"view_context":{"anyOf":[{"$ref":"#/components/schemas/ViewContext"},{"type":"null"}]}},"type":"object","required":["message"],"title":"ChatRequest","description":"One chat turn (M-Stabilize-c, s1): the new user message only.\n\nPrior turns are rebuilt server-side from the persisted transcript — the\nclient never resends history. (The old shape validated a 2000-char cap\nagainst every history message, so one long stored assistant reply\npermanently 422-wedged its conversation.) The cap below applies to the\nsingle incoming turn; stored turns are uncapped on read-back."},"ClosedStageRollup":{"properties":{"stage":{"type":"string","title":"Stage"},"label":{"type":"string","title":"Label"},"is_closed_won":{"type":"boolean","title":"Is Closed Won"},"deal_count":{"type":"integer","title":"Deal Count"},"total_value":{"type":"integer","title":"Total Value"}},"type":"object","required":["stage","label","is_closed_won","deal_count","total_value"],"title":"ClosedStageRollup","description":"Per-closed-stage aggregate (M-DealPage-a / UX-045) — counts + values are\nserver-side and exact, unlike the 50-row page the closed tables fetch."},"ColumnSuggestion":{"properties":{"source_column":{"type":"string","title":"Source Column"},"suggested_target":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suggested Target"},"confidence":{"type":"string","title":"Confidence"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"rationale":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rationale"}},"type":"object","required":["source_column","confidence"],"title":"ColumnSuggestion"},"CompanyBulkDeleteRequest":{"properties":{"selection":{"$ref":"#/components/schemas/CompanyBulkSelection"}},"additionalProperties":false,"type":"object","required":["selection"],"title":"CompanyBulkDeleteRequest"},"CompanyBulkFilters":{"properties":{"search":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"}},"additionalProperties":false,"type":"object","title":"CompanyBulkFilters","description":"Mirror of the `GET /companies` list query params."},"CompanyBulkReassignRequest":{"properties":{"selection":{"$ref":"#/components/schemas/CompanyBulkSelection"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"}},"additionalProperties":false,"type":"object","required":["selection"],"title":"CompanyBulkReassignRequest"},"CompanyBulkSelection":{"properties":{"ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Ids"},"filters":{"anyOf":[{"$ref":"#/components/schemas/CompanyBulkFilters"},{"type":"null"}]},"exclude_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Exclude Ids"}},"additionalProperties":false,"type":"object","title":"CompanyBulkSelection"},"CompanyBulkTagsRequest":{"properties":{"selection":{"$ref":"#/components/schemas/CompanyBulkSelection"},"mode":{"type":"string","enum":["add","remove"],"title":"Mode"},"tags":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Tags"}},"additionalProperties":false,"type":"object","required":["selection","mode","tags"],"title":"CompanyBulkTagsRequest"},"CompanyCreate":{"properties":{"name":{"type":"string","maxLength":255,"title":"Name","default":""},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"domain":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Domain"},"industry":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Industry"},"employee_count":{"anyOf":[{"type":"integer","maximum":2147483647.0,"minimum":0.0},{"type":"null"}],"title":"Employee Count"},"annual_revenue":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":0.0},{"type":"null"}],"title":"Annual Revenue"},"country":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Country"},"city":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"City"},"source":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Source"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]},"custom_fields":{"additionalProperties":true,"type":"object","title":"Custom Fields","default":{}}},"type":"object","title":"CompanyCreate"},"CompanyDetail":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"name":{"type":"string","title":"Name"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"},"industry":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Industry"},"employee_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Employee Count"},"annual_revenue":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Annual Revenue"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"owner":{"anyOf":[{"$ref":"#/components/schemas/UserSummary"},{"type":"null"}]},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]},"custom_fields":{"additionalProperties":true,"type":"object","title":"Custom Fields","default":{}},"contact_count":{"type":"integer","title":"Contact Count","default":0},"contacts":{"items":{"$ref":"#/components/schemas/ContactSummaryForCompany"},"type":"array","title":"Contacts","default":[]},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings","default":[]},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","tenant_id","name","created_at","updated_at"],"title":"CompanyDetail"},"CompanyExportRequest":{"properties":{"selection":{"$ref":"#/components/schemas/CompanyBulkSelection"}},"additionalProperties":false,"type":"object","required":["selection"],"title":"CompanyExportRequest","description":"M-Tablestakes-a s2: CSV export of a company selection (filters or ids)."},"CompanyListItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"},"industry":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Industry"},"employee_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Employee Count"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"owner":{"anyOf":[{"$ref":"#/components/schemas/UserSummary"},{"type":"null"}]},"contact_count":{"type":"integer","title":"Contact Count","default":0},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]},"created_at":{"type":"string","format":"date-time","title":"Created At"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"}},"type":"object","required":["id","name","created_at"],"title":"CompanyListItem"},"CompanyMergeRequest":{"properties":{"source_company_id":{"type":"string","format":"uuid","title":"Source Company Id"}},"type":"object","required":["source_company_id"],"title":"CompanyMergeRequest"},"CompanyPatch":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Name"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"domain":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Domain"},"industry":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Industry"},"employee_count":{"anyOf":[{"type":"integer","maximum":2147483647.0,"minimum":0.0},{"type":"null"}],"title":"Employee Count"},"annual_revenue":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":0.0},{"type":"null"}],"title":"Annual Revenue"},"country":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Country"},"city":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"City"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"custom_fields":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Custom Fields"}},"type":"object","title":"CompanyPatch"},"CompanyProfile":{"properties":{"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"products":{"items":{"type":"string","maxLength":200,"minLength":1},"type":"array","maxItems":40,"title":"Products"},"positioning":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Positioning"},"voice":{"anyOf":[{"type":"string","maxLength":1500},{"type":"null"}],"title":"Voice"},"industry":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Industry"}},"type":"object","title":"CompanyProfile","description":"The stored company-profile shape (the ``company_profiles.profile`` JSONB blob).\n\n``extra=\"ignore\"`` (not ``forbid``): this is a shape *read back* from JSONB, so a\nlenient read is the defensive default (mirrors :class:`app.schemas.icp.IcpProfile`).\nAll fields optional so a partial derivation — or a from-scratch hand-authored doc —\nis valid."},"CompanyProfileDerivationJobCreated":{"properties":{"job_id":{"type":"string","title":"Job Id","description":"ARQ job id to poll for the derivation result."}},"type":"object","required":["job_id"],"title":"CompanyProfileDerivationJobCreated","description":"The handle returned by ``POST …/derive`` — poll ``GET …/derive/{job_id}``."},"CompanyProfileDerivationJobOut":{"properties":{"status":{"type":"string","enum":["pending","done","error","expired"],"title":"Status","description":"pending (keep polling) | done | error | expired (re-run)."},"profile":{"anyOf":[{"$ref":"#/components/schemas/CompanyProfile"},{"type":"null"}],"description":"The distilled company profile, present only when status == 'done'."},"progress":{"anyOf":[{"$ref":"#/components/schemas/JobProgress"},{"type":"null"}],"description":"Live phase/count snapshot while status == 'pending'; null once terminal."}},"type":"object","required":["status"],"title":"CompanyProfileDerivationJobOut","description":"Poll envelope: the derive job's state + the distilled profile once ``done``.\n\n``done`` means the ``company_profiles`` row has already been upserted with\n``source='derived'`` — ``profile`` echoes it so the UI can render immediately (it\nrefetches ``GET /company-profile`` for the full provenance + source_links)."},"CompanyProfileDeriveRequest":{"properties":{"website":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Website"},"linkedin_url":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Linkedin Url"},"other_links":{"items":{"type":"string","maxLength":2000,"minLength":1},"type":"array","maxItems":10,"title":"Other Links"},"pasted_text":{"anyOf":[{"type":"string","maxLength":20000},{"type":"null"}],"title":"Pasted Text"}},"additionalProperties":false,"type":"object","title":"CompanyProfileDeriveRequest","description":"``POST /company-profile/derive`` input — the owner-supplied sources to absorb.\n\nWebsite-led (the only reliably fetchable source, locked decision 4); the LinkedIn\nURL is stored inert + best-effort-fetched. ``pasted_text`` is the \"paste anything we\ncan't read automatically\" fallback. At least one source is required (the service\n409s otherwise)."},"CompanyProfileResponse":{"properties":{"profile":{"anyOf":[{"$ref":"#/components/schemas/CompanyProfile"},{"type":"null"}]},"source_links":{"items":{"$ref":"#/components/schemas/SourceLink"},"type":"array","title":"Source Links"},"source":{"anyOf":[{"type":"string","enum":["derived","edited"]},{"type":"null"}],"title":"Source"},"ai_updates_enabled":{"type":"boolean","title":"Ai Updates Enabled","default":true},"last_edited_by":{"anyOf":[{"type":"string","enum":["human","ai"]},{"type":"null"}],"title":"Last Edited By"},"derived_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Derived At"},"edited_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Edited At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"}},"type":"object","title":"CompanyProfileResponse","description":"``GET`` / ``PATCH /company-profile`` — the profile + its provenance + the toggle.\n\nA never-derived, never-edited tenant gets an empty shell at 200 (empty profile, no\nprovenance) so the Settings UI shows an empty state + a \"Derive from the web\" button\nrather than a 404."},"CompanyProfileUpdate":{"properties":{"profile":{"$ref":"#/components/schemas/CompanyProfile"},"source_links":{"anyOf":[{"items":{"$ref":"#/components/schemas/SourceLink"},"type":"array"},{"type":"null"}],"title":"Source Links"},"ai_updates_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Ai Updates Enabled"}},"additionalProperties":false,"type":"object","required":["profile"],"title":"CompanyProfileUpdate","description":"``PATCH /company-profile`` input — a whole-profile replace (+ the toggle).\n\nThe Settings UI holds the full profile and saves it back (mirrors the ICP edit).\n``ai_updates_enabled`` rides along so the toggle can flip in the same save;\n``source_links`` is optional (editing the cited sources). ``extra=\"forbid\"`` —\na strict input boundary."},"CompanySummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"}},"type":"object","required":["id","name"],"title":"CompanySummary"},"CompanySummaryForDeal":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"}},"type":"object","required":["id","name"],"title":"CompanySummaryForDeal"},"CompanyUpdate":{"properties":{"name":{"type":"string","maxLength":255,"title":"Name","default":""},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"domain":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Domain"},"industry":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Industry"},"employee_count":{"anyOf":[{"type":"integer","maximum":2147483647.0,"minimum":0.0},{"type":"null"}],"title":"Employee Count"},"annual_revenue":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":0.0},{"type":"null"}],"title":"Annual Revenue"},"country":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Country"},"city":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"City"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]},"custom_fields":{"additionalProperties":true,"type":"object","title":"Custom Fields","default":{}}},"type":"object","title":"CompanyUpdate"},"ComparisonMode":{"type":"string","enum":["none","vs_prior_period","vs_baseline"],"title":"ComparisonMode","description":"Whether to draw a comparison series alongside the primary one.\n\n`VS_PRIOR_PERIOD`: shifts the time-range back by the same window and\ncompares against it (e.g. \"this quarter vs last quarter\"). On a\nsingle_value tile this emits a `value_prior` aggregate alongside\n`value` (M-Trend-a / GAP-059); on a time-grain line/bar chart it is\nreserved for a future comparison-series overlay.\n`VS_BASELINE`: compares against a fixed baseline range stored in\n`ChartSpec.baseline_range` (lands in a later chunk if needed —\nrejected by `validate.py` until that field exists).\n`NONE`: single series — the default."},"CompileRuleRequest":{"properties":{"prompt":{"type":"string","maxLength":2000,"minLength":1,"title":"Prompt"}},"additionalProperties":false,"type":"object","required":["prompt"],"title":"CompileRuleRequest","description":"Natural-language automation request to compile into a rule draft."},"CompiledRuleDraftRead":{"properties":{"draft":{"anyOf":[{"$ref":"#/components/schemas/WorkflowRuleCreate"},{"type":"null"}]},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings"}},"type":"object","required":["draft"],"title":"CompiledRuleDraftRead","description":"Compile result: an *unsaved* draft (or null when it couldn't be composed)\nplus any warnings. The human reviews/edits, then saves via POST /workflows —\nnothing here is persisted (DA3)."},"ConditionPredicate":{"properties":{"field":{"type":"string","maxLength":128,"minLength":1,"title":"Field"},"op":{"type":"string","enum":["eq","neq","gt","lt","gte","lte","contains","in","is_set","is_unset","changed"],"title":"Op"},"value":{"anyOf":[{},{"type":"null"}],"title":"Value"}},"additionalProperties":false,"type":"object","required":["field","op"],"title":"ConditionPredicate","description":"One `{field, op, value}` predicate. `value` is unused for set/unset/changed."},"ConnectionTestResult":{"properties":{"success":{"type":"boolean","title":"Success"},"latency_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Latency Ms"},"sample_response":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sample Response"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["success"],"title":"ConnectionTestResult","description":"POST /tenants/me/ai-config/test response."},"ContactBulkAddToCampaignRequest":{"properties":{"selection":{"$ref":"#/components/schemas/ContactBulkSelection"},"campaign_id":{"type":"string","format":"uuid","title":"Campaign Id"}},"additionalProperties":false,"type":"object","required":["selection","campaign_id"],"title":"ContactBulkAddToCampaignRequest","description":"M-Bulk-b: add a contact selection to a campaign. Contacts-only (campaign\nmembership is contact-scoped); deals/companies have no place here (D7)."},"ContactBulkDeleteRequest":{"properties":{"selection":{"$ref":"#/components/schemas/ContactBulkSelection"}},"additionalProperties":false,"type":"object","required":["selection"],"title":"ContactBulkDeleteRequest"},"ContactBulkFilters":{"properties":{"search":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"tag":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tag"},"excluded_from_outreach":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Excluded From Outreach"},"untouched":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Untouched"}},"additionalProperties":false,"type":"object","title":"ContactBulkFilters","description":"Mirror of the `GET /contacts` list query params (the canonical filter set)."},"ContactBulkReassignRequest":{"properties":{"selection":{"$ref":"#/components/schemas/ContactBulkSelection"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"}},"additionalProperties":false,"type":"object","required":["selection"],"title":"ContactBulkReassignRequest"},"ContactBulkSelection":{"properties":{"ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Ids"},"filters":{"anyOf":[{"$ref":"#/components/schemas/ContactBulkFilters"},{"type":"null"}]},"exclude_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Exclude Ids"}},"additionalProperties":false,"type":"object","title":"ContactBulkSelection"},"ContactBulkStatusRequest":{"properties":{"selection":{"$ref":"#/components/schemas/ContactBulkSelection"},"status":{"type":"string","minLength":1,"title":"Status"}},"additionalProperties":false,"type":"object","required":["selection","status"],"title":"ContactBulkStatusRequest"},"ContactBulkTagsRequest":{"properties":{"selection":{"$ref":"#/components/schemas/ContactBulkSelection"},"mode":{"type":"string","enum":["add","remove"],"title":"Mode"},"tags":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Tags"}},"additionalProperties":false,"type":"object","required":["selection","mode","tags"],"title":"ContactBulkTagsRequest"},"ContactCreate":{"properties":{"first_name":{"type":"string","maxLength":255,"minLength":1,"title":"First Name"},"last_name":{"type":"string","maxLength":255,"minLength":1,"title":"Last Name"},"email":{"type":"string","format":"email","title":"Email"},"company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"phone":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Phone"},"job_title":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Job Title"},"linkedin_url":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Linkedin Url"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"source":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Source"},"timezone":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Timezone"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]},"custom_fields":{"additionalProperties":true,"type":"object","title":"Custom Fields","default":{}}},"type":"object","required":["first_name","last_name","email"],"title":"ContactCreate"},"ContactDetail":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"job_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Title"},"linkedin_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linkedin Url"},"company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"},"company":{"anyOf":[{"$ref":"#/components/schemas/CompanySummary"},{"type":"null"}]},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"owner":{"anyOf":[{"$ref":"#/components/schemas/UserSummary"},{"type":"null"}]},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"status":{"type":"string","title":"Status"},"is_subscribed":{"type":"boolean","title":"Is Subscribed"},"email_invalid":{"type":"boolean","title":"Email Invalid","default":false},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timezone"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]},"custom_fields":{"additionalProperties":true,"type":"object","title":"Custom Fields","default":{}},"recent_outreach":{"items":{"$ref":"#/components/schemas/OutreachRecordSummary"},"type":"array","title":"Recent Outreach","default":[]},"open_deals":{"items":{"$ref":"#/components/schemas/DealSummary"},"type":"array","title":"Open Deals","default":[]},"notes_count":{"type":"integer","title":"Notes Count","default":0},"tasks_count":{"type":"integer","title":"Tasks Count","default":0},"meetings_count":{"type":"integer","title":"Meetings Count","default":0},"calls_count":{"type":"integer","title":"Calls Count","default":0},"files_count":{"type":"integer","title":"Files Count","default":0},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","tenant_id","first_name","last_name","email","status","is_subscribed","created_at","updated_at"],"title":"ContactDetail"},"ContactExportRequest":{"properties":{"selection":{"$ref":"#/components/schemas/ContactBulkSelection"}},"additionalProperties":false,"type":"object","required":["selection"],"title":"ContactExportRequest","description":"M-Tablestakes-a s2: CSV export of a contact selection (filters or explicit\nids). POST body — the selection carries an arbitrary id list the 1000-row cap\nbounds, which a GET query string can't safely hold."},"ContactListItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"},"job_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Title"},"status":{"type":"string","title":"Status"},"is_subscribed":{"type":"boolean","title":"Is Subscribed"},"company":{"anyOf":[{"$ref":"#/components/schemas/CompanySummary"},{"type":"null"}]},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"owner":{"anyOf":[{"$ref":"#/components/schemas/UserSummary"},{"type":"null"}]},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]},"last_outreach_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Outreach At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"}},"type":"object","required":["id","first_name","last_name","email","status","is_subscribed","created_at"],"title":"ContactListItem"},"ContactMergeRequest":{"properties":{"source_contact_id":{"type":"string","format":"uuid","title":"Source Contact Id"}},"type":"object","required":["source_contact_id"],"title":"ContactMergeRequest"},"ContactPatch":{"properties":{"first_name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Last Name"},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Phone"},"job_title":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Job Title"},"linkedin_url":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Linkedin Url"},"company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"is_subscribed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Subscribed"},"timezone":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Timezone"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"custom_fields":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Custom Fields"},"source_outreach_record_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Source Outreach Record Id"}},"type":"object","title":"ContactPatch"},"ContactStatusCreate":{"properties":{"label":{"type":"string","maxLength":100,"minLength":1,"title":"Label"},"is_initial":{"type":"boolean","title":"Is Initial","default":false}},"type":"object","required":["label"],"title":"ContactStatusCreate"},"ContactStatusReorder":{"properties":{"keys":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Keys"}},"type":"object","required":["keys"],"title":"ContactStatusReorder"},"ContactStatusUpdate":{"properties":{"label":{"anyOf":[{"type":"string","maxLength":100,"minLength":1},{"type":"null"}],"title":"Label"},"is_initial":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Initial"}},"type":"object","title":"ContactStatusUpdate"},"ContactSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"},"job_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Title"},"status":{"type":"string","title":"Status"}},"type":"object","required":["id","first_name","last_name","email","status"],"title":"ContactSummary"},"ContactSummaryForCompany":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"},"job_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Title"},"status":{"type":"string","title":"Status"}},"type":"object","required":["id","first_name","last_name","email","status"],"title":"ContactSummaryForCompany"},"ContactSummaryForOutreach":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"}},"type":"object","required":["id","first_name","last_name","email"],"title":"ContactSummaryForOutreach"},"ContactUpdate":{"properties":{"first_name":{"type":"string","maxLength":255,"minLength":1,"title":"First Name"},"last_name":{"type":"string","maxLength":255,"minLength":1,"title":"Last Name"},"phone":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Phone"},"job_title":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Job Title"},"linkedin_url":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Linkedin Url"},"company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"status":{"type":"string","title":"Status","default":"prospect"},"is_subscribed":{"type":"boolean","title":"Is Subscribed","default":true},"timezone":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Timezone"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]},"custom_fields":{"additionalProperties":true,"type":"object","title":"Custom Fields","default":{}}},"type":"object","required":["first_name","last_name"],"title":"ContactUpdate"},"ConversationDetail":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"messages":{"items":{"$ref":"#/components/schemas/MessageResponse"},"type":"array","title":"Messages"}},"type":"object","required":["id","title","created_at","updated_at"],"title":"ConversationDetail"},"ConversationListPage":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ConversationSummary"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["items"],"title":"ConversationListPage","description":"Keyset page of a user's conversations, newest-active first."},"ConversationRenameRequest":{"properties":{"title":{"type":"string","maxLength":255,"minLength":1,"title":"Title"}},"additionalProperties":false,"type":"object","required":["title"],"title":"ConversationRenameRequest","description":"Rename a conversation (M-Memory-b, D8)."},"ConversationSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","title","created_at","updated_at"],"title":"ConversationSummary","description":"List-row projection — no messages (the detail endpoint carries those)."},"ConversationThread":{"properties":{"sent":{"$ref":"#/components/schemas/OutreachRecordDetail"},"replies":{"items":{"$ref":"#/components/schemas/OutreachRecordDetail"},"type":"array","title":"Replies","default":[]},"reply_count":{"type":"integer","title":"Reply Count","default":0},"latest_activity_at":{"type":"string","format":"date-time","title":"Latest Activity At"}},"type":"object","required":["sent","latest_activity_at"],"title":"ConversationThread","description":"M-Thread-a (GAP-031) — one conversation in the per-contact thread list.\n\nA sent root row plus the inbound reply children that point back at it,\nwith rollup metadata for list rendering. Distinct from `OutreachThreadView`\n(which serves the single-thread `?include_thread=true` panel) by the\n`reply_count` + `latest_activity_at` rollups the conversation list needs to\norder + summarise threads without re-walking each one. `replies` is ordered\n`sent_at` ascending so the thread reads oldest-first (D7); a reply-less send\nis a valid one-message thread with `reply_count=0` (D3)."},"ConversationThreadPage":{"properties":{"threads":{"items":{"$ref":"#/components/schemas/ConversationThread"},"type":"array","title":"Threads","default":[]},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","title":"ConversationThreadPage","description":"M-Thread-a — one keyset page of a contact's conversation threads.\n\n`next_cursor` is the opaque base64 `(latest_activity_at, sent_id)` of the\nlast thread on this page; pass it back as `cursor` for the next page. Null\nwhen this is the final page."},"CostEstimatePayload":{"properties":{"model_name":{"type":"string","title":"Model Name"},"is_local_model":{"type":"boolean","title":"Is Local Model"},"used_fallback":{"type":"boolean","title":"Used Fallback"},"batch_size":{"type":"integer","title":"Batch Size"},"member_count":{"type":"integer","title":"Member Count"},"estimated_total_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Estimated Total Usd"},"per_recipient_avg_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Per Recipient Avg Usd"},"cap_usd_per_100":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cap Usd Per 100"},"cap_total_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cap Total Usd"},"exceeds_cap":{"type":"boolean","title":"Exceeds Cap"},"prompt_tokens_total":{"type":"integer","title":"Prompt Tokens Total"},"completion_tokens_per_recipient":{"type":"integer","title":"Completion Tokens Per Recipient"}},"type":"object","required":["model_name","is_local_model","used_fallback","batch_size","member_count","estimated_total_usd","per_recipient_avg_usd","cap_usd_per_100","cap_total_usd","exceeds_cap","prompt_tokens_total","completion_tokens_per_recipient"],"title":"CostEstimatePayload","description":"Wire format for `CostEstimate`. Decimals serialise as strings so the\nUI can render with full precision and reason about cents without\nfloat drift.\n\nThe four USD fields are `null` for managed \"Limena AI\" tenants — their\nestimate is Limena's own cost basis, not their spend, so the compose UI\nhides the dollar figures and shows recipient counts instead (see\n`tenant_llm_config.is_managed_ai_tenant`). `exceeds_cap` is still populated\nso the batch cap keeps working as a silent server-side backstop."},"CreateTenantRequest":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"slug":{"type":"string","maxLength":100,"minLength":3,"title":"Slug"},"plan":{"$ref":"#/components/schemas/Plan","default":"ultra"},"data_region":{"type":"string","maxLength":50,"minLength":1,"title":"Data Region","default":"eu-west-2"},"owner_email":{"type":"string","format":"email","title":"Owner Email"},"owner_full_name":{"type":"string","maxLength":255,"minLength":1,"title":"Owner Full Name"}},"type":"object","required":["name","slug","owner_email","owner_full_name"],"title":"CreateTenantRequest","description":"POST /admin/tenants body. The owner is provisioned in `welcome_email`\nmode only: a 72h set-password token is minted and `send_welcome_email` is\nenqueued (the admin never sets or sees the owner's password)."},"CreateTenantResponse":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"slug":{"type":"string","title":"Slug"},"owner_user_id":{"type":"string","format":"uuid","title":"Owner User Id"},"owner_email":{"type":"string","title":"Owner Email"},"set_password_link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Set Password Link"}},"type":"object","required":["tenant_id","slug","owner_user_id","owner_email","set_password_link"],"title":"CreateTenantResponse"},"CustomFieldCreate":{"properties":{"label":{"type":"string","maxLength":100,"minLength":1,"title":"Label"},"type":{"type":"string","maxLength":20,"minLength":1,"title":"Type"},"options":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Options"}},"type":"object","required":["label","type"],"title":"CustomFieldCreate"},"CustomFieldReorder":{"properties":{"keys":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Keys"}},"type":"object","required":["keys"],"title":"CustomFieldReorder"},"CustomFieldUpdate":{"properties":{"label":{"anyOf":[{"type":"string","maxLength":100,"minLength":1},{"type":"null"}],"title":"Label"},"options":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Options"}},"type":"object","title":"CustomFieldUpdate"},"DashboardAlertCreate":{"properties":{"tile_id":{"type":"string","format":"uuid","title":"Tile Id"},"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"condition":{"type":"string","enum":["gt","lt","pct_change_gt","pct_change_lt"],"title":"Condition"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Threshold"},"delivery":{"$ref":"#/components/schemas/AlertDeliveryConfig"},"cooldown_seconds":{"type":"integer","maximum":604800.0,"minimum":60.0,"title":"Cooldown Seconds","default":86400}},"additionalProperties":false,"type":"object","required":["tile_id","name","condition","threshold"],"title":"DashboardAlertCreate"},"DashboardAlertListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/DashboardAlertRead"},"type":"array","title":"Items"}},"additionalProperties":false,"type":"object","required":["items"],"title":"DashboardAlertListResponse"},"DashboardAlertRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"tile_id":{"type":"string","format":"uuid","title":"Tile Id"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"name":{"type":"string","title":"Name"},"condition":{"type":"string","title":"Condition"},"threshold":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Threshold"},"delivery":{"additionalProperties":true,"type":"object","title":"Delivery"},"is_active":{"type":"boolean","title":"Is Active"},"cooldown_seconds":{"type":"integer","title":"Cooldown Seconds"},"last_triggered_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Triggered At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"additionalProperties":false,"type":"object","required":["id","tenant_id","tile_id","created_by","name","condition","threshold","delivery","is_active","cooldown_seconds","last_triggered_at","created_at","updated_at"],"title":"DashboardAlertRead"},"DashboardAlertUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Name"},"condition":{"anyOf":[{"type":"string","enum":["gt","lt","pct_change_gt","pct_change_lt"]},{"type":"null"}],"title":"Condition"},"threshold":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Threshold"},"delivery":{"anyOf":[{"$ref":"#/components/schemas/AlertDeliveryConfig"},{"type":"null"}]},"cooldown_seconds":{"anyOf":[{"type":"integer","maximum":604800.0,"minimum":60.0},{"type":"null"}],"title":"Cooldown Seconds"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"additionalProperties":false,"type":"object","title":"DashboardAlertUpdate"},"DashboardClone":{"properties":{"new_name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"New Name"}},"additionalProperties":false,"type":"object","title":"DashboardClone","description":"Payload for `POST /api/v1/dashboards/{id}/clone`.\n\n`new_name` is optional — when omitted, the service auto-derives a\nfree `\"{name} (copy)\"` / `\"{name} (copy 2)\"` / ... per the M26c\nstanding decision."},"DashboardCreate":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"visibility":{"type":"string","enum":["private","tenant_shared"],"title":"Visibility","default":"private"}},"additionalProperties":false,"type":"object","required":["name"],"title":"DashboardCreate","description":"Payload for `POST /api/v1/dashboards`.\n\n`owner_user_id` is always the caller (sourced from the JWT). The\nroute layer never trusts a body-supplied owner."},"DashboardListItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"visibility":{"type":"string","title":"Visibility"},"is_system":{"type":"boolean","title":"Is System"},"owner_user_id":{"type":"string","format":"uuid","title":"Owner User Id"},"tile_count":{"type":"integer","title":"Tile Count","description":"Number of tiles on this dashboard. Sourced from a COUNT subquery."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"additionalProperties":false,"type":"object","required":["id","name","description","visibility","is_system","owner_user_id","tile_count","created_at","updated_at"],"title":"DashboardListItem","description":"Compact dashboard row for the picker — no tiles loaded.\n\nThe picker doesn't need tile data, just enough to render a\nselectable label. Loading tiles lazily on selection keeps the list\nresponse small (~30 dashboards × ~6 tiles × ~1KB spec = 180KB\navoided per list call)."},"DashboardListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/DashboardListItem"},"type":"array","title":"Items"}},"additionalProperties":false,"type":"object","required":["items"],"title":"DashboardListResponse","description":"List wrapper — keeps room for future pagination metadata.\n\nToday the picker shows every visible dashboard at once; tenants\nwon't realistically exceed ~30 (8 system + ~20 custom). When that\nbreaks, we add `next_cursor` and friends."},"DashboardRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"visibility":{"type":"string","title":"Visibility"},"is_system":{"type":"boolean","title":"Is System"},"owner_user_id":{"type":"string","format":"uuid","title":"Owner User Id"},"layout":{"additionalProperties":true,"type":"object","title":"Layout"},"tiles":{"items":{"$ref":"#/components/schemas/DashboardTileRead"},"type":"array","title":"Tiles"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"additionalProperties":false,"type":"object","required":["id","name","description","visibility","is_system","owner_user_id","layout","tiles","created_at","updated_at"],"title":"DashboardRead","description":"Full dashboard payload — header fields + tiles ordered by position."},"DashboardTileRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"dashboard_id":{"type":"string","format":"uuid","title":"Dashboard Id"},"name":{"type":"string","title":"Name"},"chart_spec":{"additionalProperties":true,"type":"object","title":"Chart Spec"},"position_x":{"type":"integer","title":"Position X"},"position_y":{"type":"integer","title":"Position Y"},"width":{"type":"integer","title":"Width"},"height":{"type":"integer","title":"Height"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"additionalProperties":false,"type":"object","required":["id","dashboard_id","name","chart_spec","position_x","position_y","width","height","created_at","updated_at"],"title":"DashboardTileRead","description":"A single tile on a dashboard — position + spec + dimensions."},"DashboardUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"visibility":{"anyOf":[{"type":"string","enum":["private","tenant_shared"]},{"type":"null"}],"title":"Visibility"}},"additionalProperties":false,"type":"object","title":"DashboardUpdate","description":"Payload for `PATCH /api/v1/dashboards/{id}`.\n\nOmit a key to leave it unchanged. To clear `description`, send the\nkey with a literal `null` value — the route reads `exclude_unset`\non this model to distinguish \"omitted\" from \"set to null\"."},"DataExportJobListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/DataExportJobRead"},"type":"array","title":"Items"}},"additionalProperties":false,"type":"object","required":["items"],"title":"DataExportJobListResponse"},"DataExportJobRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"initiated_by_user_id":{"type":"string","format":"uuid","title":"Initiated By User Id"},"status":{"type":"string","enum":["queued","running","complete","failed","expired"],"title":"Status"},"file_size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"File Size Bytes"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"downloaded_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Downloaded At"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"}},"additionalProperties":false,"type":"object","required":["id","tenant_id","initiated_by_user_id","status","file_size_bytes","expires_at","downloaded_at","error_message","created_at","completed_at"],"title":"DataExportJobRead"},"DealBulkDeleteRequest":{"properties":{"selection":{"$ref":"#/components/schemas/DealBulkSelection"}},"additionalProperties":false,"type":"object","required":["selection"],"title":"DealBulkDeleteRequest"},"DealBulkFilters":{"properties":{"stage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stage"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"contact_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Contact Id"},"company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"}},"additionalProperties":false,"type":"object","title":"DealBulkFilters","description":"Mirror of the `GET /deals` list query params (the canonical filter set)."},"DealBulkReassignRequest":{"properties":{"selection":{"$ref":"#/components/schemas/DealBulkSelection"},"owner_id":{"type":"string","format":"uuid","title":"Owner Id"}},"additionalProperties":false,"type":"object","required":["selection","owner_id"],"title":"DealBulkReassignRequest"},"DealBulkSelection":{"properties":{"ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Ids"},"filters":{"anyOf":[{"$ref":"#/components/schemas/DealBulkFilters"},{"type":"null"}]},"exclude_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Exclude Ids"}},"additionalProperties":false,"type":"object","title":"DealBulkSelection"},"DealCard":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"title":{"type":"string","title":"Title"},"value":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Value"},"currency":{"type":"string","title":"Currency"},"contact":{"anyOf":[{"$ref":"#/components/schemas/ContactSummary"},{"type":"null"}]},"owner":{"$ref":"#/components/schemas/UserSummary"},"close_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Close Date"},"days_in_stage":{"type":"integer","title":"Days In Stage"}},"type":"object","required":["id","title","currency","owner","days_in_stage"],"title":"DealCard"},"DealCreate":{"properties":{"contact_id":{"type":"string","format":"uuid","title":"Contact Id"},"company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"},"title":{"type":"string","maxLength":255,"minLength":1,"title":"Title"},"value":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":0.0},{"type":"null"}],"title":"Value"},"currency":{"type":"string","pattern":"^[A-Z]{3}$","title":"Currency","default":"GBP"},"stage":{"type":"string","title":"Stage","default":"qualified"},"close_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Close Date"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"custom_fields":{"additionalProperties":true,"type":"object","title":"Custom Fields","default":{}}},"type":"object","required":["contact_id","title"],"title":"DealCreate"},"DealDetail":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"title":{"type":"string","title":"Title"},"stage":{"type":"string","title":"Stage"},"value":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Value"},"currency":{"type":"string","title":"Currency"},"close_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Close Date"},"owner":{"$ref":"#/components/schemas/UserSummary"},"contact":{"anyOf":[{"$ref":"#/components/schemas/ContactSummary"},{"type":"null"}]},"company":{"anyOf":[{"$ref":"#/components/schemas/CompanySummaryForDeal"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time","title":"Created At"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"stage_changed_at":{"type":"string","format":"date-time","title":"Stage Changed At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"custom_fields":{"additionalProperties":true,"type":"object","title":"Custom Fields","default":{}}},"type":"object","required":["id","title","stage","currency","owner","created_at","stage_changed_at","updated_at"],"title":"DealDetail"},"DealInsightOut":{"properties":{"deal_id":{"type":"string","format":"uuid","title":"Deal Id"},"next_step":{"type":"string","title":"Next Step","description":"The deal brief's suggested next step — the per-card chip."},"status":{"type":"string","title":"Status","description":"The deal brief's current-status line (for a tooltip / hover)."}},"type":"object","required":["deal_id","next_step","status"],"title":"DealInsightOut","description":"A single deal's per-card chip, from its cached AI brief."},"DealListItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"title":{"type":"string","title":"Title"},"stage":{"type":"string","title":"Stage"},"value":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Value"},"currency":{"type":"string","title":"Currency"},"close_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Close Date"},"owner":{"$ref":"#/components/schemas/UserSummary"},"contact":{"anyOf":[{"$ref":"#/components/schemas/ContactSummary"},{"type":"null"}]},"company":{"anyOf":[{"$ref":"#/components/schemas/CompanySummaryForDeal"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time","title":"Created At"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"}},"type":"object","required":["id","title","stage","currency","owner","created_at"],"title":"DealListItem"},"DealMergeRequest":{"properties":{"source_deal_id":{"type":"string","format":"uuid","title":"Source Deal Id"}},"type":"object","required":["source_deal_id"],"title":"DealMergeRequest"},"DealSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"title":{"type":"string","title":"Title"},"value":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Value"},"currency":{"type":"string","title":"Currency"},"stage":{"type":"string","title":"Stage"}},"type":"object","required":["id","title","currency","stage"],"title":"DealSummary"},"DealUpdate":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Title"},"value":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":0.0},{"type":"null"}],"title":"Value"},"stage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stage"},"close_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Close Date"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"custom_fields":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Custom Fields"}},"type":"object","title":"DealUpdate"},"DemoRequestCreate":{"properties":{"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name"},"email":{"type":"string","maxLength":255,"format":"email","title":"Email"},"firm":{"type":"string","maxLength":200,"minLength":1,"title":"Firm"},"headcount":{"type":"string","enum":["1-4","5-10","11-30","31-50","51+"],"title":"Headcount"},"what_brings_you":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"What Brings You"},"source":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Source"}},"type":"object","required":["name","email","firm","headcount"],"title":"DemoRequestCreate","description":"Public POST body."},"DigestSubscriptionCreate":{"properties":{"preset_key":{"type":"string","maxLength":100,"title":"Preset Key"},"cadence":{"type":"string","enum":["weekly","monthly"],"title":"Cadence","default":"weekly"},"schedule_hour":{"type":"integer","maximum":23.0,"minimum":0.0,"title":"Schedule Hour","default":9}},"additionalProperties":false,"type":"object","required":["preset_key"],"title":"DigestSubscriptionCreate"},"DigestSubscriptionListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/DigestSubscriptionRead"},"type":"array","title":"Items"}},"additionalProperties":false,"type":"object","required":["items"],"title":"DigestSubscriptionListResponse"},"DigestSubscriptionRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"preset_key":{"type":"string","title":"Preset Key"},"cadence":{"type":"string","title":"Cadence"},"schedule_hour":{"type":"integer","title":"Schedule Hour"},"is_active":{"type":"boolean","title":"Is Active"},"last_sent_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Sent At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"additionalProperties":false,"type":"object","required":["id","tenant_id","user_id","preset_key","cadence","schedule_hour","is_active","last_sent_at","created_at","updated_at"],"title":"DigestSubscriptionRead"},"DigestSubscriptionUpdate":{"properties":{"cadence":{"anyOf":[{"type":"string","enum":["weekly","monthly"]},{"type":"null"}],"title":"Cadence"},"schedule_hour":{"anyOf":[{"type":"integer","maximum":23.0,"minimum":0.0},{"type":"null"}],"title":"Schedule Hour"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"additionalProperties":false,"type":"object","title":"DigestSubscriptionUpdate"},"Dimension":{"properties":{"field":{"type":"string","maxLength":128,"minLength":1,"title":"Field"},"time_grain":{"anyOf":[{"$ref":"#/components/schemas/TimeGrain"},{"type":"null"}]}},"additionalProperties":false,"type":"object","required":["field"],"title":"Dimension","description":"A grouping axis on the chart.\n\n`time_grain` is required iff the underlying field is of `TIME` kind;\nPydantic doesn't know the field's kind (that's in the registry, not\nin this schema), so this rule is enforced at the validator layer."},"DraftDetail":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"contact":{"$ref":"#/components/schemas/ContactSummaryForOutreach"},"campaign_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Campaign Id"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"},"body_full":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Full"},"body_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Summary"},"status":{"type":"string","title":"Status"},"approved":{"type":"boolean","title":"Approved"},"body_edited_by_user":{"type":"boolean","title":"Body Edited By User"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","contact","status","approved","body_edited_by_user","created_at"],"title":"DraftDetail","description":"Full draft payload for the inline editor — body included verbatim."},"DraftEvidence":{"properties":{"claim":{"type":"string","maxLength":2000,"minLength":1,"title":"Claim"},"source_label":{"type":"string","maxLength":200,"minLength":1,"title":"Source Label"},"source_text":{"type":"string","maxLength":2000,"minLength":1,"title":"Source Text"}},"additionalProperties":false,"type":"object","required":["claim","source_label","source_text"],"title":"DraftEvidence","description":"One grounded claim in the composed draft, tied to a supplied source.\n\nEmitted only after the service has verified the model cited a real supplied\nsource id (a fabricated citation is dropped in code), then resolved that id\nto its human-readable label + the fact text — so a `DraftEvidence` on the\nwire always points at a fact that was actually supplied to the model."},"DraftListItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"contact":{"$ref":"#/components/schemas/ContactSummaryForOutreach"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"},"body_preview":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Preview"},"status":{"type":"string","title":"Status"},"approved":{"type":"boolean","title":"Approved"},"body_edited_by_user":{"type":"boolean","title":"Body Edited By User"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","contact","status","approved","body_edited_by_user","created_at"],"title":"DraftListItem","description":"Single row in `GET /api/v1/campaigns/{id}/drafts`.\n\nCollapsed-view payload: enough for the list row's header (recipient\n+ subject + first-line preview + state badges) without shipping the\nfull body to the wire on every list refresh. The expanded view in\nthe UI hits the detail endpoint inline before showing the editor."},"DraftPatch":{"properties":{"body_full":{"anyOf":[{"type":"string","maxLength":10000},{"type":"null"}],"title":"Body Full"},"subject":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Subject"},"approved":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Approved"}},"additionalProperties":false,"type":"object","title":"DraftPatch","description":"Body for `PATCH /api/v1/campaigns/{id}/drafts/{record_id}`.\n\nInline edit + approve toggle. Uses the M26c standing decision\n`*_set: bool` companion pattern for nullable PATCH semantics:\n`body_set=True, body=None` means \"clear the body\" — distinct from\n\"client omitted the key\". Pydantic's `model_fields_set` at the\nroute boundary computes the companions automatically.\n\nApproval is a bool with no nullable shape — `approved=None` means\n\"don't touch the flag\", `approved=True/False` flips it."},"DryRunActionPreview":{"properties":{"type":{"type":"string","title":"Type"},"summary":{"type":"string","title":"Summary"},"will_execute":{"type":"boolean","title":"Will Execute","default":true},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings"}},"type":"object","required":["type","summary"],"title":"DryRunActionPreview","description":"What one action *would* do, described without executing it. `will_execute`\nis False when the action would be skipped or fail (the `warnings` say why)."},"DryRunDraftRequest":{"properties":{"draft":{"$ref":"#/components/schemas/WorkflowRuleCreate"},"resource_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Resource Id"}},"additionalProperties":false,"type":"object","required":["draft"],"title":"DryRunDraftRequest","description":"Body for a non-committing test of an *unsaved* draft (the builder's\ntest-before-save, mirroring /compile). The draft is validated as a full rule."},"DryRunRequest":{"properties":{"resource_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Resource Id"}},"additionalProperties":false,"type":"object","title":"DryRunRequest","description":"Body for a non-committing test of a *saved* rule. `resource_id` is optional —\nomitted, the engine auto-picks the most-recent record of the trigger entity."},"DryRunResult":{"properties":{"matched":{"type":"boolean","title":"Matched"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"trigger_event":{"type":"string","title":"Trigger Event"},"resource_type":{"type":"string","title":"Resource Type"},"resource_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Resource Id"},"resource_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource Label"},"actions_would_fire":{"items":{"$ref":"#/components/schemas/DryRunActionPreview"},"type":"array","title":"Actions Would Fire"},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings"}},"type":"object","required":["matched","trigger_event","resource_type"],"title":"DryRunResult","description":"Outcome of a non-committing rule test against one record (UX-013).\n\n`matched` is whether the rule's conditions hold for the tested record; `reason`\nnames the first failing predicate when they don't. `actions_would_fire` is\npopulated only when matched. `resource_id`/`resource_label` are null when the\ntrigger entity has no record to test against — `warnings` explains."},"EmailAccount":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"provider":{"type":"string","title":"Provider"},"email_address":{"type":"string","format":"email","title":"Email Address"},"send_as_aliases":{"items":{"$ref":"#/components/schemas/SendAsAlias"},"type":"array","title":"Send As Aliases"},"tracking_enabled":{"type":"boolean","title":"Tracking Enabled"},"background_scan_enabled":{"type":"boolean","title":"Background Scan Enabled"},"connected_at":{"type":"string","format":"date-time","title":"Connected At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"}},"type":"object","required":["id","user_id","provider","email_address","tracking_enabled","background_scan_enabled","connected_at","last_used_at","revoked_at"],"title":"EmailAccount"},"EmailAccountBackgroundScanUpdate":{"properties":{"enabled":{"type":"boolean","title":"Enabled"}},"type":"object","required":["enabled"],"title":"EmailAccountBackgroundScanUpdate","description":"M-SelfBuild-a — body for `PATCH /api/v1/integrations/accounts/{id}/background-scan`."},"EmailAccountList":{"properties":{"accounts":{"items":{"$ref":"#/components/schemas/EmailAccount"},"type":"array","title":"Accounts"}},"type":"object","required":["accounts"],"title":"EmailAccountList"},"EmailAccountTrackingUpdate":{"properties":{"enabled":{"type":"boolean","title":"Enabled"}},"type":"object","required":["enabled"],"title":"EmailAccountTrackingUpdate","description":"M28 — body for `PATCH /api/v1/integrations/gmail/accounts/{id}/tracking`."},"EmailCandidateOut":{"properties":{"email":{"type":"string","title":"Email"},"status":{"$ref":"#/components/schemas/EmailStatus"},"source":{"type":"string","title":"Source"},"confidence":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Confidence"},"found_at":{"type":"string","format":"date-time","title":"Found At"},"custom_fields":{"additionalProperties":true,"type":"object","title":"Custom Fields"}},"type":"object","required":["email","status","source","confidence","found_at","custom_fields"],"title":"EmailCandidateOut","description":"One resolved address + its provenance. ``custom_fields`` is the flat\nprovenance dict to send straight back as ``custom_fields`` on ``POST /contacts``\nwhen the user adds the contact (the email value goes on ``email``)."},"EmailChangeRequest":{"properties":{"current_password":{"type":"string","title":"Current Password"},"new_email":{"type":"string","format":"email","title":"New Email"}},"type":"object","required":["current_password","new_email"],"title":"EmailChangeRequest"},"EmailStatus":{"type":"string","enum":["verified","guessed","unverified"],"title":"EmailStatus","description":"Email-provenance deliverability status (M-Find-a) — mirrors the finder\nwaterfall's vocabulary (`integrations/email_finder/base.py`): `verified` = an\nexplicit deliverability check passed; `guessed` = a finder hit carrying a\nconfidence score but unverified; `unverified` = no confidence signal (or a\nfailed verification). The existing `contacts.email_invalid` bool stays the hard\nbounce signal — this is the *find-time* provenance, not the live deliverability."},"EmailTemplateCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"subject":{"type":"string","minLength":1,"title":"Subject"},"body":{"type":"string","minLength":1,"title":"Body"},"folder":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Folder"}},"type":"object","required":["name","subject","body"],"title":"EmailTemplateCreate"},"EmailTemplateListItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"subject":{"type":"string","title":"Subject"},"variables":{"items":{"type":"string"},"type":"array","title":"Variables"},"folder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","name","subject","created_at","updated_at"],"title":"EmailTemplateListItem","description":"Listing projection — omits the full body (table rows don't need it).\nThe M26c library page reads name + subject + variable count + folder +\nupdated_at off this shape."},"EmailTemplateRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"name":{"type":"string","title":"Name"},"subject":{"type":"string","title":"Subject"},"body":{"type":"string","title":"Body"},"variables":{"items":{"type":"string"},"type":"array","title":"Variables"},"folder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","tenant_id","name","subject","body","created_at","updated_at"],"title":"EmailTemplateRead","description":"Detail view — full subject + body. Used by M26c GET /{id} and by the\nM26d compose flow when a template is selected."},"EmailTemplateUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"subject":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Subject"},"body":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Body"},"folder":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Folder"}},"type":"object","title":"EmailTemplateUpdate","description":"Partial update — any subset of fields, at least one required.\n\nEmpty-body rejection is the route's responsibility so the audit row\ncarries a meaningful diff (mirrors `SavedSegmentUpdate`)."},"EnrichmentJobCreated":{"properties":{"job_id":{"type":"string","title":"Job Id","description":"ARQ job id to poll for the enrichment result."}},"type":"object","required":["job_id"],"title":"EnrichmentJobCreated","description":"The handle returned by ``POST …/enrich`` — poll ``GET …/enrich/{job_id}``."},"EnrichmentJobOut":{"properties":{"status":{"type":"string","enum":["pending","done","error","expired"],"title":"Status","description":"pending (keep polling) | done | error | expired (re-run)."},"result":{"anyOf":[{"$ref":"#/components/schemas/EnrichmentResult"},{"type":"null"}],"description":"The cited proposals + sources, present only when status == 'done'."},"progress":{"anyOf":[{"$ref":"#/components/schemas/JobProgress"},{"type":"null"}],"description":"Live phase/count snapshot while status == 'pending'; null once terminal."}},"type":"object","required":["status"],"title":"EnrichmentJobOut","description":"Poll envelope: the job's state + the cited result once it's ``done``."},"EnrichmentResult":{"properties":{"entity_type":{"type":"string","title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"},"label":{"type":"string","title":"Label"},"proposals":{"items":{"$ref":"#/components/schemas/FieldProposal"},"type":"array","title":"Proposals"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary"},"sources":{"items":{"$ref":"#/components/schemas/EnrichmentSource"},"type":"array","title":"Sources"}},"type":"object","required":["entity_type","entity_id","label","proposals"],"title":"EnrichmentResult"},"EnrichmentSource":{"properties":{"title":{"type":"string","title":"Title"},"url":{"type":"string","title":"Url"}},"type":"object","required":["title","url"],"title":"EnrichmentSource"},"EntitlementsView":{"properties":{"plan":{"type":"string","title":"Plan"},"features":{"items":{"type":"string"},"type":"array","title":"Features"},"limits":{"additionalProperties":{"type":"integer"},"type":"object","title":"Limits"},"ai_monthly_token_limit":{"type":"integer","title":"Ai Monthly Token Limit"}},"type":"object","required":["plan","features","limits","ai_monthly_token_limit"],"title":"EntitlementsView","description":"M-Tier-c (GAP-054) — the resolved plan entitlements echoed onto the\ncurrent-user response so the frontend can hide/disable gated affordances\nproactively without a second fetch (mirrored client-side by\n``frontend/src/lib/entitlements.ts``, kept in fact-parity by a drift test).\n\nAdvisory UX only — the backend 402 (``PLAN_UPGRADE_REQUIRED``) is the sole\nauthority. ``features`` is the plan's resolved hard-gate set (sorted for a\nstable snapshot); ``limits`` carries the numeric caps (``-1`` = uncapped);\nthe four ``tenant.settings`` override flags are deliberately NOT surfaced\nhere — they belong to the tenant-settings query, not this per-user echo."},"Entity":{"type":"string","enum":["contact","company","deal","outreach_record","campaign","prospect_list","ai_usage_log","audit_log","user"],"title":"Entity","description":"Root entity types analytics charts can be built against.\n\nStrict subset of the project's domain models. Mapping:\n    CONTACT          → app.models.contact.Contact\n    COMPANY          → app.models.company.Company\n    DEAL             → app.models.deal.Deal\n    OUTREACH_RECORD  → app.models.outreach.OutreachRecord\n    CAMPAIGN         → app.models.campaign.Campaign\n    PROSPECT_LIST    → app.models.prospect_list.ProspectList\n    AI_USAGE_LOG     → app.models.ai_usage_log.AIUsageLog\n    AUDIT_LOG        → app.models.audit_log.AuditLog\n    USER             → app.models.user.User\n\nUSER is primarily a *join target* — the CRM entities carry an\n`owner_id` FK to it, and a bare `owner_id` dimension resolves to the\nowner's display name via the compiler's FK-label path (M-Charts-a).\nIt is also a valid root entity (RLS-scoped, tenant-only) so \"team by\nrole\" style charts are expressible; only non-sensitive columns\n(name, email, role, created_at) are registered."},"Evidence":{"properties":{"claim":{"type":"string","maxLength":2000,"minLength":1,"title":"Claim"},"url":{"type":"string","maxLength":2083,"minLength":1,"format":"uri","title":"Url"},"retrieved_at":{"type":"string","format":"date-time","title":"Retrieved At"}},"additionalProperties":false,"type":"object","required":["claim","url","retrieved_at"],"title":"Evidence","description":"Provenance for a claim attached to a prospect record.\n\nEvery research-derived claim should carry one of these so the\nrecord is auditable back to its source — the Registrar agent is\nresponsible for producing these alongside any structured intel."},"ExecuteResponse":{"properties":{"import_job_id":{"type":"string","format":"uuid","title":"Import Job Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["import_job_id","status"],"title":"ExecuteResponse"},"ExportRunListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ExportRunRead"},"type":"array","title":"Items"}},"additionalProperties":false,"type":"object","required":["items"],"title":"ExportRunListResponse"},"ExportRunRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"scheduled_export_id":{"type":"string","format":"uuid","title":"Scheduled Export Id"},"status":{"type":"string","title":"Status"},"output_object_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Output Object Key"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"run_at":{"type":"string","format":"date-time","title":"Run At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"download_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Download Url"}},"additionalProperties":false,"type":"object","required":["id","scheduled_export_id","status","output_object_key","error_message","run_at","completed_at"],"title":"ExportRunRead"},"FederatedSendApprovalOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"campaign_id":{"type":"string","format":"uuid","title":"Campaign Id"},"campaign_name":{"type":"string","title":"Campaign Name"},"requested_by":{"type":"string","title":"Requested By"},"account_email":{"type":"string","title":"Account Email"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","campaign_id","campaign_name","requested_by","account_email","created_at"],"title":"FederatedSendApprovalOut","description":"A pending M33d campaign-send approval federated into the approvals inbox\n(M-Actions-b) — READ-only: storage is untouched and deciding stays the\nstep-up-gated campaign endpoints, which the surface links to via\n`campaign_id`. Tenant-visible (any member may decide a send), unlike the\nowner-private agent actions beside it."},"FieldMappingsRequest":{"properties":{"field_mappings":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":"object","title":"Field Mappings"},"entity_type":{"type":"string","title":"Entity Type","default":"contacts"},"sheet":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sheet"},"header_row":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Header Row"}},"type":"object","required":["field_mappings"],"title":"FieldMappingsRequest"},"FieldProposal":{"properties":{"field":{"type":"string","title":"Field"},"current_value":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Value"},"suggested_value":{"type":"string","title":"Suggested Value"},"source_url":{"type":"string","title":"Source Url"},"confidence":{"type":"string","title":"Confidence"},"rationale":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rationale"}},"type":"object","required":["field","current_value","suggested_value","source_url","confidence"],"title":"FieldProposal","description":"One sourced, human-reviewable field change. ``current_value`` is the record's\nvalue today (``None`` if unset); ``suggested_value`` is what the evidence\nsupports; ``source_url`` is mandatory (D10 — no source ⇒ no proposal)."},"FindEmailRequest":{"properties":{"first_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Last Name"},"full_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Full Name"},"company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id","description":"An existing CRM company to source the domain from."},"domain":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Domain","description":"Company domain to search (e.g. acme.com)."},"company":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Company","description":"Company name, when no domain is known."}},"additionalProperties":false,"type":"object","title":"FindEmailRequest","description":"Resolve an email for a person at a company. Needs a name (``full_name`` OR\n``first_name``/``last_name``) and a company signal (``company_id`` OR ``domain``\nOR ``company`` name) — enforced in the service, surfaced as a 422."},"FindEmailResponse":{"properties":{"found":{"type":"boolean","title":"Found"},"candidates":{"items":{"$ref":"#/components/schemas/EmailCandidateOut"},"type":"array","title":"Candidates"}},"type":"object","required":["found","candidates"],"title":"FindEmailResponse","description":"The resolve outcome. ``candidates`` holds the winning address (0 or 1 in\nM-Find-a — Hunter returns one per lookup; the list shape leaves room for a\nmulti-rung waterfall to surface alternates later)."},"FindPeopleJobCreated":{"properties":{"job_id":{"type":"string","title":"Job Id","description":"ARQ job id to poll for the discovery result."}},"type":"object","required":["job_id"],"title":"FindPeopleJobCreated","description":"The handle returned by ``POST …/people/discover`` — poll\n``GET …/people/discover/{job_id}``."},"FindPeopleJobOut":{"properties":{"status":{"type":"string","enum":["pending","done","error","expired"],"title":"Status","description":"pending (keep polling) | done | error | expired (re-run)."},"result":{"anyOf":[{"$ref":"#/components/schemas/FindPeopleResult"},{"type":"null"}],"description":"The scored, deduped people batch, present only when status == 'done'."},"progress":{"anyOf":[{"$ref":"#/components/schemas/JobProgress"},{"type":"null"}],"description":"Live phase/count snapshot while status == 'pending'; null once terminal."}},"type":"object","required":["status"],"title":"FindPeopleJobOut","description":"Poll envelope: the people-discovery job's state + the batch once ``done``."},"FindPeopleRequest":{"properties":{"targets":{"items":{"$ref":"#/components/schemas/FindPeopleTarget"},"type":"array","maxItems":5,"minItems":1,"title":"Targets"},"roles":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Roles","description":"Role/title focus, e.g. 'CTO or Head of Engineering'. Omit to target decision-makers broadly."},"limit":{"type":"integer","maximum":25.0,"minimum":1.0,"title":"Limit","description":"Maximum people to return (1–25).","default":10}},"type":"object","required":["targets"],"title":"FindPeopleRequest","description":"A people-discovery run — target companies + an optional role/title focus.\n\n``roles`` is free text (\"CTO or Head of Engineering\"); it carries user text,\nso the service fences it as untrusted before it reaches the prompt (SEC-009).\nOmit it to target decision-makers broadly."},"FindPeopleResult":{"properties":{"batch_id":{"type":"string","format":"uuid","title":"Batch Id"},"people":{"items":{"$ref":"#/components/schemas/ProspectPersonCreate"},"type":"array","title":"People"},"sources":{"items":{"$ref":"#/components/schemas/FindProspectsSource"},"type":"array","title":"Sources"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary"}},"type":"object","required":["batch_id"],"title":"FindPeopleResult","description":"A scored, deduped people batch — ready to feed ``stage_people``.\n\n``people`` may be empty (targets unresolvable, web unavailable, nothing new\nfound, or scoring degraded) — a valid, never-fabricated result, never a 500."},"FindPeopleTarget":{"properties":{"candidate_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Candidate Id","description":"An in-review prospect candidate to find people at."},"company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id","description":"An existing CRM company to find people at."}},"type":"object","title":"FindPeopleTarget","description":"One company a discovery run targets — an in-review candidate XOR a real CRM\ncompany. Exactly one id must be set; the service resolves it to the company's\nname/domain and refuses ids that don't resolve in-tenant (404)."},"FindProspectsJobCreated":{"properties":{"job_id":{"type":"string","title":"Job Id","description":"ARQ job id to poll for the discovery result."}},"type":"object","required":["job_id"],"title":"FindProspectsJobCreated","description":"The handle returned by ``POST …/discover`` — poll ``GET …/discover/{job_id}``."},"FindProspectsJobOut":{"properties":{"status":{"type":"string","enum":["pending","done","error","expired"],"title":"Status","description":"pending (keep polling) | done | error | expired (re-run)."},"result":{"anyOf":[{"$ref":"#/components/schemas/FindProspectsResult"},{"type":"null"}],"description":"The scored, deduped batch, present only when status == 'done'."},"progress":{"anyOf":[{"$ref":"#/components/schemas/JobProgress"},{"type":"null"}],"description":"Live phase/count snapshot while status == 'pending'; null once terminal."}},"type":"object","required":["status"],"title":"FindProspectsJobOut","description":"Poll envelope: the discovery job's state + the scored batch once ``done``.\n\n``done`` carries the full :class:`FindProspectsResult` so the UI renders the\nscored candidates without a second call; ``error`` / ``expired`` carry no\nresult (re-run)."},"FindProspectsRequest":{"properties":{"brief":{"anyOf":[{"type":"string","maxLength":400},{"type":"null"}],"title":"Brief","description":"Optional free-text targeting hint to narrow discovery (e.g. a city, vertical, or size). Omit to discover broadly against the ICP."},"limit":{"type":"integer","maximum":25.0,"minimum":1.0,"title":"Limit","description":"Maximum candidates to return (1–25).","default":12}},"type":"object","title":"FindProspectsRequest","description":"A discovery run — an optional targeting brief + a result cap.\n\n``brief`` is a free-text hint (\"Manchester IT firms\", \"fintech startups in the\nEU\") that narrows the ICP-derived search; omit it to discover broadly against the\nICP alone. It carries user text, so the service fences it as untrusted before it\nreaches the prompt (SEC-009)."},"FindProspectsResult":{"properties":{"batch_id":{"type":"string","format":"uuid","title":"Batch Id"},"candidates":{"items":{"$ref":"#/components/schemas/ProspectCandidateCreate"},"type":"array","title":"Candidates"},"sources":{"items":{"$ref":"#/components/schemas/FindProspectsSource"},"type":"array","title":"Sources"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary"}},"type":"object","required":["batch_id"],"title":"FindProspectsResult","description":"A scored, deduped discovery batch — ready to feed ``stage_prospects``.\n\n``batch_id`` is minted by the discovery run and echoed so the subsequent\n``stage_prospects`` write groups the staged rows under the run that produced\nthem. ``candidates`` may be empty (no ICP, web unavailable, nothing new found,\nor scoring degraded) — a valid, never-fabricated result, never a 500."},"FindProspectsSource":{"properties":{"title":{"type":"string","title":"Title"},"url":{"type":"string","title":"Url"}},"type":"object","required":["title","url"],"title":"FindProspectsSource","description":"One web source the discovery pass consulted — surfaced so the reviewer can\nsee where the batch was grounded (mirrors ``EnrichmentSource``)."},"FirmographicBand":{"properties":{"min":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min"},"max":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max"},"sweet_spot_min":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Sweet Spot Min"},"sweet_spot_max":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Sweet Spot Max"},"unit":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Unit"},"notes":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Notes"}},"type":"object","title":"FirmographicBand","description":"A numeric range with an optional tighter sweet-spot — headcount / revenue /\ntenure. ``unit`` keeps it currency- and measure-agnostic (``\"employees\"`` /\n``\"GBP/year\"`` / ``\"months\"``) so the universal field names stay tenant-neutral.\nThe derive job fills ``headcount`` / ``revenue`` from the tenant's *actual* won\ncustomers (code-enforced, not LLM-guessed)."},"FirmographicBands":{"properties":{"headcount":{"anyOf":[{"$ref":"#/components/schemas/FirmographicBand"},{"type":"null"}]},"revenue":{"anyOf":[{"$ref":"#/components/schemas/FirmographicBand"},{"type":"null"}]},"tenure":{"anyOf":[{"$ref":"#/components/schemas/FirmographicBand"},{"type":"null"}]}},"type":"object","title":"FirmographicBands","description":"The three firmographic dimensions the ICP scores against."},"FitRating":{"type":"string","enum":["strong","moderate","weak","disqualified"],"title":"FitRating","description":"Universal fit-rating vocabulary — mirrors the harness `FitRating`.\n\nGraduated from `limena-labs-agents` so native + harness share the\nschema (the universality rule): the field *name* and these *values*\nare universal across tenants; only the per-record assignment is\ntenant data. Reused by the ICP rubric (M-Prospect-b chunk 2) and by\n`find_prospects` scoring (M-Prospect-c)."},"ForgotPasswordRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"}},"type":"object","required":["email"],"title":"ForgotPasswordRequest"},"FormConfig":{"properties":{"fields":{"items":{"$ref":"#/components/schemas/FormFieldSpec"},"type":"array","maxItems":20,"minItems":1,"title":"Fields"},"defaults":{"$ref":"#/components/schemas/FormDefaults"},"redirect_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Redirect Url"}},"additionalProperties":false,"type":"object","required":["fields"],"title":"FormConfig","description":"The validated `forms.config` JSONB."},"FormCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"is_active":{"type":"boolean","title":"Is Active","default":true},"config":{"$ref":"#/components/schemas/FormConfig"}},"type":"object","required":["name","config"],"title":"FormCreate"},"FormDefaults":{"properties":{"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"status":{"anyOf":[{"type":"string","maxLength":50,"minLength":1},{"type":"null"}],"title":"Status"},"tags":{"items":{"type":"string","maxLength":50,"minLength":1},"type":"array","title":"Tags"},"campaign_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Campaign Id"}},"type":"object","title":"FormDefaults","description":"Landing config applied to the contact created/linked by a submission."},"FormFieldSpec":{"properties":{"key":{"type":"string","maxLength":64,"minLength":1,"title":"Key"},"label":{"type":"string","maxLength":120,"minLength":1,"title":"Label"},"type":{"type":"string","enum":["text","textarea","email","select"],"title":"Type","default":"text"},"required":{"type":"boolean","title":"Required","default":false},"options":{"anyOf":[{"items":{"type":"string","maxLength":120,"minLength":1},"type":"array"},{"type":"null"}],"title":"Options"}},"type":"object","required":["key","label"],"title":"FormFieldSpec","description":"One field on the form. Single-step, fixed — no conditional logic (DA5)."},"FormRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"is_active":{"type":"boolean","title":"Is Active"},"config":{"$ref":"#/components/schemas/FormConfig"},"public_url":{"type":"string","title":"Public Url"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","name","description","is_active","config","public_url","created_by","created_at","updated_at"],"title":"FormRead","description":"Admin-facing form detail. `public_url` is derived from the signed token."},"FormSubmissionRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"form_id":{"type":"string","format":"uuid","title":"Form Id"},"disposition":{"type":"string","title":"Disposition"},"is_known_contact":{"type":"boolean","title":"Is Known Contact"},"contact_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Contact Id"},"company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","form_id","disposition","is_known_contact","contact_id","company_id","payload","created_at"],"title":"FormSubmissionRead"},"FormSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"is_active":{"type":"boolean","title":"Is Active"},"public_url":{"type":"string","title":"Public Url"},"submission_count":{"type":"integer","title":"Submission Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","is_active","public_url","submission_count","created_at"],"title":"FormSummary","description":"Admin list-row projection."},"FormUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"config":{"anyOf":[{"$ref":"#/components/schemas/FormConfig"},{"type":"null"}]}},"type":"object","title":"FormUpdate"},"GenerateDraftsResponse":{"properties":{"campaign_id":{"type":"string","format":"uuid","title":"Campaign Id"},"send_status":{"type":"string","title":"Send Status","default":"generating"},"recipient_count":{"type":"integer","title":"Recipient Count"},"cost_estimate":{"$ref":"#/components/schemas/CostEstimatePayload"}},"type":"object","required":["campaign_id","recipient_count","cost_estimate"],"title":"GenerateDraftsResponse","description":"200 response from `POST /campaigns/{id}/generate-drafts`. The\nrecipient count + cost estimate are echoed back so the compose UI\ncan surface \"queued 47 drafts, ~$1.20\" without a second roundtrip."},"GlobalSearchResponse":{"properties":{"contacts":{"items":{"$ref":"#/components/schemas/SearchHit"},"type":"array","title":"Contacts"},"companies":{"items":{"$ref":"#/components/schemas/SearchHit"},"type":"array","title":"Companies"},"campaigns":{"items":{"$ref":"#/components/schemas/SearchHit"},"type":"array","title":"Campaigns"},"deals":{"items":{"$ref":"#/components/schemas/SearchHit"},"type":"array","title":"Deals"},"outreach":{"items":{"$ref":"#/components/schemas/SearchHit"},"type":"array","title":"Outreach"}},"type":"object","required":["contacts","companies","campaigns","deals","outreach"],"title":"GlobalSearchResponse","description":"Five-bucket result shape returned by `GET /api/v1/search`."},"GmailAuthorizeResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"}},"type":"object","required":["auth_url"],"title":"GmailAuthorizeResponse","description":"Returned by GET /api/v1/integrations/gmail/authorize. The frontend\nnavigates the user to `auth_url`; Google redirects back to the registered\nredirect URI with `code` + `state` query params."},"GmailCallbackPayload":{"properties":{"code":{"type":"string","minLength":1,"title":"Code"},"state":{"type":"string","minLength":1,"title":"State"}},"type":"object","required":["code","state"],"title":"GmailCallbackPayload","description":"POSTed by the frontend callback page after Google's redirect.\n\nThe frontend extracts `code` + `state` from the URL and forwards them\nhere so the backend can verify state, exchange the code, and persist the\nencrypted refresh token. Two-step intentionally: keeps secrets off the\nURL-visible frontend route once exchange completes."},"GmailCallbackResponse":{"properties":{"account":{"$ref":"#/components/schemas/EmailAccount"}},"type":"object","required":["account"],"title":"GmailCallbackResponse","description":"Result of a successful OAuth exchange. Surfaces the connected account\nso the frontend can update its UI without an extra round-trip."},"GroundedDraftRequest":{"properties":{"contact_id":{"type":"string","format":"uuid","title":"Contact Id"},"brief":{"type":"string","maxLength":2000,"minLength":1,"title":"Brief","description":"What the email should accomplish (e.g. 'intro about our onboarding offer')."},"tone":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Tone","description":"Optional tone hint (e.g. 'warm', 'direct', 'formal')."},"sources":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":20},{"type":"null"}],"title":"Sources","description":"Optional extra facts to ground the draft in (e.g. pasted research). Treated strictly as data, never as instructions."}},"additionalProperties":false,"type":"object","required":["contact_id","brief"],"title":"GroundedDraftRequest"},"GroundedDraftResponse":{"properties":{"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body"},"evidence":{"items":{"$ref":"#/components/schemas/DraftEvidence"},"type":"array","title":"Evidence"},"explanation":{"type":"string","title":"Explanation"}},"additionalProperties":false,"type":"object","required":["subject","body","explanation"],"title":"GroundedDraftResponse","description":"Composed draft + cited evidence.\n\n`subject`/`body` are null when composition fails closed (malformed model\noutput after one retry) — the review card shows `explanation` and the user\nfalls back to the manual composer. The draft is reviewed before send and\nnever auto-sent."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"IcpDerivationJobCreated":{"properties":{"job_id":{"type":"string","title":"Job Id","description":"ARQ job id to poll for the derivation result."}},"type":"object","required":["job_id"],"title":"IcpDerivationJobCreated","description":"The handle returned by ``POST …/icp/derive`` — poll ``GET …/icp/derive/{job_id}``."},"IcpDerivationJobOut":{"properties":{"status":{"type":"string","enum":["pending","done","error","expired"],"title":"Status","description":"pending (keep polling) | done | error | expired (re-run)."},"profile":{"anyOf":[{"$ref":"#/components/schemas/IcpProfile-Output"},{"type":"null"}],"description":"The synthesized ICP, present only when status == 'done'."},"progress":{"anyOf":[{"$ref":"#/components/schemas/JobProgress"},{"type":"null"}],"description":"Live phase/count snapshot while status == 'pending'; null once terminal."}},"type":"object","required":["status"],"title":"IcpDerivationJobOut","description":"Poll envelope: the derive job's state + the synthesized profile once ``done``.\n\n``done`` means the ``icp_profiles`` row has already been upserted with\n``source='derived'`` — ``profile`` echoes it so the UI can render without a\nsecond ``GET``."},"IcpProfile-Input":{"properties":{"unifying_characteristic":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Unifying Characteristic"},"target_subverticals":{"items":{"type":"string","maxLength":300,"minLength":1},"type":"array","maxItems":50,"title":"Target Subverticals"},"firmographic_bands":{"$ref":"#/components/schemas/FirmographicBands"},"buying_signal_vocab":{"items":{"$ref":"#/components/schemas/BuyingSignalVocabItem"},"type":"array","maxItems":50,"title":"Buying Signal Vocab"},"fit_rating_criteria":{"additionalProperties":{"type":"string","maxLength":4000},"propertyNames":{"$ref":"#/components/schemas/FitRating"},"type":"object","title":"Fit Rating Criteria"},"anti_icp":{"items":{"$ref":"#/components/schemas/AntiIcpItem"},"type":"array","maxItems":50,"title":"Anti Icp"}},"type":"object","title":"IcpProfile","description":"The stored ICP shape (the ``icp_profiles.profile`` JSONB blob).\n\n``extra=\"ignore\"`` (not ``forbid``): this is a shape *read back* from JSONB, so\na lenient read is the defensive default — and it matches M-Prospect-a's\nvalidate-without-mutating / pass-through-unknown-keys philosophy. The fields are\nall optional so a partial derivation (or a from-scratch hand-authored ICP) is\nvalid."},"IcpProfile-Output":{"properties":{"unifying_characteristic":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Unifying Characteristic"},"target_subverticals":{"items":{"type":"string","maxLength":300,"minLength":1},"type":"array","maxItems":50,"title":"Target Subverticals"},"firmographic_bands":{"$ref":"#/components/schemas/FirmographicBands"},"buying_signal_vocab":{"items":{"$ref":"#/components/schemas/BuyingSignalVocabItem"},"type":"array","maxItems":50,"title":"Buying Signal Vocab"},"fit_rating_criteria":{"additionalProperties":{"type":"string","maxLength":4000},"propertyNames":{"$ref":"#/components/schemas/FitRating"},"type":"object","title":"Fit Rating Criteria"},"anti_icp":{"items":{"$ref":"#/components/schemas/AntiIcpItem"},"type":"array","maxItems":50,"title":"Anti Icp"}},"type":"object","title":"IcpProfile","description":"The stored ICP shape (the ``icp_profiles.profile`` JSONB blob).\n\n``extra=\"ignore\"`` (not ``forbid``): this is a shape *read back* from JSONB, so\na lenient read is the defensive default — and it matches M-Prospect-a's\nvalidate-without-mutating / pass-through-unknown-keys philosophy. The fields are\nall optional so a partial derivation (or a from-scratch hand-authored ICP) is\nvalid."},"IcpProfileResponse":{"properties":{"profile":{"anyOf":[{"$ref":"#/components/schemas/IcpProfile-Output"},{"type":"null"}]},"source":{"anyOf":[{"type":"string","enum":["derived","derived_coldstart","edited"]},{"type":"null"}],"title":"Source"},"derived_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Derived At"},"edited_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Edited At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"}},"type":"object","title":"IcpProfileResponse","description":"``GET`` / ``PATCH /tenants/me/icp`` — the profile + its provenance.\n\nA never-derived, never-edited tenant gets an empty shell (all ``None``) at 200,\nso the Settings UI shows an empty state with a \"Derive from won deals\" button\nrather than a 404."},"ImportJobDetail":{"properties":{"import_job_id":{"type":"string","format":"uuid","title":"Import Job Id"},"status":{"type":"string","title":"Status"},"original_filename":{"type":"string","title":"Original Filename"},"entity_type":{"type":"string","title":"Entity Type"},"file_format":{"type":"string","title":"File Format"},"total_rows":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Rows"},"imported_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Imported Count"},"duplicate_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duplicate Count"},"error_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Count"},"has_error_report":{"type":"boolean","title":"Has Error Report"},"failure_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failure Reason"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"}},"type":"object","required":["import_job_id","status","original_filename","entity_type","file_format","has_error_report","created_at"],"title":"ImportJobDetail"},"ImportJobListItem":{"properties":{"import_job_id":{"type":"string","format":"uuid","title":"Import Job Id"},"status":{"type":"string","title":"Status"},"original_filename":{"type":"string","title":"Original Filename"},"entity_type":{"type":"string","title":"Entity Type"},"total_rows":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Rows"},"imported_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Imported Count"},"duplicate_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duplicate Count"},"error_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Count"},"has_error_report":{"type":"boolean","title":"Has Error Report"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"}},"type":"object","required":["import_job_id","status","original_filename","entity_type","has_error_report","created_at"],"title":"ImportJobListItem"},"InsightActionType":{"type":"string","enum":["act","dismiss","snooze"],"title":"InsightActionType","description":"The three triage actions on an insight (DA1 lifecycle).\n\n`act`/`dismiss` are terminal — they move the row out of the live status set,\nwhich *frees the cooldown claim* (the partial-unique `uq_insights_tenant_signal_live`\nno longer covers the row), so a future re-detection of the same signal can\nsurface afresh. `snooze` keeps the row live (still suppressing duplicates)\nand re-stamps `surfaced_at` to the wake time so it sorts fresh on return."},"InsightFeedPage":{"properties":{"items":{"items":{"$ref":"#/components/schemas/InsightRead"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["items","next_cursor"],"title":"InsightFeedPage","description":"Keyset-paginated page for the backward \"Watch Limena work\" feed\n(M-Legible-b, GAP-041). Distinct from the flat score-ranked `InsightList`\n(the forward suggestion feed): the autonomy feed is reverse-chronological +\ninfinite, so it carries an opaque `next_cursor` — pass it back verbatim as\n`?cursor=` to fetch the next (older) page; `None` once exhausted."},"InsightList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/InsightRead"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"InsightList","description":"Flat `.items` list — mirrors the Notification / SavedTableView wrapper so\nthe frontend hook reads `.items` directly. The list is pre-ranked by score."},"InsightPatch":{"properties":{"action":{"$ref":"#/components/schemas/InsightActionType"},"snooze_minutes":{"anyOf":[{"type":"integer","maximum":43200.0,"minimum":1.0},{"type":"null"}],"title":"Snooze Minutes"}},"type":"object","required":["action"],"title":"InsightPatch","description":"Triage body. `snooze_minutes` is required iff `action == snooze` and\nforbidden otherwise — validated here so the route + service stay thin."},"InsightRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"kind":{"type":"string","title":"Kind"},"title":{"type":"string","title":"Title"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body"},"facts":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Facts"},"target_href":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Href"},"status":{"type":"string","title":"Status"},"score":{"type":"integer","title":"Score"},"surfaced_at":{"type":"string","format":"date-time","title":"Surfaced At"},"acted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Acted At"},"snoozed_until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Snoozed Until"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"category":{"type":"string","title":"Category"},"reversible":{"type":"boolean","title":"Reversible"},"reversed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Reversed At"}},"type":"object","required":["id","kind","title","body","facts","target_href","status","score","surfaced_at","acted_at","snoozed_until","created_at","category","reversible","reversed_at"],"title":"InsightRead"},"InsightUndo":{"properties":{"confirm":{"type":"boolean","title":"Confirm","default":false}},"type":"object","title":"InsightUndo","description":"Undo body (M-Legible-a, GAP-041). `confirm` gates the compliance-sensitive\n`gated` reversal (OQ3 — re-subscribing a contact): the server rejects an\nunconfirmed gated undo with `details.confirm_required` so the UI can pop a\nConfirmDialog and retry. Ignored for the cleanly-reversible kinds."},"InvitationPublic":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"email":{"type":"string","title":"Email"},"full_name":{"type":"string","title":"Full Name"},"role":{"type":"string","title":"Role"},"invited_by_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invited By Name"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"is_expired":{"type":"boolean","title":"Is Expired"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","email","full_name","role","expires_at","is_expired","created_at"],"title":"InvitationPublic","description":"M-TeamAdmin-a (GAP-070) — a pending user invitation for the admin\nmanagement surface (list / resend / revoke). ``invited_by_name`` is the\ninviter's display name (via the eager-loaded ``inviter`` relationship);\n``is_expired`` is derived from ``expires_at`` at read time so the UI can\ndistinguish an expired-but-unaccepted invite from a live one."},"InviteAcceptRequest":{"properties":{"token":{"type":"string","title":"Token"},"password":{"type":"string","minLength":12,"title":"Password"}},"type":"object","required":["token","password"],"title":"InviteAcceptRequest"},"ItemFilter":{"properties":{"dispositions":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Dispositions"}},"type":"object","title":"ItemFilter","description":"Server-side filter for selecting items by attribute.\n\nMirrors the dispositions filter used by GET /lists/{id}/items so the\n\"select all matching\" client UX maps to the same eligibility set."},"JobProgress":{"properties":{"phases":{"items":{"$ref":"#/components/schemas/ProgressPhase"},"type":"array","title":"Phases"},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail"},"current":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Current"},"total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total"}},"type":"object","required":["phases"],"title":"JobProgress","description":"A snapshot the frontend renders as a phase-chip row + an optional count.\n\n``current``/``total`` drive a determinate \"N / M\" within the active phase (e.g.\nsources read); ``detail`` is a small free note (e.g. the hostname being read).\nBoth are optional — a phase with no live count renders as a pulsing chip."},"ListAiSuggestResponse":{"properties":{"columns":{"items":{"$ref":"#/components/schemas/ColumnSuggestion"},"type":"array","title":"Columns"},"ai_available":{"type":"boolean","title":"Ai Available"}},"type":"object","required":["ai_available"],"title":"ListAiSuggestResponse","description":"Opt-in AI-assisted column suggestions for the Lists upload (M-Ingest-e).\n\n`columns` is the deterministic baseline with the tenant LLM filling ONLY the\npreviously-unmapped columns; `ai_available` is False when the LLM degraded\n(the columns are then deterministic-only — the modal still works). Mirrors the\nwizard's `AiSuggestResponse`, minus the import-job id (the Lists door is\nstateless — the file is re-parsed per call, never persisted at preview time)."},"ListPreviewResponse":{"properties":{"header_row":{"type":"integer","title":"Header Row"},"headers":{"items":{"type":"string"},"type":"array","title":"Headers"},"sample_rows":{"items":{"additionalProperties":{"type":"string"},"type":"object"},"type":"array","title":"Sample Rows"},"total_rows":{"type":"integer","title":"Total Rows"},"email_column_detected":{"type":"boolean","title":"Email Column Detected"},"columns":{"items":{"$ref":"#/components/schemas/ColumnSuggestion"},"type":"array","title":"Columns"}},"type":"object","required":["header_row","headers","sample_rows","total_rows","email_column_detected"],"title":"ListPreviewResponse","description":"Server-side preview of an uploaded list file (M-Ingest-c, GAP-164).\n\nDrives the upload modal's Preview step: the detected (or overridden) header\nrow + sample rows keyed by the real headers, so the user can see + correct\nwhich row is the header before processing. ``email_column_detected`` tells the\nmodal whether to prompt for a manual email-column pick."},"LoginRecoveryCompleteRequest":{"properties":{"partial_token":{"type":"string","title":"Partial Token"},"code":{"type":"string","maxLength":12,"minLength":10,"title":"Code","description":"Recovery code"},"trust_device":{"type":"boolean","title":"Trust Device","default":false}},"type":"object","required":["partial_token","code"],"title":"LoginRecoveryCompleteRequest"},"LoginRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"LoginRequest"},"LoginTotpCompleteRequest":{"properties":{"partial_token":{"type":"string","title":"Partial Token"},"code":{"type":"string","maxLength":10,"minLength":6,"title":"Code","description":"TOTP digits"},"trust_device":{"type":"boolean","title":"Trust Device","default":false}},"type":"object","required":["partial_token","code"],"title":"LoginTotpCompleteRequest"},"MailboxScanCandidateOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"email":{"type":"string","title":"Email"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"sent_count":{"type":"integer","title":"Sent Count"},"received_count":{"type":"integer","title":"Received Count"},"first_seen":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"First Seen"},"last_seen":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Seen"},"score":{"type":"number","title":"Score"},"disposition":{"type":"string","title":"Disposition"},"existing_contact_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Existing Contact Id"},"matched_company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Matched Company Id"},"matched_company_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Matched Company Name"},"included":{"type":"boolean","title":"Included"}},"type":"object","required":["id","email","sent_count","received_count","score","disposition","included"],"title":"MailboxScanCandidateOut","description":"One staged correspondent in the review list — aggregated, scored, deduped,\ncompany-matched. ``included`` is the DA7 pre-check the FE defaults the checkbox\nto. ``matched_company_name`` is the joined name of ``matched_company_id``\n(active companies only) so the review row can show \"→ Acme\"; it is resolved at\nread time and not stored on the candidate row."},"MailboxScanJobOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"email_account_id":{"type":"string","format":"uuid","title":"Email Account Id"},"provider":{"type":"string","title":"Provider"},"status":{"type":"string","title":"Status"},"lookback_start":{"type":"string","format":"date-time","title":"Lookback Start"},"messages_scanned":{"type":"integer","title":"Messages Scanned"},"correspondents_found":{"type":"integer","title":"Correspondents Found"},"failure_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failure Reason"},"requested_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Requested By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"}},"type":"object","required":["id","email_account_id","provider","status","lookback_start","messages_scanned","correspondents_found","created_at"],"title":"MailboxScanJobOut","description":"A scan job — the create response + the status-poll payload (clone of the\n``useImportJob`` shape, DA12)."},"McpToolSummary":{"properties":{"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"required_scope":{"type":"string","title":"Required Scope"},"mutates":{"type":"boolean","title":"Mutates"}},"type":"object","required":["name","description","required_scope","mutates"],"title":"McpToolSummary"},"McpToolsResponse":{"properties":{"tools":{"items":{"$ref":"#/components/schemas/McpToolSummary"},"type":"array","title":"Tools"}},"type":"object","required":["tools"],"title":"McpToolsResponse"},"Measure":{"properties":{"aggregation":{"$ref":"#/components/schemas/Aggregation"},"field":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Field"}},"additionalProperties":false,"type":"object","required":["aggregation"],"title":"Measure","description":"An aggregation function applied to a field (or to row count).\n\n`aggregation=COUNT` ignores `field` — counts the rows in the result\nset. Every other aggregation requires `field`; the validator checks\nthe field's `aggregatable_as` tuple at chart-execution time.\n\nRates are modelled as DERIVED FIELDS in the registry, not as a\nseparate aggregation function — users select `aggregation=AVG` on a\nderived rate field (e.g. `outreach.reply_rate`) and the compiler\nsubstitutes the CASE WHEN expression. Keeps the grammar bounded."},"MeetingAttendee":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Email"}},"type":"object","required":["name"],"title":"MeetingAttendee","description":"A free-text attendee on a logged meeting. Not Contact-linked in v1 (D6)."},"MeetingCreate":{"properties":{"entity_type":{"type":"string","enum":["contact","company","deal"],"title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"},"title":{"type":"string","maxLength":255,"minLength":1,"title":"Title"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"duration_minutes":{"anyOf":[{"type":"integer","exclusiveMinimum":0.0},{"type":"null"}],"title":"Duration Minutes"},"location":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Location"},"attendees":{"items":{"$ref":"#/components/schemas/MeetingAttendee"},"type":"array","title":"Attendees"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["entity_type","entity_id","title","occurred_at"],"title":"MeetingCreate"},"MeetingList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/MeetingResponse"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["items","total"],"title":"MeetingList"},"MeetingResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"entity_type":{"type":"string","title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"},"title":{"type":"string","title":"Title"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"duration_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Minutes"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"attendees":{"items":{"$ref":"#/components/schemas/MeetingAttendee"},"type":"array","title":"Attendees"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"logged_by_user_id":{"type":"string","format":"uuid","title":"Logged By User Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","entity_type","entity_id","title","occurred_at","duration_minutes","location","attendees","notes","logged_by_user_id","created_at","updated_at"],"title":"MeetingResponse"},"MeetingUpdate":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Title"},"occurred_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Occurred At"},"duration_minutes":{"anyOf":[{"type":"integer","exclusiveMinimum":0.0},{"type":"null"}],"title":"Duration Minutes"},"location":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Location"},"attendees":{"anyOf":[{"items":{"$ref":"#/components/schemas/MeetingAttendee"},"type":"array"},{"type":"null"}],"title":"Attendees"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","title":"MeetingUpdate","description":"Partial update — every field optional. ``entity_type``/``entity_id`` are immutable."},"MemoryFactResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"content":{"type":"string","title":"Content"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"},"source":{"type":"string","enum":["ai","human"],"title":"Source"},"source_conversation_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Source Conversation Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","content","source","created_at","updated_at"],"title":"MemoryFactResponse","description":"One learned fact as returned to its owner (``/profile``)."},"MemoryFactUpdate":{"properties":{"content":{"type":"string","maxLength":2000,"minLength":1,"title":"Content"},"category":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Category"}},"additionalProperties":false,"type":"object","required":["content"],"title":"MemoryFactUpdate","description":"``PATCH /users/me/memory-facts/{id}`` — the owner edits a fact's content/category.\n\n``extra='forbid'`` — a strict input boundary. Editing flips ``source`` to ``'human'`` (the\nowner has taken authorship); the AI never re-writes a human-edited fact silently."},"MemorySettingsResponse":{"properties":{"memory_enabled":{"type":"boolean","title":"Memory Enabled"}},"type":"object","required":["memory_enabled"],"title":"MemorySettingsResponse","description":"The owner's memory master switch (``users.memory_enabled``) — surfaced beside the\nfact list on ``/profile`` so the owner can turn auto-learning off entirely."},"MemorySettingsUpdate":{"properties":{"memory_enabled":{"type":"boolean","title":"Memory Enabled"}},"additionalProperties":false,"type":"object","required":["memory_enabled"],"title":"MemorySettingsUpdate","description":"``PATCH /users/me/memory-settings`` — flip the auto-learning master switch."},"MessageResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"role":{"type":"string","title":"Role"},"content":{"type":"string","title":"Content"},"tool_calls":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Tool Calls"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","role","content","created_at"],"title":"MessageResponse"},"MigrationExecuteResponse":{"properties":{"migration_job_id":{"type":"string","format":"uuid","title":"Migration Job Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["migration_job_id","status"],"title":"MigrationExecuteResponse"},"MigrationFileColumns":{"properties":{"file_id":{"type":"string","format":"uuid","title":"File Id"},"entity_type":{"type":"string","title":"Entity Type"},"detected_vendor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detected Vendor"},"columns":{"items":{"$ref":"#/components/schemas/ColumnSuggestion"},"type":"array","title":"Columns"},"relationship_columns":{"items":{"type":"string"},"type":"array","title":"Relationship Columns"},"sheet_names":{"items":{"type":"string"},"type":"array","title":"Sheet Names"},"selected_sheet":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Selected Sheet"},"header_row":{"type":"integer","title":"Header Row","default":0}},"type":"object","required":["file_id","entity_type","columns","relationship_columns"],"title":"MigrationFileColumns","description":"The deterministic mapping baseline for ONE file (M-Migrate-c) — vendor\npreset overlaid on the rapidfuzz floor, NO LLM (mirrors the single-file\nupload response's ``columns``; the wizard's mapping panel renders this before\nany human-triggered AI assist). ``relationship_columns`` are the FK headers\nthe executor resolves through the in-job remap — the wizard shows them\nread-only, never as mappable columns."},"MigrationFileDistinctValues":{"properties":{"file_id":{"type":"string","format":"uuid","title":"File Id"},"column":{"type":"string","title":"Column"},"values":{"items":{"type":"string"},"type":"array","title":"Values"},"truncated":{"type":"boolean","title":"Truncated"}},"type":"object","required":["file_id","column","values","truncated"],"title":"MigrationFileDistinctValues","description":"Distinct raw values of ONE source column in a migration file (M-Migrate-c)\n— feeds the wizard's stage-mapping affordance with the deals file's actual\nstage labels. First-appearance order; ``truncated`` flags that the distinct\ncount exceeded the cap (or a pathological overlong value was skipped)."},"MigrationFileItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"original_filename":{"type":"string","title":"Original Filename"},"file_format":{"type":"string","title":"File Format"},"entity_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Type"},"classification_confidence":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Classification Confidence"},"status":{"type":"string","title":"Status"},"total_rows":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Rows"},"imported_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Imported Count"},"duplicate_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duplicate Count"},"error_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Count"},"has_error_report":{"type":"boolean","title":"Has Error Report"},"field_mappings":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":"object","title":"Field Mappings"},"value_mappings":{"additionalProperties":{"additionalProperties":{"type":"string"},"type":"object"},"type":"object","title":"Value Mappings"}},"type":"object","required":["id","original_filename","file_format","status","has_error_report"],"title":"MigrationFileItem","description":"One classified file within a migration job."},"MigrationFileMappingsRequest":{"properties":{"entity_type":{"type":"string","title":"Entity Type"},"field_mappings":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":"object","title":"Field Mappings"},"value_mappings":{"additionalProperties":{"additionalProperties":{"type":"string"},"type":"object"},"type":"object","title":"Value Mappings"},"sheet":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sheet"},"header_row":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Header Row"}},"type":"object","required":["entity_type","field_mappings"],"title":"MigrationFileMappingsRequest","description":"Confirm a file's entity + per-file field mapping (the human-override gate).\n\n``entity_type`` may correct the classifier's guess (including placing an\n``entity_type=None`` file); ``field_mappings`` is the source-column → canonical-\nfield map for the file's data columns (the FK/relationship columns are resolved\nby the executor, not mapped here); ``value_mappings`` translates source values\nonto the tenant's vocabulary, ``{canonical_field: {source_value: tenant_value}}``\n— v1 accepts ``stage`` on deals files only (M-Migrate-c, the stage-mapping\naffordance)."},"MigrationFilePreview":{"properties":{"file_id":{"type":"string","format":"uuid","title":"File Id"},"entity_type":{"type":"string","title":"Entity Type"},"status":{"type":"string","title":"Status"},"total_rows":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Rows"},"preview_rows":{"items":{"$ref":"#/components/schemas/PreviewRow"},"type":"array","title":"Preview Rows"},"error_count_in_preview":{"type":"integer","title":"Error Count In Preview"}},"type":"object","required":["file_id","entity_type","status","preview_rows","error_count_in_preview"],"title":"MigrationFilePreview","description":"A per-file 10-row validation preview returned on confirm (b4's preview floor)."},"MigrationFileSuggestResponse":{"properties":{"file_id":{"type":"string","format":"uuid","title":"File Id"},"entity_type":{"type":"string","title":"Entity Type"},"columns":{"items":{"$ref":"#/components/schemas/ColumnSuggestion"},"type":"array","title":"Columns"},"ai_available":{"type":"boolean","title":"Ai Available"}},"type":"object","required":["file_id","entity_type","columns","ai_available"],"title":"MigrationFileSuggestResponse","description":"AI-assisted column-mapping suggestions for ONE file in a migration job\n(M-Migrate-b). Mirrors the single-file ``AiSuggestResponse`` but keyed on the\nmigration file. ``columns`` carries the full deterministic + preset baseline\nwith the AI filling the previously-unmapped gaps; ``entity_type`` is the entity\nthose suggestions target (the file's classified type, or the caller's override\nfor an unclassified file); ``ai_available`` is False when the LLM degraded\n(columns are then deterministic-only — the wizard still works)."},"MigrationJobDetail":{"properties":{"migration_job_id":{"type":"string","format":"uuid","title":"Migration Job Id"},"status":{"type":"string","title":"Status"},"vendor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vendor"},"archive_filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Archive Filename"},"total_files":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Files"},"imported_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Imported Count"},"duplicate_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duplicate Count"},"error_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Count"},"unresolved_fk_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Unresolved Fk Count"},"failure_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failure Reason"},"files":{"items":{"$ref":"#/components/schemas/MigrationFileItem"},"type":"array","title":"Files"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"}},"type":"object","required":["migration_job_id","status","files","created_at"],"title":"MigrationJobDetail","description":"Full migration-job view with its per-file children. ``from_job`` requires\nthe ``files`` relationship to be eager-loaded (the models are ``lazy=\"raise\"``)."},"MigrationJobListItem":{"properties":{"migration_job_id":{"type":"string","format":"uuid","title":"Migration Job Id"},"status":{"type":"string","title":"Status"},"vendor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vendor"},"archive_filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Archive Filename"},"total_files":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Files"},"imported_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Imported Count"},"error_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"}},"type":"object","required":["migration_job_id","status","created_at"],"title":"MigrationJobListItem","description":"Compact migration-job row for the list view (no per-file children)."},"MonthlyUsageRowOut":{"properties":{"month":{"type":"string","title":"Month"},"input_tokens":{"type":"integer","title":"Input Tokens"},"output_tokens":{"type":"integer","title":"Output Tokens"},"total_tokens":{"type":"integer","title":"Total Tokens"},"call_count":{"type":"integer","title":"Call Count"}},"type":"object","required":["month","input_tokens","output_tokens","total_tokens","call_count"],"title":"MonthlyUsageRowOut"},"NoteCreate":{"properties":{"body":{"type":"string","maxLength":50000,"minLength":1,"title":"Body"}},"type":"object","required":["body"],"title":"NoteCreate"},"NoteResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"author_id":{"type":"string","format":"uuid","title":"Author Id"},"entity_type":{"type":"string","title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"},"body":{"type":"string","title":"Body"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","author_id","entity_type","entity_id","body","created_at","updated_at"],"title":"NoteResponse"},"NoteUpdate":{"properties":{"body":{"type":"string","maxLength":50000,"minLength":1,"title":"Body"}},"type":"object","required":["body"],"title":"NoteUpdate"},"NotificationList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/NotificationRead"},"type":"array","title":"Items"},"unread_count":{"type":"integer","title":"Unread Count"}},"type":"object","required":["items","unread_count"],"title":"NotificationList","description":"Flat list + unread tally — mirrors the `.items` wrapper shape used by\nSavedTableView / M33b/c so the frontend hook reads `.items` directly."},"NotificationRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"type":{"type":"string","title":"Type"},"title":{"type":"string","title":"Title"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body"},"target_href":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Href"},"read_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Read At"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","type","title","body","target_href","read_at","created_at"],"title":"NotificationRead"},"NumberFormat":{"type":"string","enum":["integer","decimal","currency","percent"],"title":"NumberFormat","description":"Renderer-side number-format hints."},"OllamaModelEntry":{"properties":{"name":{"type":"string","title":"Name"},"size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Size Bytes"},"modified_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modified At"}},"type":"object","required":["name"],"title":"OllamaModelEntry","description":"One row from Ollama's `GET /api/tags` (`ollama list`).\n\nFrontend renders `name` as the primary label and `size_bytes` as a\nsecondary \"21 GB\" tag. `modified_at` is exposed for future sorting\nsurfaces but the frontend v1 keeps server order."},"OnboardingItems":{"properties":{"workspace_ready":{"type":"boolean","title":"Workspace Ready"},"email_connected":{"type":"boolean","title":"Email Connected"},"contacts_added":{"type":"boolean","title":"Contacts Added"},"team_invited":{"type":"boolean","title":"Team Invited"},"ai_configured":{"type":"boolean","title":"Ai Configured"}},"type":"object","required":["workspace_ready","email_connected","contacts_added","team_invited","ai_configured"],"title":"OnboardingItems","description":"The getting-started checklist items, each derived from live state."},"OnboardingStatus":{"properties":{"welcome_complete":{"type":"boolean","title":"Welcome Complete"},"checklist_dismissed":{"type":"boolean","title":"Checklist Dismissed"},"items":{"$ref":"#/components/schemas/OnboardingItems"}},"type":"object","required":["welcome_complete","checklist_dismissed","items"],"title":"OnboardingStatus","description":"First-run activation status for the current tenant.\n\n``welcome_complete`` is the owner-first-login gate (``tenant.onboarding_\ncomplete``); ``checklist_dismissed`` rides ``tenant.settings.onboarding``.\nBoth ``items`` and the flags are recomputed on every read — there is no\nstored progress (DO3)."},"OneOffSendRequest":{"properties":{"account_id":{"type":"string","format":"uuid","title":"Account Id"},"subject":{"type":"string","maxLength":500,"minLength":1,"title":"Subject"},"body":{"type":"string","minLength":1,"title":"Body"},"from_send_as":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From Send As","description":"Optional verified send-as alias. Same shape as the equivalent field on `CampaignSendStart`."}},"type":"object","required":["account_id","subject","body"],"title":"OneOffSendRequest","description":"Body for `POST /contacts/{contact_id}/send-one-off`.\n\nSubject + body land verbatim — no template rendering, no AI\npersonalization. `account_id` selects which connected Gmail\naccount sends."},"OneOffSendResponse":{"properties":{"campaign_id":{"type":"string","format":"uuid","title":"Campaign Id"},"outreach_record_id":{"type":"string","format":"uuid","title":"Outreach Record Id"},"estimated_completion_at":{"type":"string","format":"date-time","title":"Estimated Completion At"},"send_status":{"type":"string","title":"Send Status","default":"sending"}},"type":"object","required":["campaign_id","outreach_record_id","estimated_completion_at"],"title":"OneOffSendResponse","description":"Response from a successful one-off send dispatch. `campaign_id` is\nthe implicit one-off Campaign row (hidden from the /campaigns index\nby UX-060's 3a list filter); `outreach_record_id` is the pre-staged\nOutreachRecord the send task transitions in place.\n\n`estimated_completion_at` lets the frontend warn the user when the\nsend schedules into a future quota window (today's quota exhausted)."},"OutlookAuthorizeResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"}},"type":"object","required":["auth_url"],"title":"OutlookAuthorizeResponse","description":"Returned by GET /api/v1/integrations/outlook/authorize. The frontend\nnavigates the user to `auth_url`; Microsoft identity redirects back to\nthe registered redirect URI with `code` + `state` query params.\nMirrors GmailAuthorizeResponse — kept as a distinct type so the OpenAPI\nschema documents which provider each route serves."},"OutlookCallbackPayload":{"properties":{"code":{"type":"string","minLength":1,"title":"Code"},"state":{"type":"string","minLength":1,"title":"State"}},"type":"object","required":["code","state"],"title":"OutlookCallbackPayload","description":"POSTed by the frontend `/auth/outlook/callback` page after Microsoft's\nredirect. Identical shape to GmailCallbackPayload — both providers use\nOAuth2 auth-code flow with code + state on the query string. Kept\ndistinct for symmetry + clarity in route signatures."},"OutlookCallbackResponse":{"properties":{"account":{"$ref":"#/components/schemas/EmailAccount"}},"type":"object","required":["account"],"title":"OutlookCallbackResponse","description":"Result of a successful Microsoft OAuth exchange. Surfaces the\nconnected account so the frontend can update its UI without an\nextra round-trip."},"OutreachCreate":{"properties":{"contact_id":{"type":"string","format":"uuid","title":"Contact Id"},"channel":{"type":"string","title":"Channel"},"direction":{"type":"string","title":"Direction","default":"outbound"},"subject":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Subject"},"body_summary":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Body Summary"},"sent_at":{"type":"string","format":"date-time","title":"Sent At"},"status":{"type":"string","title":"Status","default":"sent"},"response_received":{"type":"boolean","title":"Response Received","default":false},"response_sentiment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Response Sentiment"},"campaign_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Campaign Id"},"campaign_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Campaign Name"}},"type":"object","required":["contact_id","channel","sent_at"],"title":"OutreachCreate"},"OutreachDraftRequest":{"properties":{"contact_id":{"type":"string","format":"uuid","title":"Contact Id"},"campaign_brief":{"type":"string","maxLength":1000,"title":"Campaign Brief"},"tone":{"type":"string","enum":["formal","casual","direct"],"title":"Tone","default":"direct"},"max_length":{"type":"integer","maximum":500.0,"minimum":50.0,"title":"Max Length","default":200}},"type":"object","required":["contact_id","campaign_brief"],"title":"OutreachDraftRequest"},"OutreachDraftResponse":{"properties":{"subject":{"type":"string","title":"Subject"},"body":{"type":"string","title":"Body"},"personalisation_signals":{"items":{"type":"string"},"type":"array","title":"Personalisation Signals"},"model_used":{"type":"string","title":"Model Used"},"tokens_used":{"type":"integer","title":"Tokens Used"}},"type":"object","required":["subject","body","personalisation_signals","model_used","tokens_used"],"title":"OutreachDraftResponse"},"OutreachRecordDetail":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"contact":{"$ref":"#/components/schemas/ContactSummaryForOutreach"},"user":{"anyOf":[{"$ref":"#/components/schemas/UserSummaryForOutreach"},{"type":"null"}]},"channel":{"type":"string","title":"Channel"},"direction":{"type":"string","title":"Direction"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"},"body_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Summary"},"sent_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Sent At"},"status":{"type":"string","title":"Status"},"response_received":{"type":"boolean","title":"Response Received"},"response_sentiment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Response Sentiment"},"response_received_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Response Received At"},"response_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Response Summary"},"response_body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Response Body"},"body_full":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Full"},"campaign_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Campaign Id"},"campaign_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Name"},"external_thread_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Thread Id"},"replied_to_record_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Replied To Record Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"thread":{"anyOf":[{"$ref":"#/components/schemas/OutreachThreadView"},{"type":"null"}]}},"type":"object","required":["id","contact","channel","direction","status","response_received","created_at"],"title":"OutreachRecordDetail"},"OutreachRecordSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"channel":{"type":"string","title":"Channel"},"direction":{"type":"string","title":"Direction"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"},"sent_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Sent At"},"status":{"type":"string","title":"Status"},"response_received":{"type":"boolean","title":"Response Received"},"response_sentiment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Response Sentiment"},"response_received_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Response Received At"},"response_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Response Summary"},"campaign_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Campaign Id"},"campaign_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Name"}},"type":"object","required":["id","channel","direction","status","response_received"],"title":"OutreachRecordSummary"},"OutreachSummary":{"properties":{"campaign_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Campaign Id"},"campaign_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Campaign Name"},"total_sent":{"type":"integer","title":"Total Sent","default":0},"total_delivered":{"type":"integer","title":"Total Delivered","default":0},"total_opened":{"type":"integer","title":"Total Opened","default":0},"total_replied":{"type":"integer","title":"Total Replied","default":0},"total_bounced":{"type":"integer","title":"Total Bounced","default":0},"reply_rate":{"type":"number","title":"Reply Rate","default":0.0},"open_rate":{"type":"number","title":"Open Rate","default":0.0},"by_channel":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Channel","default":{}},"by_period":{"items":{"$ref":"#/components/schemas/PeriodSummary"},"type":"array","title":"By Period","default":[]},"unique_contacts_count":{"type":"integer","title":"Unique Contacts Count","default":0},"sentiment_breakdown":{"additionalProperties":{"type":"integer"},"type":"object","title":"Sentiment Breakdown","default":{}}},"type":"object","title":"OutreachSummary"},"OutreachThreadView":{"properties":{"sent":{"$ref":"#/components/schemas/OutreachRecordDetail"},"replies":{"items":{"$ref":"#/components/schemas/OutreachRecordDetail"},"type":"array","title":"Replies","default":[]}},"type":"object","required":["sent"],"title":"OutreachThreadView","description":"M31b2.5 (UX-019) — paired sent + replies for the OutreachDetailPanel.\n\nPopulated by `GET /api/v1/outreach/{id}?include_thread=true`. `sent` is\nalways the outbound row regardless of which row id the caller queried;\n`replies` is the inbound reply children ordered by `sent_at` ascending\n(currently 0 or 1 elements — multi-message threads are schema-supported\nbut not yet produced by ingestion)."},"OutreachUpdate":{"properties":{"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"response_received":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Response Received"},"response_sentiment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Response Sentiment"}},"type":"object","title":"OutreachUpdate"},"PaginatedResponse_AuditLogEntry_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AuditLogEntry"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[AuditLogEntry]"},"PaginatedResponse_CampaignMemberContact_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/CampaignMemberContact"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[CampaignMemberContact]"},"PaginatedResponse_CampaignSummary_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/CampaignSummary"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[CampaignSummary]"},"PaginatedResponse_CompanyListItem_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/CompanyListItem"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[CompanyListItem]"},"PaginatedResponse_ContactListItem_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ContactListItem"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[ContactListItem]"},"PaginatedResponse_DealListItem_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/DealListItem"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[DealListItem]"},"PaginatedResponse_DraftListItem_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/DraftListItem"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[DraftListItem]"},"PaginatedResponse_EmailTemplateListItem_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/EmailTemplateListItem"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[EmailTemplateListItem]"},"PaginatedResponse_ImportJobListItem_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ImportJobListItem"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[ImportJobListItem]"},"PaginatedResponse_MigrationJobListItem_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/MigrationJobListItem"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[MigrationJobListItem]"},"PaginatedResponse_NoteResponse_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/NoteResponse"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[NoteResponse]"},"PaginatedResponse_OutreachRecordDetail_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/OutreachRecordDetail"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[OutreachRecordDetail]"},"PaginatedResponse_ProspectListItemDetail_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ProspectListItemDetail"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[ProspectListItemDetail]"},"PaginatedResponse_ProspectListSummary_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ProspectListSummary"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[ProspectListSummary]"},"PaginatedResponse_SavedSegmentRead_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/SavedSegmentRead"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[SavedSegmentRead]"},"PaginatedResponse_TaskListItem_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/TaskListItem"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[TaskListItem]"},"PaginatedResponse_UserPublic_":{"properties":{"items":{"items":{"$ref":"#/components/schemas/UserPublic"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"},"has_prev":{"type":"boolean","title":"Has Prev"}},"type":"object","required":["items","total","page","page_size","has_next","has_prev"],"title":"PaginatedResponse[UserPublic]"},"PasswordChangeRequest":{"properties":{"current_password":{"type":"string","title":"Current Password"},"new_password":{"type":"string","minLength":12,"title":"New Password"}},"type":"object","required":["current_password","new_password"],"title":"PasswordChangeRequest"},"PeopleBulkApproveRequest":{"properties":{"batch_id":{"type":"string","format":"uuid","title":"Batch Id"}},"type":"object","required":["batch_id"],"title":"PeopleBulkApproveRequest","description":"Approve every pending person in a batch."},"PeopleBulkApproveResult":{"properties":{"approved":{"type":"integer","title":"Approved"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["approved","total"],"title":"PeopleBulkApproveResult","description":"Outcome of a bulk-approve over a batch's pending person rows."},"PeriodSummary":{"properties":{"period":{"type":"string","title":"Period"},"sent_count":{"type":"integer","title":"Sent Count"},"replied_count":{"type":"integer","title":"Replied Count"},"bounced_count":{"type":"integer","title":"Bounced Count"}},"type":"object","required":["period","sent_count","replied_count","bounced_count"],"title":"PeriodSummary"},"PipelineInsightsResponse":{"properties":{"summary":{"anyOf":[{"$ref":"#/components/schemas/PipelineSummaryOut"},{"type":"null"}],"description":"The advisory board summary, or null when the board is empty or a generation degraded."},"deals":{"items":{"$ref":"#/components/schemas/DealInsightOut"},"type":"array","title":"Deals","description":"Per-deal next-step chips — only deals that have a cached brief."}},"type":"object","title":"PipelineInsightsResponse","description":"Envelope: the (possibly absent) advisory summary + the per-deal next-step chips."},"PipelineStageCreate":{"properties":{"label":{"type":"string","maxLength":100,"minLength":1,"title":"Label"},"is_closed_won":{"type":"boolean","title":"Is Closed Won","default":false},"is_closed_lost":{"type":"boolean","title":"Is Closed Lost","default":false}},"type":"object","required":["label"],"title":"PipelineStageCreate"},"PipelineStageData":{"properties":{"stage":{"type":"string","title":"Stage"},"label":{"type":"string","title":"Label"},"deal_count":{"type":"integer","title":"Deal Count"},"total_value":{"type":"integer","title":"Total Value"},"deals":{"items":{"$ref":"#/components/schemas/DealCard"},"type":"array","title":"Deals"}},"type":"object","required":["stage","label","deal_count","total_value","deals"],"title":"PipelineStageData"},"PipelineStageReorder":{"properties":{"keys":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Keys"}},"type":"object","required":["keys"],"title":"PipelineStageReorder"},"PipelineStageUpdate":{"properties":{"label":{"type":"string","maxLength":100,"minLength":1,"title":"Label"}},"type":"object","required":["label"],"title":"PipelineStageUpdate"},"PipelineSummaryOut":{"properties":{"summary":{"type":"string","title":"Summary","description":"One-line \"where revenue is stuck\" read over the open board."},"model":{"type":"string","title":"Model","description":"The model that produced the summary."},"generated_at":{"type":"string","format":"date-time","title":"Generated At","description":"When this summary was synthesized."}},"type":"object","required":["summary","model","generated_at"],"title":"PipelineSummaryOut","description":"The advisory one-line synthesis over the open pipeline."},"PipelineView":{"properties":{"stages":{"items":{"$ref":"#/components/schemas/PipelineStageData"},"type":"array","title":"Stages"},"total_pipeline_value":{"type":"integer","title":"Total Pipeline Value"},"currency":{"type":"string","title":"Currency"},"closed":{"items":{"$ref":"#/components/schemas/ClosedStageRollup"},"type":"array","title":"Closed","default":[]}},"type":"object","required":["stages","total_pipeline_value","currency"],"title":"PipelineView"},"Plan":{"type":"string","enum":["starter","pro","ultra"],"title":"Plan","description":"Canonical plan slugs (LOCKED by owner sign-off 2026-06-30).\n\n``starter`` collides with the legacy ``Tenant.plan`` default value — the\nM-Tier-a grandfather migration reassigns every existing tenant to ``ultra`` so\nnobody is silently downgraded to Limited (strategy §4.5)."},"PlatformTotalsOut":{"properties":{"month":{"type":"string","title":"Month"},"active_tenants":{"type":"integer","title":"Active Tenants"},"total_input_tokens":{"type":"integer","title":"Total Input Tokens"},"total_output_tokens":{"type":"integer","title":"Total Output Tokens"},"total_tokens":{"type":"integer","title":"Total Tokens"},"estimated_cost_usd":{"type":"number","title":"Estimated Cost Usd"},"top_tenants":{"items":{"$ref":"#/components/schemas/TopTenantShareOut"},"type":"array","title":"Top Tenants"}},"type":"object","required":["month","active_tenants","total_input_tokens","total_output_tokens","total_tokens","estimated_cost_usd","top_tenants"],"title":"PlatformTotalsOut"},"PreviewResponse":{"properties":{"import_job_id":{"type":"string","format":"uuid","title":"Import Job Id"},"status":{"type":"string","title":"Status"},"total_rows":{"type":"integer","title":"Total Rows"},"preview_rows":{"items":{"$ref":"#/components/schemas/PreviewRow"},"type":"array","title":"Preview Rows"},"error_count_in_preview":{"type":"integer","title":"Error Count In Preview"}},"type":"object","required":["import_job_id","status","total_rows","preview_rows","error_count_in_preview"],"title":"PreviewResponse"},"PreviewRow":{"properties":{"row_number":{"type":"integer","title":"Row Number"},"data":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":"object","title":"Data"},"errors":{"items":{"$ref":"#/components/schemas/PreviewRowError"},"type":"array","title":"Errors"},"is_valid":{"type":"boolean","title":"Is Valid"}},"type":"object","required":["row_number","data","errors","is_valid"],"title":"PreviewRow"},"PreviewRowError":{"properties":{"field":{"type":"string","title":"Field"},"message":{"type":"string","title":"Message"}},"type":"object","required":["field","message"],"title":"PreviewRowError"},"ProductAnalyticsEventIn":{"properties":{"event_name":{"type":"string","maxLength":64,"minLength":1,"title":"Event Name"},"props":{"additionalProperties":{"type":"string"},"type":"object","title":"Props"}},"additionalProperties":false,"type":"object","required":["event_name"],"title":"ProductAnalyticsEventIn","description":"One client-originated product-analytics event."},"ProgressPhase":{"properties":{"key":{"type":"string","title":"Key"},"label":{"type":"string","title":"Label"},"status":{"type":"string","enum":["pending","active","done"],"title":"Status"}},"type":"object","required":["key","label","status"],"title":"ProgressPhase","description":"One step in a job's fixed phase sequence + where the run is relative to it.\n\n``done`` = already passed (renders as a check), ``active`` = in flight (a pulse),\n``pending`` = not yet reached (a dim chip)."},"PromotePeopleRequest":{"properties":{"batch_id":{"type":"string","format":"uuid","title":"Batch Id"},"person_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Person Ids","description":"Optional subset; omit to promote every approved person in the batch."}},"type":"object","required":["batch_id"],"title":"PromotePeopleRequest","description":"Promote the approved people in a batch to reachable CRM contacts."},"PromotePeopleResult":{"properties":{"promoted":{"type":"integer","title":"Promoted"},"contacts_created":{"type":"integer","title":"Contacts Created"},"contacts_linked":{"type":"integer","title":"Contacts Linked"},"not_found":{"type":"integer","title":"Not Found"},"skipped":{"type":"integer","title":"Skipped"},"contact_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Contact Ids"}},"type":"object","required":["promoted","contacts_created","contacts_linked","not_found","skipped","contact_ids"],"title":"PromotePeopleResult","description":"Outcome of a people promote.\n\n``promoted`` people became contacts (``contacts_created`` new +\n``contacts_linked`` to an existing active contact by email). ``not_found``\nrows stayed ``approved`` with the miss recorded (re-promote skips them);\n``skipped`` rows weren't eligible (wrong state / wrong batch / no usable\nlast name)."},"PromoteProspectsRequest":{"properties":{"batch_id":{"type":"string","format":"uuid","title":"Batch Id"},"candidate_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Candidate Ids","description":"Optional subset; omit to promote every approved candidate in the batch."}},"type":"object","required":["batch_id"],"title":"PromoteProspectsRequest","description":"Promote the approved candidates in a batch to CRM companies."},"PromoteProspectsResult":{"properties":{"promoted":{"type":"integer","title":"Promoted"},"companies_created":{"type":"integer","title":"Companies Created"},"companies_linked":{"type":"integer","title":"Companies Linked"},"skipped":{"type":"integer","title":"Skipped"},"company_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Company Ids"}},"type":"object","required":["promoted","companies_created","companies_linked","skipped","company_ids"],"title":"PromoteProspectsResult","description":"Outcome of a promote — how many candidates became new vs linked companies."},"PromoteRequest":{"properties":{"scope":{"type":"string","enum":["filter","items"],"title":"Scope"},"filter":{"anyOf":[{"$ref":"#/components/schemas/ItemFilter"},{"type":"null"}]},"item_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Item Ids"}},"type":"object","required":["scope"],"title":"PromoteRequest","description":"Request body for POST /lists/{id}/promote_to_contacts."},"PromoteResult":{"properties":{"created":{"type":"integer","title":"Created","default":0},"already_existed":{"type":"integer","title":"Already Existed","default":0},"skipped":{"type":"integer","title":"Skipped","default":0},"errors":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Errors","default":[]}},"type":"object","title":"PromoteResult","description":"Counts returned by the promote endpoint.\n\n`created` = new Contact rows. `already_existed` = (tenant_id, email) was\nalready a contact, no row written. `skipped` = item was eligible-by-id but\nexcluded server-side (non-safe disposition, missing email, etc.) — see\n`errors` for per-item reasons."},"ProspectCandidateCreate":{"properties":{"company_name":{"type":"string","maxLength":255,"minLength":1,"title":"Company Name"},"domain":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Domain"},"industry":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Industry"},"employee_count":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Employee Count"},"annual_revenue":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Annual Revenue"},"country":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Country"},"city":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"City"},"fit_rating":{"anyOf":[{"$ref":"#/components/schemas/FitRating"},{"type":"null"}]},"fit_rationale":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Fit Rationale"},"score":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Score"},"buying_signals":{"items":{"$ref":"#/components/schemas/BuyingSignal"},"type":"array","title":"Buying Signals"},"evidence":{"items":{"$ref":"#/components/schemas/Evidence"},"type":"array","title":"Evidence"},"source_channel":{"type":"string","maxLength":200,"minLength":1,"title":"Source Channel","default":"find_prospects"}},"type":"object","required":["company_name"],"title":"ProspectCandidateCreate","description":"One discovered candidate company in a stage batch.\n\nThe firmographic snapshot + the ICP-fit intel ``find_prospects`` produced.\n``buying_signals`` / ``evidence`` validate against the shared element shapes\nand are stored JSON-safe in the candidate's JSONB columns."},"ProspectCandidateList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ProspectCandidateRead"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"}},"type":"object","required":["items","total","page","page_size"],"title":"ProspectCandidateList","description":"Paginated candidate list for the review queue."},"ProspectCandidatePatch":{"properties":{"company_name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Company Name"},"domain":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Domain"},"industry":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Industry"},"employee_count":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Employee Count"},"annual_revenue":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Annual Revenue"},"country":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Country"},"city":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"City"},"fit_rating":{"anyOf":[{"$ref":"#/components/schemas/FitRating"},{"type":"null"}]},"fit_rationale":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Fit Rationale"},"score":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Score"},"buying_signals":{"anyOf":[{"items":{"$ref":"#/components/schemas/BuyingSignal"},"type":"array"},{"type":"null"}],"title":"Buying Signals"},"evidence":{"anyOf":[{"items":{"$ref":"#/components/schemas/Evidence"},"type":"array"},{"type":"null"}],"title":"Evidence"},"status":{"anyOf":[{"type":"string","enum":["pending_review","approved","rejected"]},{"type":"null"}],"title":"Status"}},"type":"object","title":"ProspectCandidatePatch","description":"Inline-edit + approve/reject for one reviewable candidate.\n\nAll fields optional — only those *present* in the request are applied\n(``exclude_unset``): a PATCH of ``{\"status\": \"approved\"}`` approves without\ntouching the intel, and ``{\"fit_rationale\": \"...\"}`` edits without changing\nstatus. ``status`` is limited to the reviewable transitions; promotion is a\nseparate write."},"ProspectCandidateRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"batch_id":{"type":"string","format":"uuid","title":"Batch Id"},"status":{"type":"string","title":"Status"},"company_name":{"type":"string","title":"Company Name"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"},"industry":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Industry"},"employee_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Employee Count"},"annual_revenue":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Annual Revenue"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"fit_rating":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fit Rating"},"fit_rationale":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fit Rationale"},"score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Score"},"buying_signals":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Buying Signals"},"evidence":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Evidence"},"source_channel":{"type":"string","title":"Source Channel"},"promoted_company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Promoted Company Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","batch_id","status","company_name","domain","industry","employee_count","annual_revenue","country","city","fit_rating","fit_rationale","score","buying_signals","evidence","source_channel","promoted_company_id","created_at","updated_at"],"title":"ProspectCandidateRead","description":"Read projection of a candidate row (the review surface + API responses)."},"ProspectListDetail":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"source":{"type":"string","title":"Source"},"status":{"type":"string","title":"Status"},"total_count":{"type":"integer","title":"Total Count"},"deduped_count":{"type":"integer","title":"Deduped Count"},"safe_to_email_count":{"type":"integer","title":"Safe To Email Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"processed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Processed At"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"},"upload_intent":{"anyOf":[{"type":"string","enum":["clean_only","promote_to_contacts","use_for_outreach"]},{"type":"null"}],"title":"Upload Intent"},"default_campaign_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Campaign Name"},"intent_action_status":{"anyOf":[{"type":"string","enum":["running","complete","failed"]},{"type":"null"}],"title":"Intent Action Status"},"enriched_count":{"type":"integer","title":"Enriched Count"},"created_by":{"type":"string","format":"uuid","title":"Created By"},"disposition_counts":{"additionalProperties":{"type":"integer"},"type":"object","title":"Disposition Counts","default":{}},"known_count":{"type":"integer","title":"Known Count","default":0},"field_mappings":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":"object","title":"Field Mappings","default":{}}},"type":"object","required":["id","name","source","status","total_count","deduped_count","safe_to_email_count","created_at","enriched_count","created_by"],"title":"ProspectListDetail"},"ProspectListItemDetail":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"list_id":{"type":"string","format":"uuid","title":"List Id"},"contact_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Contact Id"},"raw_data":{"additionalProperties":true,"type":"object","title":"Raw Data"},"enriched_data":{"additionalProperties":true,"type":"object","title":"Enriched Data"},"is_duplicate":{"type":"boolean","title":"Is Duplicate"},"is_in_sfdc":{"type":"boolean","title":"Is In Sfdc"},"is_previously_contacted":{"type":"boolean","title":"Is Previously Contacted"},"is_known_contact":{"type":"boolean","title":"Is Known Contact","default":false},"last_contact_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Contact Date"},"disposition":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Disposition"}},"type":"object","required":["id","list_id","raw_data","enriched_data","is_duplicate","is_in_sfdc","is_previously_contacted"],"title":"ProspectListItemDetail"},"ProspectListSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"source":{"type":"string","title":"Source"},"status":{"type":"string","title":"Status"},"total_count":{"type":"integer","title":"Total Count"},"deduped_count":{"type":"integer","title":"Deduped Count"},"safe_to_email_count":{"type":"integer","title":"Safe To Email Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"processed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Processed At"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"},"upload_intent":{"anyOf":[{"type":"string","enum":["clean_only","promote_to_contacts","use_for_outreach"]},{"type":"null"}],"title":"Upload Intent"},"default_campaign_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Campaign Name"},"intent_action_status":{"anyOf":[{"type":"string","enum":["running","complete","failed"]},{"type":"null"}],"title":"Intent Action Status"}},"type":"object","required":["id","name","source","status","total_count","deduped_count","safe_to_email_count","created_at"],"title":"ProspectListSummary"},"ProspectPersonCreate":{"properties":{"first_name":{"type":"string","maxLength":255,"minLength":1,"title":"First Name"},"last_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Last Name"},"title":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Title"},"company_candidate_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Candidate Id"},"company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"},"company_name":{"type":"string","maxLength":255,"minLength":1,"title":"Company Name"},"company_domain":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Company Domain"},"role_query":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Role Query"},"fit_rating":{"anyOf":[{"$ref":"#/components/schemas/FitRating"},{"type":"null"}]},"fit_rationale":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Fit Rationale"},"score":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Score"},"evidence":{"items":{"$ref":"#/components/schemas/Evidence"},"type":"array","title":"Evidence"},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Email"},"email_status":{"anyOf":[{"type":"string","enum":["verified","guessed","unverified"]},{"type":"null"}],"title":"Email Status"},"email_source":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Email Source"},"source_channel":{"type":"string","maxLength":200,"minLength":1,"title":"Source Channel","default":"find_people"}},"type":"object","required":["first_name","company_name"],"title":"ProspectPersonCreate","description":"One discovered person in a stage batch — the snapshot + role-fit intel\n``find_people`` produced. ``email`` is present only for a free web-evidence\ncapture (code-verified literal presence in the consulted sources)."},"ProspectPersonList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ProspectPersonRead"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"}},"type":"object","required":["items","total","page","page_size"],"title":"ProspectPersonList","description":"Paginated person-candidate list for the review queue."},"ProspectPersonPatch":{"properties":{"first_name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Last Name"},"title":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Title"},"fit_rating":{"anyOf":[{"$ref":"#/components/schemas/FitRating"},{"type":"null"}]},"fit_rationale":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Fit Rationale"},"score":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Score"},"evidence":{"anyOf":[{"items":{"$ref":"#/components/schemas/Evidence"},"type":"array"},{"type":"null"}],"title":"Evidence"},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Email"},"status":{"anyOf":[{"type":"string","enum":["pending_review","approved","rejected"]},{"type":"null"}],"title":"Status"}},"type":"object","title":"ProspectPersonPatch","description":"Inline-edit + approve/reject for one reviewable person candidate.\n\nAll fields optional — only those *present* apply (``exclude_unset``). Editing\n``email`` records a reviewer-supplied address (promote then skips the billed\nlookup); a name or email edit also clears a recorded ``not_found`` miss so the\nnext promote retries. ``status`` is limited to the reviewable transitions."},"ProspectPersonRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"batch_id":{"type":"string","format":"uuid","title":"Batch Id"},"status":{"type":"string","title":"Status"},"first_name":{"type":"string","title":"First Name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"company_candidate_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Candidate Id"},"company_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Company Id"},"company_name":{"type":"string","title":"Company Name"},"company_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Domain"},"role_query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role Query"},"fit_rating":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fit Rating"},"fit_rationale":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fit Rationale"},"score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Score"},"evidence":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Evidence"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"email_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email Status"},"email_confidence":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Email Confidence"},"email_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email Source"},"email_checked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Email Checked At"},"source_channel":{"type":"string","title":"Source Channel"},"promoted_contact_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Promoted Contact Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","batch_id","status","first_name","last_name","title","company_candidate_id","company_id","company_name","company_domain","role_query","fit_rating","fit_rationale","score","evidence","email","email_status","email_confidence","email_source","email_checked_at","source_channel","promoted_contact_id","created_at","updated_at"],"title":"ProspectPersonRead","description":"Read projection of a person-candidate row (the review surface + API)."},"PublicFormSubmit":{"properties":{"values":{"additionalProperties":{"type":"string","maxLength":5000},"type":"object","title":"Values"},"hp":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Hp"}},"additionalProperties":false,"type":"object","required":["values"],"title":"PublicFormSubmit","description":"A public submission: the submitted field values + an optional honeypot.\n\n`hp` is a hidden honeypot field (DA2). A non-empty value marks a bot — the\nendpoint silently accepts (200) without running the ingestion."},"PublicFormSubmitResult":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"redirect_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Redirect Url"}},"type":"object","title":"PublicFormSubmitResult"},"PublicFormView":{"properties":{"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"fields":{"items":{"$ref":"#/components/schemas/FormFieldSpec"},"type":"array","title":"Fields"}},"type":"object","required":["name","description","fields"],"title":"PublicFormView","description":"What the hosted form page renders — no tenant/owner/internal leakage."},"RecipientAddResult":{"properties":{"added":{"type":"integer","title":"Added"},"suppressed":{"$ref":"#/components/schemas/SuppressionBreakdown"},"already_in_other_source":{"type":"integer","title":"Already In Other Source"},"promoted":{"type":"integer","title":"Promoted","default":0}},"additionalProperties":false,"type":"object","required":["added","suppressed","already_in_other_source"],"title":"RecipientAddResult","description":"Returned by add_{list,segment,contacts}_to_campaign."},"RecipientRow":{"properties":{"contact_id":{"type":"string","format":"uuid","title":"Contact Id"},"name":{"type":"string","title":"Name"},"email":{"type":"string","title":"Email"},"is_suppressed":{"type":"boolean","title":"Is Suppressed"},"suppression_reason":{"anyOf":[{"type":"string","enum":["unsubscribed","email_invalid"]},{"type":"null"}],"title":"Suppression Reason"},"sources":{"items":{"$ref":"#/components/schemas/RecipientSourceRef"},"type":"array","title":"Sources"}},"additionalProperties":false,"type":"object","required":["contact_id","name","email","is_suppressed","suppression_reason","sources"],"title":"RecipientRow","description":"One row in the \"View all recipients\" expandable panel."},"RecipientSourceRef":{"properties":{"kind":{"type":"string","enum":["list","segment","manual"],"title":"Kind"},"ref_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Ref Id"},"ref_label":{"type":"string","title":"Ref Label"}},"additionalProperties":false,"type":"object","required":["kind","ref_id","ref_label"],"title":"RecipientSourceRef","description":"One source attribution on a recipient row. A multi-source contact\nhas more than one of these."},"RecipientsListResponse":{"properties":{"summary":{"$ref":"#/components/schemas/RecipientsSummary"},"items":{"items":{"$ref":"#/components/schemas/RecipientRow"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"}},"additionalProperties":false,"type":"object","required":["summary","items","total"],"title":"RecipientsListResponse","description":"Response shape for GET /campaigns/{id}/recipients."},"RecipientsSummary":{"properties":{"deliverable_count":{"type":"integer","title":"Deliverable Count"},"suppressed_breakdown":{"$ref":"#/components/schemas/SuppressionBreakdown"},"sources":{"items":{"$ref":"#/components/schemas/SourceSummary"},"type":"array","title":"Sources"},"total_unique_contacts":{"type":"integer","title":"Total Unique Contacts"}},"additionalProperties":false,"type":"object","required":["deliverable_count","suppressed_breakdown","sources","total_unique_contacts"],"title":"RecipientsSummary","description":"Returned by get_recipients_summary. Drives the Recipients-step\nheader line + the per-source chips."},"RecordBriefRead":{"properties":{"headline":{"type":"string","title":"Headline","description":"Who/what this record is, in one line."},"status":{"type":"string","title":"Status","description":"Where things stand right now."},"suggested_next_step":{"type":"string","title":"Suggested Next Step","description":"One suggested action (a CTA, never autonomous)."},"generated_at":{"type":"string","format":"date-time","title":"Generated At","description":"When this brief text was last (re)generated."},"stale":{"type":"boolean","title":"Stale","description":"True when the live record has changed since this brief was generated (a fresh one is regenerating in the background)."}},"type":"object","required":["headline","status","suggested_next_step","generated_at","stale"],"title":"RecordBriefRead","description":"The fixed three-part brief (DA3) plus its freshness flag."},"RecordBriefResponse":{"properties":{"brief":{"anyOf":[{"$ref":"#/components/schemas/RecordBriefRead"},{"type":"null"}],"description":"The cached brief, or null when none has been generated yet."},"generating":{"type":"boolean","title":"Generating","description":"True when a (re)generation was enqueued for this read — the brief is absent or stale and a fresh one is on its way."}},"type":"object","required":["generating"],"title":"RecordBriefResponse","description":"Envelope: the (possibly stale, possibly absent) brief + whether a\nregenerate is in flight."},"RecoveryCodeRequest":{"properties":{"code":{"type":"string","maxLength":12,"minLength":10,"title":"Code","description":"Recovery code"}},"type":"object","required":["code"],"title":"RecoveryCodeRequest","description":"Body for `POST /2fa/step-up/recovery` — recovery-code fallback when\nthe user's authenticator app is unavailable."},"ResetPasswordRequest":{"properties":{"token":{"type":"string","title":"Token"},"new_password":{"type":"string","minLength":12,"title":"New Password"}},"type":"object","required":["token","new_password"],"title":"ResetPasswordRequest"},"RunScheduleListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/RunScheduleOut"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"RunScheduleListResponse"},"RunScheduleOut":{"properties":{"template_key":{"type":"string","title":"Template Key"},"label":{"type":"string","title":"Label"},"description":{"type":"string","title":"Description"},"enabled":{"type":"boolean","title":"Enabled"},"cadence_kind":{"type":"string","title":"Cadence Kind"},"cadence_weekday":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Cadence Weekday"},"cadence_hour":{"type":"integer","title":"Cadence Hour"},"conversation_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Conversation Id"},"last_fired_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Fired At"},"last_outcome":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Last Outcome"}},"type":"object","required":["template_key","label","description","enabled","cadence_kind","cadence_weekday","cadence_hour","conversation_id","last_fired_at","last_outcome"],"title":"RunScheduleOut","description":"One scheduled-work catalog entry: the code-defined template merged with\nthe tenant's row — registry defaults + `enabled=False` when no row exists\nyet (enabling is what mints the row)."},"RunScheduleUpdate":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"cadence_kind":{"type":"string","enum":["daily","weekly"],"title":"Cadence Kind"},"cadence_weekday":{"anyOf":[{"type":"integer","maximum":6.0,"minimum":0.0},{"type":"null"}],"title":"Cadence Weekday"},"cadence_hour":{"type":"integer","maximum":23.0,"minimum":0.0,"title":"Cadence Hour"}},"type":"object","required":["enabled","cadence_kind","cadence_hour"],"title":"RunScheduleUpdate","description":"Admin upsert of one template's schedule (M-Runs-b, GAP-177 part 2).\nHours are UTC — no tenant timezone field exists; the settings card shows a\nlocal-time hint. A weekly cadence requires `cadence_weekday` (0=Mon…6=Sun);\na daily one ignores it."},"SaveChartRequest":{"properties":{"chart_spec":{"additionalProperties":true,"type":"object","title":"Chart Spec"},"dashboard_id":{"type":"string","format":"uuid","title":"Dashboard Id"},"title":{"type":"string","maxLength":200,"title":"Title","default":""}},"type":"object","required":["chart_spec","dashboard_id"],"title":"SaveChartRequest"},"SavedSegmentCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"query":{"$ref":"#/components/schemas/SegmentQuery-Input"}},"type":"object","required":["name","query"],"title":"SavedSegmentCreate"},"SavedSegmentRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"name":{"type":"string","title":"Name"},"query":{"additionalProperties":true,"type":"object","title":"Query"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","tenant_id","name","query","created_at","updated_at"],"title":"SavedSegmentRead"},"SavedSegmentUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"query":{"anyOf":[{"$ref":"#/components/schemas/SegmentQuery-Input"},{"type":"null"}]}},"type":"object","title":"SavedSegmentUpdate","description":"Partial update — name and/or query, at least one required.\n\nEmpty body is rejected at the route layer so the audit row carries a\nmeaningful diff."},"SavedTableViewCreate":{"properties":{"entity_type":{"type":"string","enum":["contact","company","outreach"],"title":"Entity Type"},"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"view_state":{"$ref":"#/components/schemas/ViewState"},"is_default":{"type":"boolean","title":"Is Default","default":false}},"type":"object","required":["entity_type","name","view_state"],"title":"SavedTableViewCreate"},"SavedTableViewList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/SavedTableViewRead"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"SavedTableViewList","description":"Wrapper around a flat list — mirrors the M33b/M33c response shape so\nthe frontend hook can read `.items` without a top-level array."},"SavedTableViewRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"entity_type":{"type":"string","enum":["contact","company","outreach"],"title":"Entity Type"},"name":{"type":"string","title":"Name"},"view_state":{"additionalProperties":true,"type":"object","title":"View State"},"is_default":{"type":"boolean","title":"Is Default"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","tenant_id","user_id","entity_type","name","view_state","is_default","created_at","updated_at"],"title":"SavedTableViewRead"},"SavedTableViewUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"view_state":{"anyOf":[{"$ref":"#/components/schemas/ViewState"},{"type":"null"}]},"is_default":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Default"}},"type":"object","title":"SavedTableViewUpdate","description":"Partial update — any of name / view_state / is_default.\n\n`entity_type` is NOT updatable: the persisted column ids in `view_state`\nare entity-shape-specific, so switching a view's entity_type would\norphan the persisted columnVisibility / columnOrder keys. Build a new\nview for the other entity instead.\n\nEmpty body is rejected at the route layer so the audit row carries a\nmeaningful diff."},"ScanCandidatePage":{"properties":{"items":{"items":{"$ref":"#/components/schemas/MailboxScanCandidateOut"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["items","total"],"title":"ScanCandidatePage","description":"A page of the review list, highest-score-first, with the full count so the\nFE can show \"N people found\"."},"ScanCommitRequest":{"properties":{"candidate_ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":5000,"minItems":1,"title":"Candidate Ids"}},"type":"object","required":["candidate_ids"],"title":"ScanCommitRequest","description":"The user's final opt-in selection — the candidate ids to create/link."},"ScanCommitResult":{"properties":{"mode":{"type":"string","enum":["sync","enqueued"],"title":"Mode"},"created":{"type":"integer","title":"Created"},"linked":{"type":"integer","title":"Linked"},"skipped":{"type":"integer","title":"Skipped"},"job":{"$ref":"#/components/schemas/MailboxScanJobOut"}},"type":"object","required":["mode","created","linked","skipped","job"],"title":"ScanCommitResult","description":"The commit outcome. ``mode`` is ``sync`` when the import ran inline; the\n``enqueued`` path (OQ8, large selections) returns the importing job to poll.\nThe counts are the inline result (0 / poll the job for the enqueued path)."},"ScanCreateRequest":{"properties":{"account_id":{"type":"string","format":"uuid","title":"Account Id"}},"type":"object","required":["account_id"],"title":"ScanCreateRequest","description":"Start a scan of one connected account."},"ScheduledExportCreate":{"properties":{"dashboard_id":{"type":"string","format":"uuid","title":"Dashboard Id"},"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"cadence":{"type":"string","enum":["daily","weekly","monthly"],"title":"Cadence"},"format":{"type":"string","enum":["pdf","png","csv"],"title":"Format","default":"pdf"},"delivery_kind":{"type":"string","enum":["email","webhook"],"title":"Delivery Kind","default":"email"},"delivery_config":{"additionalProperties":true,"type":"object","title":"Delivery Config"},"schedule_hour":{"type":"integer","maximum":23.0,"minimum":0.0,"title":"Schedule Hour","default":9}},"additionalProperties":false,"type":"object","required":["dashboard_id","name","cadence"],"title":"ScheduledExportCreate"},"ScheduledExportListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ScheduledExportRead"},"type":"array","title":"Items"}},"additionalProperties":false,"type":"object","required":["items"],"title":"ScheduledExportListResponse"},"ScheduledExportRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"dashboard_id":{"type":"string","format":"uuid","title":"Dashboard Id"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"name":{"type":"string","title":"Name"},"cadence":{"type":"string","title":"Cadence"},"format":{"type":"string","title":"Format"},"delivery_kind":{"type":"string","title":"Delivery Kind"},"delivery_config":{"additionalProperties":true,"type":"object","title":"Delivery Config"},"is_active":{"type":"boolean","title":"Is Active"},"schedule_hour":{"type":"integer","title":"Schedule Hour"},"last_run_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Run At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"additionalProperties":false,"type":"object","required":["id","tenant_id","dashboard_id","created_by","name","cadence","format","delivery_kind","delivery_config","is_active","schedule_hour","last_run_at","created_at","updated_at"],"title":"ScheduledExportRead"},"ScheduledExportUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Name"},"cadence":{"anyOf":[{"type":"string","enum":["daily","weekly","monthly"]},{"type":"null"}],"title":"Cadence"},"format":{"anyOf":[{"type":"string","enum":["pdf","png","csv"]},{"type":"null"}],"title":"Format"},"delivery_kind":{"anyOf":[{"type":"string","enum":["email","webhook"]},{"type":"null"}],"title":"Delivery Kind"},"delivery_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Delivery Config"},"schedule_hour":{"anyOf":[{"type":"integer","maximum":23.0,"minimum":0.0},{"type":"null"}],"title":"Schedule Hour"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"additionalProperties":false,"type":"object","title":"ScheduledExportUpdate"},"SearchHit":{"properties":{"id":{"type":"string","title":"Id"},"label":{"type":"string","title":"Label"},"sublabel":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sublabel"}},"type":"object","required":["id","label"],"title":"SearchHit","description":"One row in a per-entity-type search result list."},"SegmentBuildListRequest":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"query":{"$ref":"#/components/schemas/SegmentQuery-Input"}},"type":"object","required":["name","query"],"title":"SegmentBuildListRequest"},"SegmentBuildListResponse":{"properties":{"list_id":{"type":"string","format":"uuid","title":"List Id"}},"type":"object","required":["list_id"],"title":"SegmentBuildListResponse","description":"Just the created list id — frontend redirects to /lists/{id} and the\nlist-detail page is responsible for surfacing the populated state."},"SegmentCondition":{"properties":{"field":{"type":"string","maxLength":255,"minLength":1,"title":"Field"},"op":{"type":"string","enum":["equals","not_equals","in","not_in","contains","starts_with","ends_with","contains_any","contains_all","gt","lt","between","is_null","is_not_null"],"title":"Op"},"value":{"title":"Value"}},"additionalProperties":false,"type":"object","required":["field","op"],"title":"SegmentCondition","description":"A single field/op/value triple.\n\nValue semantics are op-dependent:\n  - `is_null` / `is_not_null` ignore `value`\n  - `in` / `not_in` / `contains_any` / `contains_all` expect a list\n  - `between` expects a 2-tuple-like list `[lo, hi]`\n  - all others expect a scalar\nType-level validation happens in the compiler against the field registry."},"SegmentDescribeRequest":{"properties":{"prompt":{"type":"string","maxLength":2000,"minLength":1,"title":"Prompt"}},"type":"object","required":["prompt"],"title":"SegmentDescribeRequest","description":"POST /segments/describe — a natural-language description of a filter."},"SegmentDescribeResponse":{"properties":{"query":{"anyOf":[{"$ref":"#/components/schemas/SegmentQuery-Output"},{"type":"null"}]},"explanation":{"type":"string","title":"Explanation"}},"type":"object","required":["explanation"],"title":"SegmentDescribeResponse","description":"The composed segment. `query` is null when the description can't be\nexpressed with the available fields (or composition failed after a retry);\n`explanation` always carries a human-readable note for the review panel.\nReview-before-build: the query is returned for the user to confirm into the\nbuilder, never auto-run."},"SegmentLeafGroup":{"properties":{"operator":{"type":"string","enum":["AND","OR"],"title":"Operator"},"conditions":{"items":{"$ref":"#/components/schemas/SegmentCondition"},"type":"array","minItems":1,"title":"Conditions"}},"additionalProperties":false,"type":"object","required":["operator","conditions"],"title":"SegmentLeafGroup","description":"A nested group — conditions only (1-level nesting cap)."},"SegmentPreviewRequest":{"properties":{"query":{"$ref":"#/components/schemas/SegmentQuery-Input"}},"type":"object","required":["query"],"title":"SegmentPreviewRequest"},"SegmentPreviewResponse":{"properties":{"count":{"type":"integer","title":"Count"},"contacts":{"items":{"$ref":"#/components/schemas/ContactSummary"},"type":"array","title":"Contacts"}},"type":"object","required":["count","contacts"],"title":"SegmentPreviewResponse","description":"Match count + first 20 contacts (no pagination — preview is a\nquick \"does this look right?\" surface, not a browse-table)."},"SegmentQuery-Input":{"properties":{"operator":{"type":"string","enum":["AND","OR"],"title":"Operator"},"conditions":{"items":{"oneOf":[{"$ref":"#/components/schemas/SegmentCondition"},{"$ref":"#/components/schemas/SegmentLeafGroup"}]},"type":"array","maxItems":50,"minItems":1,"title":"Conditions"}},"additionalProperties":false,"type":"object","required":["operator","conditions"],"title":"SegmentQuery","description":"Top-level group — may contain conditions and at most one level of\nnested groups (rejected as a doubly-nested structure otherwise)."},"SegmentQuery-Output":{"properties":{"operator":{"type":"string","enum":["AND","OR"],"title":"Operator"},"conditions":{"items":{"oneOf":[{"$ref":"#/components/schemas/SegmentCondition"},{"$ref":"#/components/schemas/SegmentLeafGroup"}]},"type":"array","maxItems":50,"minItems":1,"title":"Conditions"}},"additionalProperties":false,"type":"object","required":["operator","conditions"],"title":"SegmentQuery","description":"Top-level group — may contain conditions and at most one level of\nnested groups (rejected as a doubly-nested structure otherwise)."},"SelfBuildRevealOut":{"properties":{"job_id":{"type":"string","format":"uuid","title":"Job Id"},"account_id":{"type":"string","format":"uuid","title":"Account Id"},"provider":{"type":"string","title":"Provider"},"total_found":{"type":"integer","title":"Total Found"},"recommended_count":{"type":"integer","title":"Recommended Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["job_id","account_id","provider","total_found","recommended_count","created_at"],"title":"SelfBuildRevealOut","description":"The ``/today`` self-build reveal payload (M-SelfBuild-b): the staged cold-start\nbackfill awaiting one-tap acceptance. ``recommended_count`` is the high-confidence\nsubset \"Add all\" imports; ``total_found`` is everyone the scan staged. The route\nreturns ``null`` when there is nothing to reveal."},"SendApprovalApproveRequest":{"properties":{"account_id":{"type":"string","format":"uuid","title":"Account Id"},"template_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Template Id"},"from_send_as":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From Send As"}},"type":"object","required":["account_id"],"title":"SendApprovalApproveRequest"},"SendApprovalRejectRequest":{"properties":{"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","title":"SendApprovalRejectRequest"},"SendApprovalResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"campaign_id":{"type":"string","format":"uuid","title":"Campaign Id"},"status":{"type":"string","title":"Status"},"account_id":{"type":"string","format":"uuid","title":"Account Id"},"template_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Template Id"},"from_send_as":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From Send As"},"requested_by_user_id":{"type":"string","format":"uuid","title":"Requested By User Id"},"requested_via_api_key_id":{"type":"string","format":"uuid","title":"Requested Via Api Key Id"},"decided_by_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Decided By User Id"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Decided At"},"rejection_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rejection Reason"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","campaign_id","status","account_id","requested_by_user_id","requested_via_api_key_id","created_at"],"title":"SendApprovalResponse"},"SendAsAlias":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"is_primary":{"type":"boolean","title":"Is Primary","default":false},"is_default":{"type":"boolean","title":"Is Default","default":false}},"type":"object","required":["email"],"title":"SendAsAlias","description":"One row from `users.settings.sendAs.list()` — what Gmail exposes as a\nvalid From address on the connected account. The compose picker (M26d)\nconsumes this list to drive its send-as dropdown."},"SetTenantPlanRequest":{"properties":{"plan":{"$ref":"#/components/schemas/Plan"}},"type":"object","required":["plan"],"title":"SetTenantPlanRequest","description":"PATCH .../tenant/{id}/plan — the target plan. `plan` is the `Plan` enum, so an\nunknown slug is rejected at the schema layer with 422 (GAP-054, M-Tier-a)."},"SetTenantPlanResponse":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"plan":{"type":"string","title":"Plan"},"ai_monthly_token_limit":{"type":"integer","title":"Ai Monthly Token Limit"}},"type":"object","required":["tenant_id","plan","ai_monthly_token_limit"],"title":"SetTenantPlanResponse"},"SimilarCompaniesResponse":{"properties":{"seed_embedded":{"type":"boolean","title":"Seed Embedded","description":"Whether the seed record has a stored embedding yet (false = backfill pending)."},"results":{"items":{"$ref":"#/components/schemas/SimilarCompanyResult"},"type":"array","title":"Results"}},"type":"object","required":["seed_embedded","results"],"title":"SimilarCompaniesResponse"},"SimilarCompanyResult":{"properties":{"company":{"$ref":"#/components/schemas/CompanyListItem"},"distance":{"type":"number","title":"Distance","description":"Cosine distance from the seed (0 = identical)."}},"type":"object","required":["company","distance"],"title":"SimilarCompanyResult"},"SimilarContactResult":{"properties":{"contact":{"$ref":"#/components/schemas/ContactListItem"},"distance":{"type":"number","title":"Distance","description":"Cosine distance from the seed (0 = identical)."}},"type":"object","required":["contact","distance"],"title":"SimilarContactResult"},"SimilarContactsResponse":{"properties":{"seed_embedded":{"type":"boolean","title":"Seed Embedded","description":"Whether the seed record has a stored embedding yet (false = backfill pending)."},"results":{"items":{"$ref":"#/components/schemas/SimilarContactResult"},"type":"array","title":"Results"}},"type":"object","required":["seed_embedded","results"],"title":"SimilarContactsResponse"},"SortDirection":{"type":"string","enum":["asc_value","desc_value","asc_label","desc_label"],"title":"SortDirection","description":"Sort direction options for `ChartDisplay.sort_by`."},"SortingItem":{"properties":{"id":{"type":"string","maxLength":64,"minLength":1,"title":"Id"},"desc":{"type":"boolean","title":"Desc"}},"additionalProperties":false,"type":"object","required":["id","desc"],"title":"SortingItem","description":"One entry in TanStack Table's `sorting` state — a column id + direction."},"SourceLink":{"properties":{"url":{"type":"string","maxLength":2000,"minLength":1,"title":"Url"},"label":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Label"},"cited_fields":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Cited Fields"}},"type":"object","required":["url"],"title":"SourceLink","description":"One source URL + which profile fields it grounded — the single home for links.\n\nOwner-supplied entries carry just ``url`` (+ optional ``label``); the absorb distill\nannotates ``cited_fields`` so the never-fabricate eval can assert each citation\ntraces back to a supplied source."},"SourceSummary":{"properties":{"kind":{"type":"string","enum":["list","segment","manual"],"title":"Kind"},"ref_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Ref Id"},"ref_label":{"type":"string","title":"Ref Label"},"added_count":{"type":"integer","title":"Added Count"},"also_in_other_sources_count":{"type":"integer","title":"Also In Other Sources Count"}},"additionalProperties":false,"type":"object","required":["kind","ref_id","ref_label","added_count","also_in_other_sources_count"],"title":"SourceSummary","description":"One row per source attached to the campaign. Surfaces as a chip in\nthe Recipients wizard step."},"StagePeopleRequest":{"properties":{"batch_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Batch Id"},"people":{"items":{"$ref":"#/components/schemas/ProspectPersonCreate"},"type":"array","maxItems":200,"minItems":1,"title":"People"}},"type":"object","required":["people"],"title":"StagePeopleRequest","description":"A batch of discovered people to persist for review. ``batch_id`` ties the\nstaged rows to the ``find_people`` run; omit it and the service mints one."},"StagePeopleResult":{"properties":{"batch_id":{"type":"string","format":"uuid","title":"Batch Id"},"staged":{"type":"integer","title":"Staged"}},"type":"object","required":["batch_id","staged"],"title":"StagePeopleResult","description":"Outcome of a stage write — the batch persisted + how many rows it created."},"StageProspectsRequest":{"properties":{"batch_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Batch Id"},"candidates":{"items":{"$ref":"#/components/schemas/ProspectCandidateCreate"},"type":"array","maxItems":200,"minItems":1,"title":"Candidates"}},"type":"object","required":["candidates"],"title":"StageProspectsRequest","description":"A batch of discovered candidates to persist for review.\n\n``batch_id`` ties the staged rows to the ``find_prospects`` run that produced\nthem; omit it and the service mints one. ``candidates`` is bounded by\n``MAX_BATCH_SIZE``."},"StageProspectsResult":{"properties":{"batch_id":{"type":"string","format":"uuid","title":"Batch Id"},"staged":{"type":"integer","title":"Staged"}},"type":"object","required":["batch_id","staged"],"title":"StageProspectsResult","description":"Outcome of a stage write — the batch persisted + how many rows it created."},"SuggestedActionAssignee":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"label":{"type":"string","title":"Label"}},"type":"object","required":["user_id","label"],"title":"SuggestedActionAssignee","description":"The team user a create_task suggestion's committer resolved to."},"SuggestedActionRead":{"properties":{"id":{"type":"string","title":"Id"},"kind":{"type":"string","enum":["create_task","add_note","advance_deal_stage"],"title":"Kind"},"action_item":{"type":"string","title":"Action Item"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"},"target":{"anyOf":[{"$ref":"#/components/schemas/SuggestedActionTarget"},{"type":"null"}]},"assignee":{"anyOf":[{"$ref":"#/components/schemas/SuggestedActionAssignee"},{"type":"null"}]},"state":{"type":"string","enum":["suggested","applied","dismissed"],"title":"State"},"applied_resource_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Applied Resource Id"}},"type":"object","required":["id","kind","action_item","state"],"title":"SuggestedActionRead","description":"One typed, suggest-only action extracted from a call transcript\n(M-Calls-e, GAP-175 §D3). Persisted in ``call_recordings.suggested_actions``;\nnothing is written to the CRM until the user's explicit Apply."},"SuggestedActionResult":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","enum":["applied","reused","dismissed","already_applied","already_dismissed","failed"],"title":"Status"},"resource_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource Id"},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail"}},"type":"object","required":["id","status"],"title":"SuggestedActionResult","description":"Per-item outcome of a review POST. ``reused`` is the idempotent hit — an\nopen task with the same subject already existed, or the deal was already in\nthe requested stage."},"SuggestedActionTarget":{"properties":{"entity_type":{"type":"string","enum":["contact","company","deal"],"title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"},"label":{"type":"string","title":"Label"}},"type":"object","required":["entity_type","entity_id","label"],"title":"SuggestedActionTarget","description":"The CRM record a suggestion resolved to (deterministic grounding —\nunique high-confidence match only, M-Calls-e)."},"SuppressionBreakdown":{"properties":{"unsubscribed":{"type":"integer","title":"Unsubscribed","default":0},"email_invalid":{"type":"integer","title":"Email Invalid","default":0}},"additionalProperties":false,"type":"object","title":"SuppressionBreakdown","description":"Counts of contacts excluded from a recipient-set due to per-contact\nsuppression flags. Precedence when both apply: unsubscribed > email_invalid\n(the user-action signal beats the system-action signal for display)."},"TagAssignmentCount":{"properties":{"tag_id":{"type":"string","format":"uuid","title":"Tag Id"},"usage_count":{"type":"integer","title":"Usage Count"}},"type":"object","required":["tag_id","usage_count"],"title":"TagAssignmentCount"},"TagAssignmentCreate":{"properties":{"entity_type":{"type":"string","enum":["contact","company","deal"],"title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"},"tag_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Tag Id"},"name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Name"}},"type":"object","required":["entity_type","entity_id"],"title":"TagAssignmentCreate","description":"Assign a tag to an entity by ``tag_id`` (an existing tag) OR ``name``\n(M-Tags-c get-or-create — the picker's allow-create) — exactly one of the two."},"TagAssignmentRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tag_id":{"type":"string","format":"uuid","title":"Tag Id"},"entity_type":{"type":"string","title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"}},"type":"object","required":["id","tag_id","entity_type","entity_id"],"title":"TagAssignmentRead"},"TagCreate":{"properties":{"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name"},"category":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Category"},"color":{"anyOf":[{"type":"string","enum":["slate","red","orange","amber","green","teal","blue","violet","pink"]},{"type":"null"}],"title":"Color"}},"type":"object","required":["name"],"title":"TagCreate"},"TagList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/TagRead"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["items","total"],"title":"TagList"},"TagMergeRequest":{"properties":{"source_id":{"type":"string","format":"uuid","title":"Source Id"},"target_id":{"type":"string","format":"uuid","title":"Target Id"}},"type":"object","required":["source_id","target_id"],"title":"TagMergeRequest"},"TagRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"usage_count":{"type":"integer","title":"Usage Count","default":0}},"type":"object","required":["id","name","slug"],"title":"TagRead"},"TagUpdate":{"properties":{"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name"},"category":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Category"},"color":{"anyOf":[{"type":"string","enum":["slate","red","orange","amber","green","teal","blue","violet","pink"]},{"type":"null"}],"title":"Color"}},"type":"object","required":["name"],"title":"TagUpdate","description":"PUT-style: the full editable set. ``category``/``color`` = null clears them."},"TaskCreate":{"properties":{"subject":{"type":"string","maxLength":255,"minLength":1,"title":"Subject"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"assignee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assignee Id"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"priority":{"type":"string","pattern":"^(low|normal|high)$","title":"Priority","default":"normal"},"status":{"type":"string","pattern":"^(todo|in_progress|waiting|done)$","title":"Status","default":"todo"},"size":{"anyOf":[{"type":"string","pattern":"^(s|m|l)$"},{"type":"null"}],"title":"Size"},"entity_type":{"anyOf":[{"type":"string","pattern":"^(contact|deal|company)$"},{"type":"null"}],"title":"Entity Type"},"entity_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Entity Id"}},"type":"object","required":["subject"],"title":"TaskCreate"},"TaskDetail":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"subject":{"type":"string","title":"Subject"},"priority":{"type":"string","title":"Priority"},"status":{"type":"string","title":"Status"},"size":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Size"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"assignee":{"$ref":"#/components/schemas/UserSummary"},"entity_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Type"},"entity_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Entity Id"},"entity_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Label"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"created_by":{"$ref":"#/components/schemas/UserSummary"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","subject","priority","status","assignee","created_at","created_by","updated_at"],"title":"TaskDetail"},"TaskListItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"subject":{"type":"string","title":"Subject"},"priority":{"type":"string","title":"Priority"},"status":{"type":"string","title":"Status"},"size":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Size"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"assignee":{"$ref":"#/components/schemas/UserSummary"},"entity_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Type"},"entity_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Entity Id"},"entity_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Label"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","subject","priority","status","assignee","created_at"],"title":"TaskListItem"},"TaskUpdate":{"properties":{"subject":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Subject"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"assignee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assignee Id"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"priority":{"anyOf":[{"type":"string","pattern":"^(low|normal|high)$"},{"type":"null"}],"title":"Priority"},"status":{"anyOf":[{"type":"string","pattern":"^(todo|in_progress|waiting|done)$"},{"type":"null"}],"title":"Status"},"size":{"anyOf":[{"type":"string","pattern":"^(s|m|l)$"},{"type":"null"}],"title":"Size"},"entity_type":{"anyOf":[{"type":"string","pattern":"^(contact|deal|company)$"},{"type":"null"}],"title":"Entity Type"},"entity_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Entity Id"}},"type":"object","title":"TaskUpdate"},"TemplateDraft":{"properties":{"subject":{"type":"string","title":"Subject"},"body":{"type":"string","title":"Body"},"folder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder"}},"type":"object","required":["subject","body"],"title":"TemplateDraft","description":"A composed template draft. `name` is deliberately absent — the user names\nthe template; `variables` are re-derived client-side from subject + body."},"TemplateDraftRequest":{"properties":{"prompt":{"type":"string","maxLength":2000,"minLength":1,"title":"Prompt"}},"type":"object","required":["prompt"],"title":"TemplateDraftRequest","description":"POST /email-templates/describe — a natural-language description of the\nemail to draft."},"TemplateDraftResponse":{"properties":{"draft":{"anyOf":[{"$ref":"#/components/schemas/TemplateDraft"},{"type":"null"}]},"explanation":{"type":"string","title":"Explanation"}},"type":"object","required":["explanation"],"title":"TemplateDraftResponse","description":"The composed draft. `draft` is null when the description can't be turned\ninto a template (or composition failed after a retry); `explanation` always\ncarries a human-readable note for the review panel. Review-before-emit: the\ndraft is returned for the user to confirm into the editor, never auto-saved."},"TenantAIConfigRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"provider":{"$ref":"#/components/schemas/AIProvider"},"model":{"type":"string","title":"Model"},"api_key_set":{"type":"boolean","title":"Api Key Set"},"endpoint_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Endpoint Url"},"extra_headers":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Extra Headers"},"default_for_tasks":{"items":{"type":"string"},"type":"array","title":"Default For Tasks"},"thinking_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Thinking Enabled"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","provider","model","api_key_set","endpoint_url","extra_headers","default_for_tasks","thinking_enabled","created_at","updated_at"],"title":"TenantAIConfigRead"},"TenantAIConfigResponse":{"properties":{"config":{"anyOf":[{"$ref":"#/components/schemas/TenantAIConfigRead"},{"type":"null"}]}},"type":"object","title":"TenantAIConfigResponse","description":"GET /tenants/me/ai-config envelope.\n\n`config` is null when the tenant has no AI config row — the system\nfalls back to platform defaults for AI calls. UI uses null to show\nan empty-state (\"Configure AI\") instead of an error."},"TenantAIConfigTestRequest":{"properties":{"provider":{"$ref":"#/components/schemas/AIProvider"},"model":{"type":"string","maxLength":255,"minLength":1,"title":"Model"},"api_key":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Api Key"},"endpoint_url":{"anyOf":[{"type":"string","maxLength":2083,"minLength":1,"format":"uri"},{"type":"null"}],"title":"Endpoint Url"},"extra_headers":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Extra Headers"},"thinking_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Thinking Enabled"}},"type":"object","required":["provider","model"],"title":"TenantAIConfigTestRequest","description":"POST /tenants/me/ai-config/test — connection test without saving.\n\nSame shape as Upsert; if `api_key` is omitted the service uses the\nalready-saved value (so a tenant can re-test their stored config\nwithout retyping the key)."},"TenantAIConfigUpsert":{"properties":{"provider":{"$ref":"#/components/schemas/AIProvider"},"model":{"type":"string","maxLength":255,"minLength":1,"title":"Model"},"api_key":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Api Key"},"endpoint_url":{"anyOf":[{"type":"string","maxLength":2083,"minLength":1,"format":"uri"},{"type":"null"}],"title":"Endpoint Url"},"extra_headers":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Extra Headers"},"default_for_tasks":{"items":{"$ref":"#/components/schemas/TenantAITask"},"type":"array","title":"Default For Tasks"},"thinking_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Thinking Enabled"}},"type":"object","required":["provider","model"],"title":"TenantAIConfigUpsert","description":"PATCH /tenants/me/ai-config — full-config replace.\n\nWhole-row upsert (one row per tenant, no partial-field patch in v1).\napi_key is plaintext at the wire boundary; the service encrypts before\npersisting."},"TenantAITask":{"type":"string","enum":["sentiment","personalization","analytics","outreach_agent","general"],"title":"TenantAITask","description":"Workflow-level task buckets — distinct from `LLMClient.TaskType`.\n\nUsed by `get_llm_client_for_tenant(task=...)` to support future per-task\nrouting (e.g. cheap classifier for sentiment, premium model for\npersonalization). v1: single config services every task; the value is\naccepted but ignored by the router."},"TenantAgentActivityOut":{"properties":{"actions_total":{"type":"integer","title":"Actions Total"},"actions_by_status":{"additionalProperties":{"type":"integer"},"type":"object","title":"Actions By Status"},"runs_total":{"type":"integer","title":"Runs Total"},"runs_last_finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Runs Last Finished At"},"schedules_enabled":{"type":"integer","title":"Schedules Enabled"}},"type":"object","required":["actions_total","actions_by_status","runs_total","runs_last_finished_at","schedules_enabled"],"title":"TenantAgentActivityOut"},"TenantAuditEntryOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"action":{"type":"string","title":"Action"},"resource_type":{"type":"string","title":"Resource Type"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"actor_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Email"}},"type":"object","required":["id","action","resource_type","created_at","actor_email"],"title":"TenantAuditEntryOut"},"TenantDeletionInitiate":{"properties":{"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"additionalProperties":false,"type":"object","title":"TenantDeletionInitiate"},"TenantDeletionRequestRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"initiated_by_user_id":{"type":"string","format":"uuid","title":"Initiated By User Id"},"initiated_at":{"type":"string","format":"date-time","title":"Initiated At"},"initiation_actor_type":{"type":"string","title":"Initiation Actor Type"},"scheduled_at":{"type":"string","format":"date-time","title":"Scheduled At"},"cancelled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Cancelled At"},"cancelled_by_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Cancelled By User Id"},"cancellation_actor_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cancellation Actor Type"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"courtesy_export_job_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Courtesy Export Job Id"},"last_reminder_sent_day":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Last Reminder Sent Day"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"additionalProperties":false,"type":"object","required":["id","tenant_id","initiated_by_user_id","initiated_at","initiation_actor_type","scheduled_at","cancelled_at","cancelled_by_user_id","cancellation_actor_type","reason","courtesy_export_job_id","last_reminder_sent_day","created_at"],"title":"TenantDeletionRequestRead"},"TenantDetailMailboxOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"provider":{"type":"string","title":"Provider"},"email_address":{"type":"string","title":"Email Address"},"connected_at":{"type":"string","format":"date-time","title":"Connected At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"}},"type":"object","required":["id","provider","email_address","connected_at","last_used_at","revoked_at"],"title":"TenantDetailMailboxOut"},"TenantDetailOut":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"plan":{"type":"string","title":"Plan"},"subscription_status":{"type":"string","title":"Subscription Status"},"data_region":{"type":"string","title":"Data Region"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"onboarding_complete":{"type":"boolean","title":"Onboarding Complete"},"ai_monthly_token_limit":{"type":"integer","title":"Ai Monthly Token Limit"},"ai_quota_exceeded":{"type":"boolean","title":"Ai Quota Exceeded"},"require_2fa":{"type":"boolean","title":"Require 2Fa"},"users":{"items":{"$ref":"#/components/schemas/TenantDetailUserOut"},"type":"array","title":"Users"},"mailboxes":{"items":{"$ref":"#/components/schemas/TenantDetailMailboxOut"},"type":"array","title":"Mailboxes"},"contacts":{"type":"integer","title":"Contacts"},"companies":{"type":"integer","title":"Companies"},"deals":{"type":"integer","title":"Deals"},"agent_activity":{"$ref":"#/components/schemas/TenantAgentActivityOut"},"recent_audit":{"items":{"$ref":"#/components/schemas/TenantAuditEntryOut"},"type":"array","title":"Recent Audit"}},"type":"object","required":["tenant_id","name","slug","plan","subscription_status","data_region","created_at","onboarding_complete","ai_monthly_token_limit","ai_quota_exceeded","require_2fa","users","mailboxes","contacts","companies","deals","agent_activity","recent_audit"],"title":"TenantDetailOut"},"TenantDetailUserOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"email":{"type":"string","title":"Email"},"full_name":{"type":"string","title":"Full Name"},"role":{"type":"string","title":"Role"},"is_active":{"type":"boolean","title":"Is Active"},"last_login_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Login At"},"is_platform_admin":{"type":"boolean","title":"Is Platform Admin"}},"type":"object","required":["id","email","full_name","role","is_active","last_login_at","is_platform_admin"],"title":"TenantDetailUserOut"},"TenantDirectoryRowOut":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"plan":{"type":"string","title":"Plan"},"onboarding_complete":{"type":"boolean","title":"Onboarding Complete"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_activity_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Activity At"},"active_seats":{"type":"integer","title":"Active Seats"},"contacts":{"type":"integer","title":"Contacts"},"companies":{"type":"integer","title":"Companies"},"deals":{"type":"integer","title":"Deals"},"mailboxes_connected":{"type":"integer","title":"Mailboxes Connected"},"ai_tokens_month":{"type":"integer","title":"Ai Tokens Month"},"ai_monthly_token_limit":{"type":"integer","title":"Ai Monthly Token Limit"},"ai_pct_of_limit":{"type":"number","title":"Ai Pct Of Limit"},"ai_quota_exceeded":{"type":"boolean","title":"Ai Quota Exceeded"}},"type":"object","required":["tenant_id","name","slug","plan","onboarding_complete","created_at","last_activity_at","active_seats","contacts","companies","deals","mailboxes_connected","ai_tokens_month","ai_monthly_token_limit","ai_pct_of_limit","ai_quota_exceeded"],"title":"TenantDirectoryRowOut"},"TenantProfile":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"plan":{"type":"string","title":"Plan"},"subscription_status":{"type":"string","title":"Subscription Status"},"data_region":{"type":"string","title":"Data Region"},"onboarding_complete":{"type":"boolean","title":"Onboarding Complete"},"settings":{"additionalProperties":true,"type":"object","title":"Settings"},"require_2fa":{"type":"boolean","title":"Require 2Fa"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"inbound_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inbound Address","description":"The tenant's inbound-capture address (M-Inbound-a), derived from the\nHMAC self-describing token. ``None`` when ``INBOUND_PARSE_DOMAIN`` is\nunset (the feature is off). Computed (not stored) so it rides every\nTenantProfile serialization — GET and the PATCH echo the frontend caches\n— without a column. The opt-in toggle state lives in\n``settings.inbound_capture_enabled``.","readOnly":true}},"type":"object","required":["id","name","slug","plan","subscription_status","data_region","onboarding_complete","settings","require_2fa","created_at","updated_at","inbound_address"],"title":"TenantProfile"},"TenantUpdate":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"onboarding_complete":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Onboarding Complete"},"settings":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Settings"},"require_2fa":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Require 2Fa"}},"type":"object","title":"TenantUpdate"},"TenantUsageRowOut":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"tenant_name":{"type":"string","title":"Tenant Name"},"tenant_slug":{"type":"string","title":"Tenant Slug"},"input_tokens":{"type":"integer","title":"Input Tokens"},"output_tokens":{"type":"integer","title":"Output Tokens"},"total_tokens":{"type":"integer","title":"Total Tokens"},"monthly_limit":{"type":"integer","title":"Monthly Limit"},"pct_of_limit":{"type":"number","title":"Pct Of Limit"},"quota_exceeded":{"type":"boolean","title":"Quota Exceeded"},"call_count":{"type":"integer","title":"Call Count"},"last_call_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Call At"}},"type":"object","required":["tenant_id","tenant_name","tenant_slug","input_tokens","output_tokens","total_tokens","monthly_limit","pct_of_limit","quota_exceeded","call_count","last_call_at"],"title":"TenantUsageRowOut"},"TileCreate":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"chart_spec":{"additionalProperties":true,"type":"object","title":"Chart Spec"},"position_x":{"type":"integer","maximum":11.0,"minimum":0.0,"title":"Position X","default":0},"position_y":{"type":"integer","minimum":0.0,"title":"Position Y","default":0},"width":{"type":"integer","maximum":12.0,"minimum":1.0,"title":"Width","default":4},"height":{"type":"integer","minimum":1.0,"title":"Height","default":4}},"additionalProperties":false,"type":"object","required":["name","chart_spec"],"title":"TileCreate","description":"Payload for `POST /api/v1/dashboards/{id}/tiles`."},"TileLayoutItem":{"properties":{"tile_id":{"type":"string","format":"uuid","title":"Tile Id"},"position_x":{"type":"integer","maximum":11.0,"minimum":0.0,"title":"Position X"},"position_y":{"type":"integer","minimum":0.0,"title":"Position Y"},"width":{"type":"integer","maximum":12.0,"minimum":1.0,"title":"Width"},"height":{"type":"integer","minimum":1.0,"title":"Height"}},"additionalProperties":false,"type":"object","required":["tile_id","position_x","position_y","width","height"],"title":"TileLayoutItem","description":"A single tile's position + dimensions for bulk layout update."},"TileLayoutUpdate":{"properties":{"tiles":{"items":{"$ref":"#/components/schemas/TileLayoutItem"},"type":"array","minItems":1,"title":"Tiles"}},"additionalProperties":false,"type":"object","required":["tiles"],"title":"TileLayoutUpdate","description":"Payload for `PATCH /api/v1/dashboards/{id}/layout`.\n\nBulk layout update — every tile's position + dimensions in one call.\nThe editor sends the full layout on every drag-end so the server has\na consistent snapshot; partial updates (only the moved tile) would\ncreate race conditions with concurrent edits."},"TileUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Name"},"chart_spec":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Chart Spec"},"position_x":{"anyOf":[{"type":"integer","maximum":11.0,"minimum":0.0},{"type":"null"}],"title":"Position X"},"position_y":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Position Y"},"width":{"anyOf":[{"type":"integer","maximum":12.0,"minimum":1.0},{"type":"null"}],"title":"Width"},"height":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Height"}},"additionalProperties":false,"type":"object","title":"TileUpdate","description":"Payload for `PATCH /api/v1/dashboards/{id}/tiles/{tile_id}`."},"TimeGrain":{"type":"string","enum":["day","week","month","quarter","year"],"title":"TimeGrain","description":"Time-bucket granularity for time-based dimensions.\n\nThe compiler maps these to PostgreSQL `date_trunc(...)` arguments\n(e.g. `DAY` → `date_trunc('day', sent_at)`). Time grains finer than\n`DAY` are deliberately not exposed — minute/hour granularity stresses\nthe cache and the user experience suggests a different chart (event\nlog, not aggregated time series)."},"TimeRange":{"properties":{"start":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Start"},"end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"End"}},"additionalProperties":false,"type":"object","title":"TimeRange","description":"Bounded inclusive time window for the chart's data.\n\nBoth bounds optional — `start=None` means \"all history up to `end`\",\n`end=None` means \"from `start` onward\". `start=None AND end=None`\nmeans no time filter, which is allowed but typically not what a\nchart wants (renders too noisy at scale). The validator emits a\nwarning-shaped result (not an error) when both are None on entities\n> 100k rows; tunable later."},"TimelineEvent":{"properties":{"id":{"type":"string","title":"Id"},"type":{"type":"string","enum":["outreach","note_created","meeting_logged","task_created","task_completed","deal_stage_changed","owner_changed","call_recorded"],"title":"Type"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"actor":{"anyOf":[{"$ref":"#/components/schemas/ActorSummary"},{"type":"null"}]},"actor_type":{"type":"string","title":"Actor Type","default":"user"},"title":{"type":"string","title":"Title"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary"},"data":{"additionalProperties":true,"type":"object","title":"Data"}},"type":"object","required":["id","type","occurred_at","title"],"title":"TimelineEvent","description":"One normalized row in the merged feed.\n\n``id`` is source-prefixed (``outreach:<uuid>``, ``note:<uuid>``,\n``task:<uuid>:created``, ``audit:<uuid>``) so it is globally unique across\nthe union and stable for React keys + the cursor tiebreaker. ``occurred_at``\nis the canonical sort key, derived per source. ``data`` carries the\ntype-specific render payload (keys documented per producer in the service)."},"TodaySynthesisOut":{"properties":{"summary":{"type":"string","title":"Summary","description":"One-line \"here's your day\" read over the ranked brief."},"model":{"type":"string","title":"Model","description":"The model that produced the synthesis."},"generated_at":{"type":"string","format":"date-time","title":"Generated At","description":"When this synthesis was produced."}},"type":"object","required":["summary","model","generated_at"],"title":"TodaySynthesisOut","description":"The advisory one-line cross-signal synthesis over the morning brief."},"TodaySynthesisResponse":{"properties":{"summary":{"anyOf":[{"$ref":"#/components/schemas/TodaySynthesisOut"},{"type":"null"}],"description":"The advisory morning-brief synthesis, or null when the brief has fewer than two signals or a generation degraded."}},"type":"object","title":"TodaySynthesisResponse","description":"Envelope: the (possibly absent) advisory synthesis line."},"TopTenantShareOut":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"tenant_name":{"type":"string","title":"Tenant Name"},"total_tokens":{"type":"integer","title":"Total Tokens"}},"type":"object","required":["tenant_id","tenant_name","total_tokens"],"title":"TopTenantShareOut"},"TotpCompletionResponse":{"properties":{"recovery_codes":{"items":{"type":"string"},"type":"array","title":"Recovery Codes"}},"type":"object","required":["recovery_codes"],"title":"TotpCompletionResponse","description":"Returned ONCE from `POST /2fa/verify` on the enrolment-completion\npath (D6: copy-first one-shot reveal). The plaintext codes are never\nretrievable again — the user must download/copy/print them."},"TotpEnrolResponse":{"properties":{"provisioning_uri":{"type":"string","title":"Provisioning Uri"},"qr_png_base64":{"type":"string","title":"Qr Png Base64"}},"type":"object","required":["provisioning_uri","qr_png_base64"],"title":"TotpEnrolResponse","description":"Returned from `POST /2fa/enrol`. The user scans `qr_png_base64`\n(decoded as `data:image/png;base64,<...>`), then submits the next code\ntheir authenticator displays to `POST /2fa/verify`.\n\n`provisioning_uri` is the same `otpauth://totp/...` payload encoded in\nthe QR, surfaced as a fallback for users with text-secret-entry\nauthenticator apps (Bitwarden's CLI, etc.)."},"TotpStatusResponse":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"verified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Verified At"},"recovery_codes_remaining":{"type":"integer","title":"Recovery Codes Remaining"}},"type":"object","required":["enabled","verified_at","recovery_codes_remaining"],"title":"TotpStatusResponse","description":"Returned from `GET /2fa/status`. Drives the Settings → Security\npanel's enabled-state + the \"regenerate recovery codes\" CTA."},"TotpStepUpResponse":{"properties":{"access_token":{"type":"string","title":"Access Token"},"token_type":{"type":"string","const":"bearer","title":"Token Type","default":"bearer"}},"type":"object","required":["access_token"],"title":"TotpStepUpResponse","description":"Returned from `POST /2fa/step-up` after a successful TOTP submit.\nCarries a refreshed access token whose `step_up_at` claim is `now()`,\ngranting another STEP_UP_WINDOW_SECONDS of step-up-gated access."},"TotpVerifyRequest":{"properties":{"code":{"type":"string","maxLength":10,"minLength":6,"title":"Code","description":"TOTP digits"}},"type":"object","required":["code"],"title":"TotpVerifyRequest","description":"Body for `POST /2fa/verify` (completes enrolment) and\n`POST /2fa/step-up` (re-verify for a sensitive action)."},"TrustedDeviceResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["id","created_at","expires_at"],"title":"TrustedDeviceResponse","description":"One row in the trusted-device list. UA + jti are hashed at rest so\n`created_at` + `expires_at` are the only meaningful fields for the UI."},"UpdateMonthlyLimitRequest":{"properties":{"monthly_token_limit":{"type":"integer","maximum":10000000000.0,"minimum":100000.0,"title":"Monthly Token Limit"}},"type":"object","required":["monthly_token_limit"],"title":"UpdateMonthlyLimitRequest","description":"PATCH .../tenant/{id}/limit — bounded so a slipped extra zero doesn't\nnuke the gross-margin protection in one click."},"UpdateMonthlyLimitResponse":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"monthly_token_limit":{"type":"integer","title":"Monthly Token Limit"}},"type":"object","required":["tenant_id","monthly_token_limit"],"title":"UpdateMonthlyLimitResponse"},"UploadInstructionResponse":{"properties":{"kind":{"type":"string","enum":["presigned","direct"],"title":"Kind"},"method":{"type":"string","title":"Method"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"headers":{"additionalProperties":{"type":"string"},"type":"object","title":"Headers"}},"type":"object","required":["kind","method"],"title":"UploadInstructionResponse","description":"How the client delivers the audio bytes (see ``service.UploadInstruction``)."},"UploadResponse":{"properties":{"import_job_id":{"type":"string","format":"uuid","title":"Import Job Id"},"status":{"type":"string","title":"Status"},"original_filename":{"type":"string","title":"Original Filename"},"total_rows":{"type":"integer","title":"Total Rows"},"entity_type":{"type":"string","title":"Entity Type"},"columns":{"items":{"$ref":"#/components/schemas/ColumnSuggestion"},"type":"array","title":"Columns"},"detected_vendor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detected Vendor"},"sheet_names":{"items":{"type":"string"},"type":"array","title":"Sheet Names"},"selected_sheet":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Selected Sheet"},"header_row":{"type":"integer","title":"Header Row","default":0}},"type":"object","required":["import_job_id","status","original_filename","total_rows","entity_type","columns"],"title":"UploadResponse"},"UsageBucketOut":{"properties":{"input_tokens":{"type":"integer","title":"Input Tokens"},"output_tokens":{"type":"integer","title":"Output Tokens"},"request_count":{"type":"integer","title":"Request Count"},"estimated_cost_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Estimated Cost Usd"}},"type":"object","required":["input_tokens","output_tokens","request_count","estimated_cost_usd"],"title":"UsageBucketOut","description":"Per-dimension (task_type or model) rollup. `estimated_cost_usd` is a\nread-time per-model estimate in USD, never a billed figure (DA2/DA6). It is\n`null` for managed \"Limena AI\" tenants, whose estimate is Limena's own cost\nbasis rather than their spend (see `tenant_llm_config.is_managed_ai_tenant`)."},"UsageTimeseriesPointOut":{"properties":{"bucket":{"type":"string","title":"Bucket"},"input_tokens":{"type":"integer","title":"Input Tokens"},"output_tokens":{"type":"integer","title":"Output Tokens"},"request_count":{"type":"integer","title":"Request Count"},"estimated_cost_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Estimated Cost Usd"}},"type":"object","required":["bucket","input_tokens","output_tokens","request_count","estimated_cost_usd"],"title":"UsageTimeseriesPointOut"},"UsageTimeseriesResponse":{"properties":{"grain":{"type":"string","enum":["daily","monthly"],"title":"Grain"},"points":{"items":{"$ref":"#/components/schemas/UsageTimeseriesPointOut"},"type":"array","title":"Points"}},"type":"object","required":["grain","points"],"title":"UsageTimeseriesResponse"},"UseForOutreachRequest":{"properties":{"scope":{"type":"string","enum":["filter","items"],"title":"Scope"},"filter":{"anyOf":[{"$ref":"#/components/schemas/ItemFilter"},{"type":"null"}]},"item_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Item Ids"},"campaign_name":{"type":"string","maxLength":255,"minLength":1,"title":"Campaign Name"}},"type":"object","required":["scope","campaign_name"],"title":"UseForOutreachRequest","description":"Request body for POST /lists/{id}/use_for_outreach.\n\n`campaign_name` is resolved get-or-create per tenant — the user can pick an\nexisting campaign by exact name or create a new one inline."},"UseForOutreachResult":{"properties":{"campaign_id":{"type":"string","format":"uuid","title":"Campaign Id"},"campaign_name":{"type":"string","title":"Campaign Name"},"contacts_created":{"type":"integer","title":"Contacts Created","default":0},"contacts_already_existed":{"type":"integer","title":"Contacts Already Existed","default":0},"members_added":{"type":"integer","title":"Members Added","default":0},"members_already_existed":{"type":"integer","title":"Members Already Existed","default":0},"skipped":{"type":"integer","title":"Skipped","default":0},"errors":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Errors","default":[]}},"type":"object","required":["campaign_id","campaign_name"],"title":"UseForOutreachResult","description":"Composed result: contact promotion + campaign membership."},"UserInviteRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"full_name":{"type":"string","title":"Full Name"},"role":{"type":"string","title":"Role","default":"member"}},"type":"object","required":["email","full_name"],"title":"UserInviteRequest"},"UserPatch":{"properties":{"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role"},"signature_html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signature Html"},"notify_email_on_assignment":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Notify Email On Assignment"},"notify_email_on_mention":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Notify Email On Mention"},"notify_email_on_task_due":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Notify Email On Task Due"},"digest_cadence":{"anyOf":[{"type":"string","enum":["off","daily","weekly"]},{"type":"null"}],"title":"Digest Cadence"}},"type":"object","title":"UserPatch"},"UserProfileDoc":{"properties":{"role_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Role Title"},"what_for":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"What For"},"focus":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Focus"},"preferences":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Preferences"},"communication_style":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Communication Style"},"summary":{"anyOf":[{"type":"string","maxLength":280},{"type":"null"}],"title":"Summary"},"auto_derived":{"$ref":"#/components/schemas/AutoDerivedFacts"}},"type":"object","title":"UserProfileDoc","description":"The stored user-profile shape (the ``user_profiles.profile`` JSONB blob).\n\n``extra=\"ignore\"`` (not ``forbid``): a shape *read back* from JSONB, so a lenient read\nis the defensive default (mirrors :class:`app.schemas.company_profile.CompanyProfile`).\nAll asked fields optional so a partial synthesis — or a bare auto-derived doc — is\nvalid."},"UserProfileResponse":{"properties":{"profile":{"anyOf":[{"$ref":"#/components/schemas/UserProfileDoc"},{"type":"null"}]},"source":{"anyOf":[{"type":"string","enum":["derived","edited"]},{"type":"null"}],"title":"Source"},"ai_updates_enabled":{"type":"boolean","title":"Ai Updates Enabled","default":true},"last_edited_by":{"anyOf":[{"type":"string","enum":["human","ai"]},{"type":"null"}],"title":"Last Edited By"},"derived_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Derived At"},"edited_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Edited At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"}},"type":"object","title":"UserProfileResponse","description":"``GET`` / ``PATCH /users/me/profile-doc`` — the doc + its provenance + the toggle.\n\nA user who has never derived / edited gets an empty shell at 200 (empty profile, no\nprovenance) so the ``/profile`` UI shows the first-run state rather than a 404."},"UserProfileSynthesizeRequest":{"properties":{"role_title":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Role Title"},"what_for":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"What For"},"focus":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Focus"},"preferences":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Preferences"},"communication_style":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Communication Style"}},"additionalProperties":false,"type":"object","title":"UserProfileSynthesizeRequest","description":"``POST /users/me/profile-doc/synthesize`` input — the user's raw free-text answers.\n\nThe first-run card / the ``/profile`` \"regenerate\" action collects a few short free-text\nanswers; the thin LLM pass structures + summarises them (together with the auto-derived\naccount facts) into a schema-valid doc + a one-line ``summary``. Caps are generous (raw\nanswers); the synthesis polishes them down to the doc's storage caps. At least one\nanswer must be non-empty (the service 409s otherwise — nothing to synthesize from).\n``extra=\"forbid\"`` — a strict input boundary."},"UserProfileUpdate":{"properties":{"profile":{"$ref":"#/components/schemas/UserProfileDoc"},"ai_updates_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Ai Updates Enabled"}},"additionalProperties":false,"type":"object","required":["profile"],"title":"UserProfileUpdate","description":"``PATCH /users/me/profile-doc`` input — a whole-doc replace (+ the toggle).\n\nThe ``/profile`` UI holds the full doc and saves it back (mirrors the company-profile\nedit). ``ai_updates_enabled`` rides along so the toggle can flip in the same save.\n``extra=\"forbid\"`` — a strict input boundary. The ``auto_derived`` facts ride along\n(echoed back from GET) but are **never trusted**: the service always re-snapshots them\nfrom the live account row, overwriting whatever the client sent."},"UserPublic":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"email":{"type":"string","title":"Email"},"full_name":{"type":"string","title":"Full Name"},"role":{"type":"string","title":"Role"},"is_active":{"type":"boolean","title":"Is Active"},"is_platform_admin":{"type":"boolean","title":"Is Platform Admin","default":false},"tenant_ai_quota_exceeded":{"type":"boolean","title":"Tenant Ai Quota Exceeded","default":false},"tenant_onboarding_complete":{"type":"boolean","title":"Tenant Onboarding Complete","default":true},"entitlements":{"anyOf":[{"$ref":"#/components/schemas/EntitlementsView"},{"type":"null"}]},"last_login_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Login At"},"signature_html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signature Html"},"logo_mime_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Mime Type"},"logo_width":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Logo Width"},"logo_height":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Logo Height"},"notify_email_on_assignment":{"type":"boolean","title":"Notify Email On Assignment","default":true},"notify_email_on_mention":{"type":"boolean","title":"Notify Email On Mention","default":true},"notify_email_on_task_due":{"type":"boolean","title":"Notify Email On Task Due","default":true},"digest_cadence":{"type":"string","title":"Digest Cadence","default":"off"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","tenant_id","email","full_name","role","is_active","created_at"],"title":"UserPublic"},"UserSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"full_name":{"type":"string","title":"Full Name"},"email":{"type":"string","title":"Email"},"role":{"type":"string","title":"Role"}},"type":"object","required":["id","full_name","email","role"],"title":"UserSummary"},"UserSummaryForOutreach":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"full_name":{"type":"string","title":"Full Name"},"email":{"type":"string","title":"Email"}},"type":"object","required":["id","full_name","email"],"title":"UserSummaryForOutreach"},"UserUsageResponse":{"properties":{"rows":{"items":{"$ref":"#/components/schemas/UserUsageRowOut"},"type":"array","title":"Rows"}},"type":"object","required":["rows"],"title":"UserUsageResponse"},"UserUsageRowOut":{"properties":{"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"input_tokens":{"type":"integer","title":"Input Tokens"},"output_tokens":{"type":"integer","title":"Output Tokens"},"request_count":{"type":"integer","title":"Request Count"},"estimated_cost_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Estimated Cost Usd"}},"type":"object","required":["user_id","full_name","email","input_tokens","output_tokens","request_count","estimated_cost_usd"],"title":"UserUsageRowOut"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"ViewContext":{"properties":{"entity_type":{"type":"string","enum":["contact","company","deal","campaign"],"title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"}},"type":"object","required":["entity_type","entity_id"],"title":"ViewContext","description":"The record the user is viewing when they send the turn (M-Context-c, GAP-155).\n\nID-only by design (SEC-009 / D2): the server re-resolves the entity under RLS\ninto the identity block — client-supplied record data is never trusted. An id\nthat doesn't resolve (cross-tenant, deleted, unknown) is silently omitted."},"ViewState":{"properties":{"columnVisibility":{"additionalProperties":{"type":"boolean"},"type":"object","title":"Columnvisibility"},"columnOrder":{"items":{"type":"string"},"type":"array","title":"Columnorder"},"sorting":{"items":{"$ref":"#/components/schemas/SortingItem"},"type":"array","title":"Sorting"},"filters":{"additionalProperties":true,"type":"object","title":"Filters"}},"additionalProperties":false,"type":"object","title":"ViewState","description":"Canonical persisted-table-state shape.\n\nTop-level keys are strict (extra=\"forbid\"); `filters` value is loose\nby design — see module docstring."},"WorkflowRuleCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"trigger_entity":{"type":"string","enum":["deal","contact","company","task"],"title":"Trigger Entity"},"trigger_event":{"type":"string","maxLength":64,"minLength":1,"title":"Trigger Event"},"conditions":{"items":{"oneOf":[{"$ref":"#/components/schemas/ConditionPredicate"},{"$ref":"#/components/schemas/AICondition"}]},"type":"array","maxItems":20,"title":"Conditions"},"actions":{"items":{"$ref":"#/components/schemas/ActionSpec"},"type":"array","minItems":1,"title":"Actions"},"is_active":{"type":"boolean","title":"Is Active","default":true}},"additionalProperties":false,"type":"object","required":["name","trigger_entity","trigger_event","actions"],"title":"WorkflowRuleCreate"},"WorkflowRuleList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/WorkflowRuleRead"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"WorkflowRuleList"},"WorkflowRuleRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"trigger_entity":{"type":"string","title":"Trigger Entity"},"trigger_event":{"type":"string","title":"Trigger Event"},"conditions":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Conditions"},"actions":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Actions"},"is_active":{"type":"boolean","title":"Is Active"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","tenant_id","name","description","trigger_entity","trigger_event","conditions","actions","is_active","created_by","created_at","updated_at"],"title":"WorkflowRuleRead"},"WorkflowRuleUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"conditions":{"anyOf":[{"items":{"oneOf":[{"$ref":"#/components/schemas/ConditionPredicate"},{"$ref":"#/components/schemas/AICondition"}]},"type":"array","maxItems":20},{"type":"null"}],"title":"Conditions"},"actions":{"anyOf":[{"items":{"$ref":"#/components/schemas/ActionSpec"},"type":"array","minItems":1},{"type":"null"}],"title":"Actions"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"additionalProperties":false,"type":"object","title":"WorkflowRuleUpdate","description":"Partial update. Trigger is immutable (changing it would orphan\nentity-specific conditions/actions); build a new rule instead."},"WorkflowRunLogPage":{"properties":{"items":{"items":{"$ref":"#/components/schemas/WorkflowRunLogRead"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["items"],"title":"WorkflowRunLogPage"},"WorkflowRunLogRead":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"rule_id":{"type":"string","format":"uuid","title":"Rule Id"},"trigger_event":{"type":"string","title":"Trigger Event"},"resource_type":{"type":"string","title":"Resource Type"},"resource_id":{"type":"string","format":"uuid","title":"Resource Id"},"status":{"type":"string","title":"Status"},"actions_total":{"type":"integer","title":"Actions Total"},"actions_succeeded":{"type":"integer","title":"Actions Succeeded"},"actions_failed":{"type":"integer","title":"Actions Failed"},"detail":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Detail"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","rule_id","trigger_event","resource_type","resource_id","status","actions_total","actions_succeeded","actions_failed","detail","created_at"],"title":"WorkflowRunLogRead"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}}}