{"openapi":"3.1.0","info":{"title":"NovumOS","description":"Block-based kanban where agents are first-class users.","version":"0.0.0"},"paths":{"/v1/auth/google/start":{"get":{"tags":["auth"],"summary":"Google Start","operationId":"google_start_v1_auth_google_start_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/auth/google/callback":{"get":{"tags":["auth"],"summary":"Google Callback","description":"Complete the Authorization-Code + PKCE flow Google bounced back to us.\n\nSpec §4.1.1. Every failure path is a ``302`` to the SPA login with an\n``?oauth_error=`` reason (never a JSON error body) so the user lands\nsomewhere usable. On success we mint the same session state as the\nemail/password path and bounce to ``{app_base_url}{return_to}``.\n\nCSRF is intentionally *not* enforced on this GET — the signed, single-use\n``state`` carried in the ``novumos_oauth`` flow cookie is the CSRF defense.","operationId":"google_callback_v1_auth_google_callback_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/auth/csrf":{"get":{"tags":["auth"],"summary":"Csrf Token","description":"Issue a CSRF token cookie (double-submit). Safe to call before login.","operationId":"csrf_token_v1_auth_csrf_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Csrf Token V1 Auth Csrf Get"}}}}},"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/auth/signup":{"post":{"tags":["auth"],"summary":"Signup","operationId":"signup_v1_auth_signup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/auth/verify-email":{"post":{"tags":["auth"],"summary":"Verify Email","operationId":"verify_email_v1_auth_verify_email_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyEmailRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/auth/login":{"post":{"tags":["auth"],"summary":"Login","operationId":"login_v1_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/auth/logout":{"post":{"tags":["auth"],"summary":"Logout","operationId":"logout_v1_auth_logout_post","responses":{"204":{"description":"Successful Response"}},"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/auth/password-reset/request":{"post":{"tags":["auth"],"summary":"Password Reset Request","operationId":"password_reset_request_v1_auth_password_reset_request_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetRequest"}}},"required":true},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/auth/password-reset/confirm":{"post":{"tags":["auth"],"summary":"Password Reset Confirm","operationId":"password_reset_confirm_v1_auth_password_reset_confirm_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetConfirmRequest"}}},"required":true},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/auth/resend-verification":{"post":{"tags":["auth"],"summary":"Resend Verification","operationId":"resend_verification_v1_auth_resend_verification_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResendVerificationRequest"}}},"required":true},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/me":{"get":{"tags":["me"],"summary":"Get Me","operationId":"get_me_v1_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/me/memberships":{"get":{"tags":["me"],"summary":"List My Memberships","operationId":"list_my_memberships_v1_me_memberships_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_MembershipResponse_"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/me/profile":{"get":{"tags":["me"],"summary":"Get My Profile","operationId":"get_my_profile_v1_me_profile_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]},"patch":{"tags":["me"],"summary":"Update My Profile","operationId":"update_my_profile_v1_me_profile_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProfileRequest"}}},"required":true},"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"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/me/password":{"post":{"tags":["me"],"summary":"Change My Password","operationId":"change_my_password_v1_me_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordRequest"}}},"required":true},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/me/password/set":{"post":{"tags":["me"],"summary":"Set My Initial Password","operationId":"set_my_initial_password_v1_me_password_set_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPasswordRequest"}}},"required":true},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/me/sessions":{"get":{"tags":["me"],"summary":"List My Sessions","operationId":"list_my_sessions_v1_me_sessions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_UserSessionResponse_"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]},"delete":{"tags":["me"],"summary":"Revoke All My Sessions","operationId":"revoke_all_my_sessions_v1_me_sessions_delete","responses":{"204":{"description":"Successful Response"}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/me/sessions/{session_id}":{"delete":{"tags":["me"],"summary":"Revoke My Session","operationId":"revoke_my_session_v1_me_sessions__session_id__delete","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Session Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/me/preferences":{"get":{"tags":["me"],"summary":"Get My Preferences","operationId":"get_my_preferences_v1_me_preferences_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPreferencesResponse"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]},"patch":{"tags":["me"],"summary":"Update My Preferences","operationId":"update_my_preferences_v1_me_preferences_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePreferencesRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPreferencesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/me/surveys":{"get":{"tags":["me"],"summary":"Get My Survey Eligibility","description":"Which triggered surveys may interrupt this person right now (§25.9).\n\nLives under ``/v1/me`` rather than ``/v1/feedback`` because the answer is a\nfact about the *caller* — their frequency caps, their activity, their\nworkspace — and because the write half (``PATCH /v1/me/preferences``) is\nalready here. Keeping the read and the write on the same resource is what\nstops the cap from acquiring two homes.\n\nThe org half of the NPS decision is evaluated against the request's\nworkspace context, so this takes ``get_workspace_principal`` rather than\n``get_principal`` (§4.8): the active workspace is a property of the REQUEST,\nand one account may hold memberships in several. Its sibling\n``/me/preferences`` correctly does not — a preference is user-global —\nwhich is exactly the distinction that makes the choice worth stating.\nA principal with no workspace simply isn't NPS-eligible.\n\nNothing here reads another user's or another org's rows: the preferences\nblob comes off the caller's own ``User`` and the activity count is filtered\non both ``org_id`` and the caller's ``user_id``.","operationId":"get_my_survey_eligibility_v1_me_surveys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SurveyEligibilityResponse"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}]}},"/v1/me/invitations":{"get":{"tags":["invitations"],"summary":"List My Invitations","description":"Live invitations addressed to the signed-in account, across every workspace.\n\nThis is the in-app half of §4.8's \"if the address already has an account,\nthe invitation surfaces in-app on their next load *and* by email\" — the\nemail can be lost, filtered, or read on a phone, and the invitation still\nhas to be findable. Each row carries ``org_name`` because the caller does\nnot belong to the inviting workspace and has no other way to learn what they\nare being asked to join.\n\nOnly pending, unexpired invitations appear; the list never offers something\naccept would refuse.","operationId":"list_my_invitations_v1_me_invitations_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_InvitationResponse_"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/invitations/{invitation_id}:accept":{"post":{"tags":["invitations"],"summary":"Accept Invitation","description":"Accept an invitation — the moment the membership and the seat exist (§4.8).\n\nRequires being signed in as the invited address; anything else is ``404``,\nnever ``403``, so a guessed id tells a stranger nothing. Returns the\nmembership just created, which is what the client needs to switch into the\nnew workspace.\n\n``402 plan_limit_reached`` when accepting would breach the workspace's free\n-tier seat cap: pending invitations hold no seat, so the cap is only truly\ndecidable here, inside the per-org seat lock.","operationId":"accept_invitation_v1_invitations__invitation_id__accept_post","parameters":[{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Invitation Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MembershipResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/invitations/{invitation_id}:decline":{"post":{"tags":["invitations"],"summary":"Decline Invitation","description":"Decline an invitation. Terminal, and it never touched a seat (§4.8).\n\nSame authorization as accept. Declining is deliberately a real, recorded\naction rather than an ignore: the workspace's admin sees the pending row\nclear, and the ``(org_id, lower(email))`` uniqueness slot frees up so they\ncan invite again later.","operationId":"decline_invitation_v1_invitations__invitation_id__decline_post","parameters":[{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Invitation Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/notifications":{"get":{"tags":["notifications"],"summary":"Get Notifications","description":"List the caller's notifications, newest first (spec §22.2).","operationId":"get_notifications_v1_notifications_get","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Org Id"}},{"name":"unread","in":"query","required":false,"schema":{"type":"boolean","description":"Only return unread notifications.","default":false,"title":"Unread"},"description":"Only return unread notifications."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Notifications V1 Notifications Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/notifications/count":{"get":{"tags":["notifications"],"summary":"Get Unread Count","description":"Unread notification count for the caller (spec §22.2).","operationId":"get_unread_count_v1_notifications_count_get","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadCountResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/notifications/{notification_id}:read":{"post":{"tags":["notifications"],"summary":"Mark Notification Read","description":"Mark one of the caller's notifications read. 404 if it isn't theirs.","operationId":"mark_notification_read_v1_notifications__notification_id__read_post","parameters":[{"name":"notification_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Notification Id"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/notifications:readAll":{"post":{"tags":["notifications"],"summary":"Mark All Notifications Read","description":"Mark every unread notification for the caller read (spec §22.2).","operationId":"mark_all_notifications_read_v1_notifications_readAll_post","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Mark All Notifications Read V1 Notifications Readall Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/feedback":{"post":{"tags":["feedback"],"summary":"Submit Feedback","description":"Submit one feedback report (spec §25.4).\n\nThe share-link refusal, the workspace resolution and the hourly caps all run\nin ``require_submit_context`` — a dependency, so they run before the body is\nvalidated (see the module docstring). It hands back the resolved ``org_id``,\nwhich is what the row is stamped with.\n\n``surface`` is derived from HOW the caller authenticated, not claimed by\nthem: an integration token is the ``api`` surface, a browser session is\n``web``. (``mcp`` is set by the §25.11 tools.)","operationId":"submit_feedback_v1_feedback_post","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The workspace this call acts in. Browser sessions may name any org the user is a live member of; integration tokens stay pinned to their own.","title":"Org Id"},"description":"The workspace this call acts in. Browser sessions may name any org the user is a live member of; integration tokens stay pinned to their own."},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFeedbackRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"get":{"tags":["feedback"],"summary":"List Own Feedback","description":"List the caller's OWN submissions, newest first (spec §25.4).","operationId":"list_own_feedback_v1_feedback_get","parameters":[{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by feedback type.","title":"Type"},"description":"Filter by feedback type."},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by triage status.","title":"Status"},"description":"Filter by triage status."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The workspace this call acts in. Browser sessions may name any org the user is a live member of; integration tokens stay pinned to their own.","title":"Org Id"},"description":"The workspace this call acts in. Browser sessions may name any org the user is a live member of; integration tokens stay pinned to their own."},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Own Feedback V1 Feedback Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/feedback/screenshots:presign":{"post":{"tags":["feedback"],"summary":"Presign Feedback Screenshot","description":"Mint a presigned POST for a feedback screenshot (spec §25.4).\n\nBody: ``{filename, content_type, size_bytes?}``. Returns the standard file\nenvelope plus ``upload_url`` + ``upload_url_fields`` + ``upload_url_method``\n(POST — the multipart form is what carries the ``content-length-range``\npolicy). The client posts the bytes straight to S3, calls\n``POST /v1/feedback/screenshots/{id}:commit`` below to flip the row to\n``available``, and then passes the id as ``screenshot_file_id`` on\n``POST /v1/feedback``.\n\nOrdering is ``submit_feedback``'s, because it is the same dependency: the\nshare-link refusal, the workspace resolution and the hourly cap all run in\n``require_screenshot_presign_context`` — before the body is validated, which\nis what makes the cap count malformed requests too.\n\nThe ``org_id`` the row is stamped with is the one the dependency RESOLVED,\nnot ``principal.org_id``. ``?org_id=`` hints are honored here as everywhere\non this router, so the principal's org is the cookie's possibly-stale active\nworkspace; stamping from it would file the object under one org while the\nrequest ran scoped to another.","operationId":"presign_feedback_screenshot_v1_feedback_screenshots_presign_post","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The workspace this call acts in. Browser sessions may name any org the user is a live member of; integration tokens stay pinned to their own.","title":"Org Id"},"description":"The workspace this call acts in. Browser sessions may name any org the user is a live member of; integration tokens stay pinned to their own."},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFeedbackScreenshotUploadRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Presign Feedback Screenshot V1 Feedback Screenshots Presign Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/feedback/screenshots/{file_id}:commit":{"post":{"tags":["feedback"],"summary":"Commit Feedback Screenshot","description":"Finalize a feedback screenshot upload (spec §25.4, NOV-442 review F1).\n\nThe screenshot's own commit rather than the shared ``POST\n/v1/files/{id}:commit``, because the two legs of one chain have to agree\nabout which workspace they are in. The presign resolves ``?org_id=`` and\nstamps the row with the SELECTED workspace; the shared route resolves its\norg from ``principal.org_id``, which for a browser session is the cookie's\npossibly-stale active org. For a multi-workspace user those differ, and the\nchain died at this step — presign 201 in B, commit 404 in A, the row left\n``pending`` forever with its object orphaned in S3. Mounting the commit on\n``/v1/feedback/*`` puts it behind the same resolution as its siblings, so\nthe disagreement cannot arise.\n\nThe alternative — teaching the shared route to take the hint — was\ndeliberately not taken: it would extend ``?org_id=`` workspace selection\nover the entire file surface to fix one route. This one accepts the hint\nonly for rows the feedback presign minted (anything else is a 404, in\n:func:`app.services.files.commit_feedback_screenshot_for_principal`).\n\nRate limit: its own ``feedback_upload_commit`` per-user window — see\n``require_screenshot_commit_context`` for why it is a third key rather than\na second charge against the presign's.\n\nPast_due: this path is the §25.4 carve-out from the §18.8 write-block, and\nsince NOV-442's review it is the ONLY commit path that carries one. The\nshared route went back to being blocked for every producer, screenshots\nincluded — a read-only workspace finishes its screenshot here.","operationId":"commit_feedback_screenshot_v1_feedback_screenshots__file_id__commit_post","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"File Id"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The workspace this call acts in. Browser sessions may name any org the user is a live member of; integration tokens stay pinned to their own.","title":"Org Id"},"description":"The workspace this call acts in. Browser sessions may name any org the user is a live member of; integration tokens stay pinned to their own."},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Commit Feedback Screenshot V1 Feedback Screenshots  File Id  Commit Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/feedback/{feedback_id}":{"get":{"tags":["feedback"],"summary":"Get Own Feedback","description":"One of the caller's own submissions. Someone else's → 404, never 403.","operationId":"get_own_feedback_v1_feedback__feedback_id__get","parameters":[{"name":"feedback_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Feedback Id"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The workspace this call acts in. Browser sessions may name any org the user is a live member of; integration tokens stay pinned to their own.","title":"Org Id"},"description":"The workspace this call acts in. Browser sessions may name any org the user is a live member of; integration tokens stay pinned to their own."},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/email-outbox/{outbox_id}:requeue":{"post":{"tags":["email-outbox"],"summary":"Requeue Email Outbox Endpoint","description":"Re-drive one failed row. Owner/admin only; repeated requests are safe.","operationId":"requeue_email_outbox_endpoint_v1_orgs__org_id__email_outbox__outbox_id__requeue_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"outbox_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Outbox Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailOutboxRequeueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs":{"get":{"tags":["orgs"],"summary":"List Orgs","operationId":"list_orgs_v1_orgs_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_OrganizationResponse_"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]},"post":{"tags":["orgs"],"summary":"Create Org","operationId":"create_org_v1_orgs_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrganizationRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/orgs/{org_id}":{"get":{"tags":["orgs"],"summary":"Get Org","operationId":"get_org_v1_orgs__org_id__get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"patch":{"tags":["orgs"],"summary":"Update Org","operationId":"update_org_v1_orgs__org_id__patch","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrganizationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}:switch":{"post":{"tags":["orgs"],"summary":"Switch Org","description":"Set ``org_id`` as the caller's active org and re-issue the session cookie.\n\nNOV-119: the org switcher was purely client-side (localStorage + the\n``/v1/orgs/{org_id}`` URL path hint). That left the session cookie's\n``org_id`` claim stale, so requests to routes with no org_id in the path\nresolved to the *old* (or no) org. This server-side switch re-stamps the\ncookie with the selected org so the active-org choice is durable across the\nwhole API surface, not just org-scoped paths.\n\nNOV-527 (§4.8): the switch now also persists ``user_sessions.active_org_id``,\nso the choice survives a reload instead of depending on a cookie the client\ncannot read. The distinction that matters: this endpoint sets the DEFAULT\nworkspace for the next fresh page load; the ``X-Novum-Org`` header is what\nmakes an individual request act elsewhere. A tab that switches workspaces\nmust not silently retarget every OTHER tab's writes, which is exactly what\nre-stamping a process-global cookie would do on its own.\n\n``require_org_access`` gates on real membership in ``org_id`` (404 for\nnon-members, never leaking org existence), so a caller can only switch into\nan org they actually belong to. The re-issued cookie carries that org's\nreal role. No-op cookie-wise for integration tokens (single-org by\ndefinition) — they get the org body back without a cookie.","operationId":"switch_org_v1_orgs__org_id__switch_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/members":{"get":{"tags":["orgs"],"summary":"List Members","operationId":"list_members_v1_orgs__org_id__members_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_MembershipResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"post":{"tags":["orgs"],"summary":"Add Member","operationId":"add_member_v1_orgs__org_id__members_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMemberRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MembershipResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/members/{user_id}:resend-invite":{"post":{"tags":["orgs"],"summary":"Resend Member Invite","description":"Resend the verification / invite email for an org member.\n\nAdmin/owner-only. The endpoint inspects the target user's state and\npicks the right email template:\n  - email_verified=False → mint a fresh verification token and send\n    the `send_invite_new_user` template (the user finishes verifying\n    + setting up their account from the link).\n  - email_verified=True (already verified, just hasn't logged in\n    yet) → send `send_invite_existing_user` so they get a nudge with\n    a link back to the workspace.\n\nRate limit: once per minute per (org, target user). Returns 429 with\na clear retry message past that ceiling. Distinct from the\nuser-self path at `/v1/auth/resend-verification` (NOV-83 ticket).","operationId":"resend_member_invite_v1_orgs__org_id__members__user_id__resend_invite_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MembershipResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/members/{user_id}":{"patch":{"tags":["orgs"],"summary":"Update Member Role","operationId":"update_member_role_v1_orgs__org_id__members__user_id__patch","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMembershipRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MembershipResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["orgs"],"summary":"Remove Member","operationId":"remove_member_v1_orgs__org_id__members__user_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/members/me":{"delete":{"tags":["orgs"],"summary":"Leave Org","description":"Leave this workspace (§4.8 invariant 6, \"leaving is self-service\").\n\nThe counterpart to ``DELETE /{org_id}/members/{user_id}``: that one is an\nadmin removing somebody else and is owner/admin-only, which is precisely why\na member invited into a client's workspace previously had no way out. This\none takes no role — every member may leave — but the sole remaining owner\ngets a ``409`` and has to transfer ownership first, because §4.4's \"there is\nalways ≥1 owner\" must hold through every path.\n\nDeclared BEFORE the ``/{user_id}`` route: ``me`` is not a UUID, so without\nthis ordering FastAPI would try the parameterized route first and reject the\nliteral at path-parsing time.","operationId":"leave_org_v1_orgs__org_id__members_me_delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/invitations":{"post":{"tags":["orgs"],"summary":"Create Invitation","description":"Invite an email address into this workspace. Owner/admin only.\n\nThe consented replacement for ``POST /{org_id}/members`` on the human path\n(§4.8 invariant 5): this writes a *pending invitation*, not a membership, so\nnobody's account acquires a second employer without their say-so. No seat is\nconsumed here — that happens at accept.\n\nStatus codes carry the outcome: **201** for a new invitation, **200** for a\nresend of a live one (the token is rotated, so the previous link dies) and\nfor the \"they're already a member\" no-op, which per §4.8 is a no-op rather\nthan a duplicate row or an error.\n\nThe response is identical whether or not the address has a NovumOS account —\ninviting somebody must not become a way to test which addresses are\nregistered.","operationId":"create_invitation_v1_orgs__org_id__invitations_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvitationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/InvitationResponse"},{"$ref":"#/components/schemas/AlreadyMemberResponse"}],"title":"Response Create Invitation V1 Orgs  Org Id  Invitations Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"get":{"tags":["orgs"],"summary":"List Invitations","description":"List this workspace's invitations. Owner/admin only.\n\nDefaults to the pending slice — the actionable one. ``?status=`` selects one\nother state, and ``?status=all`` returns the full history (terminal rows are\nkept forever). Admin-only because the list is a directory of addresses the\nworkspace has approached.","operationId":"list_invitations_v1_orgs__org_id__invitations_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"default":"pending","title":"Status"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_InvitationResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/invitations/{invitation_id}":{"delete":{"tags":["orgs"],"summary":"Revoke Invitation","description":"Withdraw a pending invitation. Owner/admin only.\n\nTerminal and seatless — a revoked invitation never created a membership, so\nit never held a seat to release. The emailed token stops resolving the\nmoment the row leaves ``pending``.","operationId":"revoke_invitation_v1_orgs__org_id__invitations__invitation_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Invitation Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/integrations":{"get":{"tags":["orgs"],"summary":"List Integrations","operationId":"list_integrations_v1_orgs__org_id__integrations_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_IntegrationResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"post":{"tags":["orgs"],"summary":"Create Integration","operationId":"create_integration_v1_orgs__org_id__integrations_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateIntegrationRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateIntegrationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/integrations/{integration_id}":{"delete":{"tags":["orgs"],"summary":"Revoke Integration","description":"Revoke (hard-delete) an integration token (NOV-190 / spec §4.5).\n\nWorks for BOTH agent tokens (``user_id`` set) and raw tokens\n(``user_id IS NULL``). Org-scoped — an integration in another org 404s, so\nthere is no cross-tenant revoke. Revoking an agent's last token leaves the\nagent User / Membership / seat intact; killing the agent itself is a member\ndelete, not a token revoke.\n\nHARD delete, deliberately: ``AuditMixin`` carries ``in_trash`` / ``archived``\nflags, but the token-auth lookup (``_principal_from_integration_token``)\ndoes NOT filter on them — a soft-deleted row would still authenticate. Hard\ndelete is the only way to actually disable the credential. No seat\nrecompute: a token is not a seat (spec §18.1).","operationId":"revoke_integration_v1_orgs__org_id__integrations__integration_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"integration_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Integration Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/agents":{"post":{"tags":["orgs"],"summary":"Connect Agent","description":"Atomically connect an agent: User(kind='agent') + Membership + Integration.\n\nThe agent appears in assignee pickers and shows up with the\nsparkle badge on its avatar disc (NOV-92). The integration's\nbearer token is shown once in the response — the admin pastes it\ninto the agent runtime; we never serve it again.","operationId":"connect_agent_v1_orgs__org_id__agents_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectAgentRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectAgentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/agents/{agent_user_id}/tokens":{"post":{"tags":["orgs"],"summary":"Add Agent Token","description":"Mint an ADDITIONAL credential for an existing agent (NOV-190 / spec §4.2, §4.5).\n\nTokens are children of an agent: an agent can own several (rotation, a\nmulti-Lambda fleet). The new ``Integration`` carries ``user_id=agent_user_id``\nso bearer-auth resolves to the same agent identity as its first token. The\nplaintext token is shown once.\n\nNO seat recompute: adding a token to an existing agent does not change the\nbillable count — the agent already counts as its (possibly free) seat; a\ntoken is a credential, not a member (spec §18.1). So this path skips the\nseat lock / recompute / Stripe sync that ``connect_agent`` runs.\n\nThe target must be a ``User(kind='agent')`` with a Membership in THIS org,\nor we 404 (no existence leak across tenants).","operationId":"add_agent_token_v1_orgs__org_id__agents__agent_user_id__tokens_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"agent_user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent User Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAgentTokenRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateIntegrationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/agents/{agent_user_id}:pause":{"post":{"tags":["orgs"],"summary":"Pause Agent","description":"Kill switch — pause an agent instantly (NOV-264 / spec §4.7).\n\nThin caller over :func:`app.services.agent_admin.pause_agent_for_principal`\n(shared with the ``pause_agent`` MCP tool). Available on ALL plans — safety\nis never paywalled. Owner/admin only.","operationId":"pause_agent_v1_orgs__org_id__agents__agent_user_id__pause_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"agent_user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent User Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/PauseAgentRequest"},{"type":"null"}],"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentKillSwitchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/agents/{agent_user_id}:resume":{"post":{"tags":["orgs"],"summary":"Resume Agent","description":"Reverse a pause — re-admit the agent (NOV-264 / spec §4.7).\n\nThin caller over :func:`app.services.agent_admin.resume_agent_for_principal`\n(shared with the ``resume_agent`` MCP tool). Owner/admin only.","operationId":"resume_agent_v1_orgs__org_id__agents__agent_user_id__resume_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"agent_user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent User Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentKillSwitchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/agents/{agent_user_id}:revoke_tokens":{"post":{"tags":["orgs"],"summary":"Revoke Agent Tokens","description":"Hard kill — irreversibly delete ALL of an agent's tokens (NOV-264 / spec §4.7).\n\nUnlike :pause (reversible), this removes credentials outright: every\n``Integration`` the agent owns in this org is hard-deleted (same rationale as\n``revoke_integration`` — a soft-deleted row would still authenticate). It\ndoes NOT set paused/disabled and does NOT recompute seats (a token is not a\nseat, spec §18.1). Owner/admin only.","operationId":"revoke_agent_tokens_v1_orgs__org_id__agents__agent_user_id__revoke_tokens_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"agent_user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent User Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeAgentTokensResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/agents/{agent_user_id}/budget":{"get":{"tags":["orgs"],"summary":"Get Agent Budget","description":"Read an agent's budget caps + read-only kill-switch state + live usage.\n\nThin caller over :func:`app.services.agent_admin.get_agent_budget_for_principal`\n(shared with the ``get_agent_budget`` MCP tool). GATED to Scale/Agency\n(entitlement checked before the owner/admin role check); owner/admin only;\nthe target must be a ``kind='agent'`` member of this org. ``board_id`` absent\n= the org-wide budget (unchanged); present = the per-board override (NOV-271).","operationId":"get_agent_budget_v1_orgs__org_id__agents__agent_user_id__budget_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"agent_user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent User Id"}},{"name":"board_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentBudgetResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"put":{"tags":["orgs"],"summary":"Set Agent Budget","description":"Set an agent's caps (PUT-replace; ``null`` clears a cap).\n\nThin caller over :func:`app.services.agent_admin.set_agent_budget_for_principal`\n(shared with the ``set_agent_budget`` MCP tool). GATED to Scale/Agency;\nowner/admin only; agent-target only. Writes an ``agent.budget_updated`` audit\nrow. Validation (``null`` or ``>=0``) is enforced by the request schema.\n``board_id`` absent = the org-wide budget (unchanged); present = the per-board\noverride (NOV-271; enforces writes/day only, per-board req/s deferred).","operationId":"set_agent_budget_v1_orgs__org_id__agents__agent_user_id__budget_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"agent_user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent User Id"}},{"name":"board_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentBudgetUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentBudgetResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/agents/activity":{"get":{"tags":["orgs"],"summary":"Get Org Agents Activity","description":"All-agents org (or per-client-board) activity rollup (NOV-266 / spec §4.7 ledger).\n\nThin caller over :func:`app.services.agent_admin.get_agent_activity_for_principal`\nwith ``agent_user_id=None`` (the all-agents rollup — no agent-target guard).\nShared with the ``get_agent_activity`` MCP tool. GATED to Scale/Agency;\nowner/admin only.","operationId":"get_org_agents_activity_v1_orgs__org_id__agents_activity_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"window","in":"query","required":false,"schema":{"type":"string","default":"24h","title":"Window"}},{"name":"board_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Limit"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentActivityResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/agents/{agent_user_id}/activity":{"get":{"tags":["orgs"],"summary":"Get Agent Activity","description":"Per-agent buyer-facing activity ledger — summary + reverse-chron feed (spec §4.7).\n\nThin caller over :func:`app.services.agent_admin.get_agent_activity_for_principal`\n(shared with the ``get_agent_activity`` MCP tool). GATED to Scale/Agency;\nowner/admin only; the target must resolve to a ``kind='agent'`` member of this\norg. ``window`` is ``24h`` or ``7d`` (400 otherwise); ``board_id`` scopes to\none client board.","operationId":"get_agent_activity_v1_orgs__org_id__agents__agent_user_id__activity_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"agent_user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent User Id"}},{"name":"window","in":"query","required":false,"schema":{"type":"string","default":"24h","title":"Window"}},{"name":"board_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Limit"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentActivityResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/presence/agents":{"get":{"tags":["orgs"],"summary":"List Live Agents","description":"Snapshot of agent Users with a live WS connection in ``org_id``.\n\nThe frontend's live-agents badge hits this on mount to hydrate before\nits WS opens (and starts streaming ``presence.agent.connected`` /\n``.disconnected`` events). Membership in ``org_id`` is the gate for the\nbaseline roles — every member sees the same snapshot.\n\nNOV-531 (§4.4): agents are Users (§4.5), so a presence roster IS a member\nroster. It is narrowed by the same directory rule as\n``GET /v1/orgs/{id}/members`` — a guest sees only the agents they share a\nreachable board with, never the org's whole agent fleet.","operationId":"list_live_agents_v1_orgs__org_id__presence_agents_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_AgentPresence_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/domains":{"get":{"tags":["orgs"],"summary":"List Domains","operationId":"list_domains_v1_orgs__org_id__domains_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_DomainClaimResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"post":{"tags":["orgs"],"summary":"Claim Domain","operationId":"claim_domain_v1_orgs__org_id__domains_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDomainClaimRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainClaimResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/domains/{domain}:verify":{"post":{"tags":["orgs"],"summary":"Verify Domain","description":"Check the org's DNS TXT record and stamp verification on success (§4.3.1).\n\nOwner/admin only. Looks up `_novumos-verify.<domain>` and compares each TXT\nvalue against the claim's verification_code (exact, case-sensitive). On a\nmatch: stamp verified_at, write a `domain.verified` audit event. On any miss\nor DNS failure: leave the claim unverified and return it (never 5xx).\n`google_workspace` claims use the same DNS-TXT check in v1.","operationId":"verify_domain_v1_orgs__org_id__domains__domain__verify_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainClaimResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/domains/{domain}":{"delete":{"tags":["orgs"],"summary":"Release Domain","description":"Release this org's claim on `domain` (NOV-603, §4.3.1).\n\nOwner/admin only, and scoped to `(org_id, domain)` — a member of org A can\nnever delete org B's claim; a claim that isn't this org's is a `404`, not a\n`403`, so the endpoint doesn't confirm the row exists elsewhere.\n\nReleasing a VERIFIED claim does **not** remove members who already\nauto-joined: they are members of the org on their own account now, and\nsilently ejecting people because an admin tidied up a DNS record would be a\nfar bigger surprise than leaving them. It only stops FUTURE auto-joins, and\nfrees the domain for another org to verify.","operationId":"release_domain_v1_orgs__org_id__domains__domain__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/billing":{"get":{"tags":["billing"],"summary":"Get Billing","description":"Return the org's billing status object (spec §18.9).\n\nOwner-only. ``billable_seats`` and the humans/agents ``seat_breakdown`` are\nrecomputed **live** via :func:`seat_breakdown` — never the stale\n``billing_seats`` cache; the total derives from the breakdown's counts so\nthey agree by construction (the first-agent-free math is server-side). On\na free org, ``current_period_end``/``cancel_at_period_end`` are null and\n``has_payment_method`` is always false (spec §18.9 invariant), even if the\norg retains a ``stripe_customer_id`` from a prior Pro subscription (§18.7\nonly clears the subscription id on cancel). On Pro, ``has_payment_method``\nis derived offline from the Stripe customer id; no live Stripe call is made.","operationId":"get_billing_v1_orgs__org_id__billing_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/billing:checkout":{"post":{"tags":["billing"],"summary":"Checkout","description":"Start a Stripe Checkout to upgrade the org to Pro (spec §18.6 / §18.5).\n\nOwner-only (``admin`` is not sufficient — §18.1). Accepts an optional\n``{\"interval\": \"annual\"|\"monthly\"}`` body (NOV-242) selecting the billing\ncadence; an omitted body defaults to ``annual`` (the promoted option, §18.1).\nReturns a ``{\"checkout_url\": …}`` the owner is redirected to. Errors:\n\n* ``409`` if the org is already Pro (nothing to check out).\n* ``503 billing_unconfigured`` if Stripe isn't configured (dev has no keys),\n  surfaced by :func:`~app.core.stripe_client.get_stripe`.\n\nThe plan flip to Pro is **webhook-driven** (``checkout.session.completed`` →\n§18.7), never set here. The router stays thin; the Customer-create /\nsession-create logic lives in :func:`create_checkout_session`.","operationId":"checkout_v1_orgs__org_id__billing_checkout_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CheckoutRequest"},{"type":"null"}],"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/billing:portal":{"post":{"tags":["billing"],"summary":"Portal","description":"Open a Stripe Customer Portal session for the org (spec §18.6).\n\nOwner-only (``admin`` is not sufficient — §18.1). Returns a\n``{\"portal_url\": …}`` the owner is redirected to. Errors:\n\n* ``409 no_subscription`` if the org has no ``stripe_customer_id`` (must\n  complete Checkout first to create the Stripe Customer).\n* ``503 billing_unconfigured`` if Stripe isn't configured (dev has no keys),\n  surfaced by :func:`~app.core.stripe_client.get_stripe`.\n\nThe router stays thin; the portal session logic lives in\n:func:`create_portal_session`.","operationId":"portal_v1_orgs__org_id__billing_portal_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/automations":{"get":{"tags":["automations"],"summary":"List Automations Endpoint","description":"List the org's automations, newest first (keyset pagination). Admin/owner-only.","operationId":"list_automations_endpoint_v1_orgs__org_id__automations_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_AutomationResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"post":{"tags":["automations"],"summary":"Create Automation Endpoint","description":"Create an automation rule (§20.8). Admin/owner-only.\n\nThe body's ``trigger`` / ``actions`` are validated (event_type ∈\nKNOWN_EVENT_TYPES; non-empty actions with known ``type``) — the schema rejects\na malformed body with 422 before the service, which re-validates for parity\nwith the MCP path. A ``notify`` action's ``params`` may set ``recipient``\n(``owner`` default | ``assignees``) and ``channels`` (any of ``comment``\ndefault | ``in_app`` | ``email``); unknown values are rejected at write time\n(422). Omitting both keys preserves the historical comment-to-owner behavior.","operationId":"create_automation_endpoint_v1_orgs__org_id__automations_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAutomationRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/automations/{automation_id}":{"get":{"tags":["automations"],"summary":"Get Automation Endpoint","description":"Fetch one automation. Admin/owner-only. Cross-org → ``404`` (no leak).","operationId":"get_automation_endpoint_v1_orgs__org_id__automations__automation_id__get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"automation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Automation Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"patch":{"tags":["automations"],"summary":"Update Automation Endpoint","description":"Update / toggle an automation (§20.8). Admin/owner-only.\n\nOnly the fields the client sent are changed (``model_dump(exclude_unset=True)``\nforwards an explicit ``null`` but not an absent key). ``is_active`` supports the\nenable/disable toggle.","operationId":"update_automation_endpoint_v1_orgs__org_id__automations__automation_id__patch","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"automation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Automation Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAutomationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["automations"],"summary":"Delete Automation Endpoint","description":"Delete an automation (§20.8). Admin/owner-only. Cross-org → ``404``.","operationId":"delete_automation_endpoint_v1_orgs__org_id__automations__automation_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"automation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Automation Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/automations/{automation_id}/runs":{"get":{"tags":["automations"],"summary":"List Automation Runs Endpoint","description":"List one automation's run ledger, newest first (§20.8). Admin/owner-only.","operationId":"list_automation_runs_endpoint_v1_orgs__org_id__automations__automation_id__runs_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"automation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Automation Id"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_AutomationRunResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/automations/{automation_id}/runs/{run_id}:replay":{"post":{"tags":["automations"],"summary":"Replay Automation Run Endpoint","description":"Re-arm a terminal-failed run (§20.6.2). Admin/owner-only.\n\nResets the run to ``pending`` + ``next_retry_at=now()`` in place (dedup_key\npreserved, NO new row); a non-terminal-failed run → ``409``; a run under\nanother automation/org → ``404``. The executor (NOV-281) drains the reset run.","operationId":"replay_automation_run_endpoint_v1_orgs__org_id__automations__automation_id__runs__run_id__replay_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"automation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Automation Id"}},{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Run Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationRunResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/playbooks":{"get":{"tags":["playbooks"],"summary":"List Playbooks Endpoint","description":"List the org's Playbooks, newest first (keyset pagination). Admin/owner-only.","operationId":"list_playbooks_endpoint_v1_orgs__org_id__playbooks_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_PlaybookResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"post":{"tags":["playbooks"],"summary":"Create Playbook Endpoint","description":"Create a Playbook (§23.4). Admin/owner-only.\n\nThe body's ``steps`` (non-empty, each with a ``prompt_md``) and ``output_target``\n(``append_body`` | ``replace_body``) are validated — the schema rejects a\nmalformed body with 422 before the service, which re-validates for parity with\nthe MCP path. ``on_success_move_to`` is the column a card advances to on success.","operationId":"create_playbook_endpoint_v1_orgs__org_id__playbooks_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePlaybookRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybookResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/playbooks/{playbook_id}":{"get":{"tags":["playbooks"],"summary":"Get Playbook Endpoint","description":"Fetch one Playbook. Admin/owner-only. Cross-org → ``404`` (no leak).","operationId":"get_playbook_endpoint_v1_orgs__org_id__playbooks__playbook_id__get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"playbook_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Playbook Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybookResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"patch":{"tags":["playbooks"],"summary":"Update Playbook Endpoint","description":"Update a Playbook (§23.4). Admin/owner-only.\n\nOnly the fields the client sent are changed (``model_dump(exclude_unset=True)``\nforwards an explicit ``null`` but not an absent key). ``steps`` / ``output_target``\nare re-validated.","operationId":"update_playbook_endpoint_v1_orgs__org_id__playbooks__playbook_id__patch","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"playbook_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Playbook Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePlaybookRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybookResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["playbooks"],"summary":"Delete Playbook Endpoint","description":"Delete a Playbook (§23.4). Admin/owner-only. Cross-org → ``404``.","operationId":"delete_playbook_endpoint_v1_orgs__org_id__playbooks__playbook_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"playbook_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Playbook Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/ai-run-usage":{"get":{"tags":["autopilot"],"summary":"List Ai Run Usage Endpoint","description":"List the org's Autopilot run history, newest first (§23.6). Admin/owner-gated.\n\nOptional ``board_id`` / ``column_id`` / ``playbook_id`` narrow the feed to a\nsingle board, Autopilot column, or Playbook.","operationId":"list_ai_run_usage_endpoint_v1_orgs__org_id__ai_run_usage_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"board_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"}},{"name":"column_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"}},{"name":"playbook_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Playbook Id"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_AiRunUsageResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/import-jobs":{"post":{"tags":["import-jobs"],"summary":"Create Import Job Endpoint","description":"Create + start an import (spec §21.9). Admin/owner-only.\n\n``notion_api`` (default): body carries the transient Notion ``token`` (§21.10 —\nbody only, never a query string) plus ``database_id`` / ``whole_workspace`` and\noptional ``column_property``; the token is registered in the process-local store\nso the worker can claim it. ``export_file`` (NOV-297): body carries ``file_id``\nof a committed upload (from ``:upload`` → ``:commit``; usable as soon as the\nfile is ``status=available``) plus optional ``column_property``. Returns the\njob WITHOUT any token.","operationId":"create_import_job_endpoint_v1_orgs__org_id__import_jobs_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateImportJobRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportJobResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"get":{"tags":["import-jobs"],"summary":"List Import Jobs Endpoint","description":"List the org's import jobs, newest first (keyset pagination). Admin/owner-only.","operationId":"list_import_jobs_endpoint_v1_orgs__org_id__import_jobs_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ImportJobResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/import-jobs:upload":{"post":{"tags":["import-jobs"],"summary":"Create Import Upload Endpoint","description":"Mint a presigned POST for a Notion export ``.zip`` (NOV-297). Admin/owner-only.\n\nReturns the standard file envelope (``upload_url`` + form fields). Flow:\n``:upload`` → PUT bytes to S3 → ``POST /v1/files/{id}:commit`` → ``POST\n.../import-jobs`` with ``source=export_file`` + this ``file_id`` (the file is\nusable as soon as ``:commit`` returns ``status=available``). Only\n``application/zip`` is accepted (415 otherwise).","operationId":"create_import_upload_endpoint_v1_orgs__org_id__import_jobs_upload_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateImportUploadRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Import Upload Endpoint V1 Orgs  Org Id  Import Jobs Upload Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/import-jobs/{job_id}":{"get":{"tags":["import-jobs"],"summary":"Get Import Job Endpoint","description":"Return one import job's status/counts/error_log/board_id (the wizard polls this).\n\nAdmin/owner-only. A job in another org is a ``404`` (org-scoping, no leak).","operationId":"get_import_job_endpoint_v1_orgs__org_id__import_jobs__job_id__get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Job Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportJobResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/import-jobs/{job_id}:cancel":{"post":{"tags":["import-jobs"],"summary":"Cancel Import Job Endpoint","description":"Cancel a queued/running import (spec §21.9). Admin/owner-only.\n\nTerminal job → ``409 conflict``. A queued/running job is landed in ``failed``\n(no ``cancelled`` status) and its transient token dropped so an unclaimed job\nnever runs. Cancelling an already-running job is best-effort — the runner does\nnot poll for cancellation.","operationId":"cancel_import_job_endpoint_v1_orgs__org_id__import_jobs__job_id__cancel_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Job Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportJobResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/orgs/{org_id}/import-jobs:databases":{"post":{"tags":["import-jobs"],"summary":"List Databases Endpoint","description":"List the Notion databases the token can see, to pick from (spec §21.9/§21.10).\n\nAdmin/owner-only. The transient token is carried in the request body (never a\nURL/query string — proxies log those) and is used once to build the Notion\nsource, then dropped; nothing is persisted. Returns ``[{id, title}]``.","operationId":"list_databases_endpoint_v1_orgs__org_id__import_jobs_databases_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDatabasesRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDatabasesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards":{"get":{"tags":["boards"],"summary":"List Boards","operationId":"list_boards_v1_boards_get","parameters":[{"name":"archived","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Archived"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_BoardResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"post":{"tags":["boards"],"summary":"Create Board","operationId":"create_board_v1_boards_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBoardRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}]}},"/v1/boards/{board_id}":{"get":{"tags":["boards"],"summary":"Get Board","operationId":"get_board_v1_boards__board_id__get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"include_archived","in":"query","required":false,"schema":{"type":"boolean","description":"NOV-459 (§7.1): also return ARCHIVED property definitions in the ``properties`` map, each tagged ``\"archived\": true``. Archived definitions are hidden by default — the map is the board's live schema. Deleting a ``unique_id`` property archives rather than removes it, preserving its id, prefix and numbering; re-adding the same name reinstates it. Use this to discover what is archived, then reinstate it (PATCH with a body under that name) or delete it permanently (POST /v1/boards/{board_id}/properties:purge). Handing this response's ``properties`` map straight back to PATCH is safe: an entry echoed with ``\"archived\": true`` stays archived, so a read-modify-write that edits one property does not reinstate the rest. Reinstating is therefore always explicit — send a body under the archived name WITHOUT the flag.","default":false,"title":"Include Archived"},"description":"NOV-459 (§7.1): also return ARCHIVED property definitions in the ``properties`` map, each tagged ``\"archived\": true``. Archived definitions are hidden by default — the map is the board's live schema. Deleting a ``unique_id`` property archives rather than removes it, preserving its id, prefix and numbering; re-adding the same name reinstates it. Use this to discover what is archived, then reinstate it (PATCH with a body under that name) or delete it permanently (POST /v1/boards/{board_id}/properties:purge). Handing this response's ``properties`` map straight back to PATCH is safe: an entry echoed with ``\"archived\": true`` stays archived, so a read-modify-write that edits one property does not reinstate the rest. Reinstating is therefore always explicit — send a body under the archived name WITHOUT the flag."},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"patch":{"tags":["boards"],"summary":"Update Board","operationId":"update_board_v1_boards__board_id__patch","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBoardRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["boards"],"summary":"Delete Board","operationId":"delete_board_v1_boards__board_id__delete","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/properties:purge":{"post":{"tags":["boards"],"summary":"Purge Board Property","description":"PERMANENTLY delete an archived property definition (NOV-459, §7.1).\n\nIRREVERSIBLE, and the only irreversible operation on a board's property\nschema. Deleting a ``unique_id`` property via ``PATCH /v1/boards/{id}`` with\n``{\"properties\": {\"ID\": null}}`` ARCHIVES it — the definition, its id, its\nprefix and its numbering all survive, and re-adding the same name reinstates\nevery one of them. This endpoint destroys that. Once purged, re-adding the\nname mints a brand-new property, and the name becomes free for any type.\n\nValues already stored on cards under the purged name are NOT deleted; only\nthe definition is. 404 if the board has no property under that name, 422 if\nit has a LIVE one (archive it first). Manager role required.","operationId":"purge_board_property_v1_boards__board_id__properties_purge_post","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurgeBoardPropertyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/query":{"post":{"tags":["boards"],"summary":"Query Board","description":"The primary board read — a page of cards in the spec §8 list envelope.\n\nNOV-457: the service still assembles the envelope as a dict (the MCP\n``list_cards`` wrapper and the public-board payload both consume it in that\nform), so the router validates it into the declared model. The published\nOpenAPI schema is then the same object the response is serialized from.","operationId":"query_board_v1_boards__board_id__query_post","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardQueryRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_CardResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/labels":{"get":{"tags":["boards"],"summary":"List Board Labels","description":"The board's label vocabulary — ``{name, color, count}``, most-used first.\n\nNOV-437: Labels are a card BUILT-IN (``card.labels``). A board-schema\n``multi_select`` named \"Labels\" *can* be created — only ``Status`` is\nreserved — but the built-in shadows it on the card read, so nothing else on\nthe API exposes \"which labels does this board actually use\". This feeds the\ncard-detail label picker and the board's\nLabels filter; without it both offered an empty list and every reuse became\na fresh typo/color variant.\n\nAggregated over the board's non-archived cards. Names merge case- and\nspace-insensitively; a name used with several colors reports its\nmost-used color, and ``count`` is the number of distinct CARDS carrying the\nname (across all its colors). The board-query ``contains`` filter folds names\nthe same way, so a value taken from here matches every spelling variant on\nthe board. Same 404-on-no-access contract as every other board endpoint.","operationId":"list_board_labels_v1_boards__board_id__labels_get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":100,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_BoardLabelResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/columns":{"get":{"tags":["boards"],"summary":"List Columns","operationId":"list_columns_v1_boards__board_id__columns_get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_BoardColumnResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"post":{"tags":["boards"],"summary":"Create Column","operationId":"create_column_v1_boards__board_id__columns_post","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateColumnRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardColumnResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/columns/{column_id}":{"patch":{"tags":["boards"],"summary":"Update Column","operationId":"update_column_v1_boards__board_id__columns__column_id__patch","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"column_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Column Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateColumnRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardColumnResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["boards"],"summary":"Delete Column","operationId":"delete_column_v1_boards__board_id__columns__column_id__delete","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"column_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Column Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/property-types":{"get":{"tags":["boards"],"summary":"List Property Types","description":"Filter+sort vocabulary metadata for the frontend (NOV-73).\n\nReturns a static map of property type → allowed operators, plus the\nmax filter nesting depth. The frontend derives its filter-builder UI\nfrom this so the operator list never drifts from the backend\ntranslator's truth.\n\nAuthenticated callers only (any role): operator lists are not org-\nsensitive, but we don't want unauthenticated clients sniffing the\nsurface area.","operationId":"list_property_types_v1_property_types_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Property Types V1 Property Types Get"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}]}},"/v1/board-templates":{"get":{"tags":["boards"],"summary":"List Board Templates","description":"List curated board-template presets (NOV-350 / §7.8).\n\nEach entry carries a ``key``, display ``name``, one-line ``description``,\n``property_validation`` mode, and a preview of the columns + properties the\npreset would create. To instantiate one, POST /v1/boards with the ``template``\nkey. The catalogue is static (org-agnostic); authenticated callers only —\nsame gate as /property-types. Uses the standard list envelope so clients\nread every list surface uniformly; the whole catalogue always fits one page\n(``has_more`` is always false).","operationId":"list_board_templates_v1_board_templates_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_BoardTemplateResponse_"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}]}},"/v1/boards/{board_id}/access":{"get":{"tags":["board-access"],"summary":"Get Board Access","operationId":"get_board_access_v1_boards__board_id__access_get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardAccessListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"post":{"tags":["board-access"],"summary":"Post Board Access","operationId":"post_board_access_v1_boards__board_id__access_post","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantBoardAccessRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardGrantResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/access/{user_id}":{"patch":{"tags":["board-access"],"summary":"Patch Board Access","operationId":"patch_board_access_v1_boards__board_id__access__user_id__patch","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBoardAccessRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardGrantResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["board-access"],"summary":"Delete Board Access","operationId":"delete_board_access_v1_boards__board_id__access__user_id__delete","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/access:reclaim":{"post":{"tags":["board-access"],"summary":"Reclaim Board Access","description":"Org-owner-only governance action: add (or raise to) the owner's manager\ngrant. Non-owners get a uniform ``403`` (the service closes the existence\noracle before any board load). See ``reclaim_board``.","operationId":"reclaim_board_access_v1_boards__board_id__access_reclaim_post","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardGrantResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/invitations":{"post":{"tags":["board-access"],"summary":"Create Board Invitation","description":"Invite an email address straight to **this board**. Board manager only.\n\nOne operator action for \"share this board with my client's reviewer\": it\nmints a pending **guest** invitation carrying the board grant, so accepting\ncreates the membership and the ``board_grants`` row in the same transaction\nand the invitee lands on a workspace where the board is already there —\ninstead of the empty workspace §4.4 describes.\n\nBoard ``manager`` is the gate (§4.6's sharing capability), **not** org\nadmin: a ``member`` who runs one client's board can bring that client onto\nit without being made an administrator of the whole workspace. A ``guest``\ncannot — an external collaborator must not be able to staff, or spend the\nseats of, a workspace they do not pay for (``403``).\n\nStatus codes carry the outcome: **201** for a new invitation, **200** for a\nresend of a live one to the same board (the token is rotated, so the\nprevious link dies), and **200** with the ``invitation_noop`` shape when the\naddress already holds a membership — in which case the board grant is\napplied immediately and rides along on the response, because the operator's\nintent (\"share this board with them\") is satisfiable right now without an\ninvitation. A pending invitation for a *different* board, or a workspace\ninvitation, is a ``409``: the two are different offers and neither may\nsilently overwrite the other.\n\nThis is **not** a share link. A share link (``POST\n/v1/boards/{id}/share-links``) is seatless, identity-free and read-only —\nthe free way to let a client watch a board. This creates a real account with\nattribution and comment/edit rights. §4.6 keeps both, and they are not\ninterchangeable.","operationId":"create_board_invitation_v1_boards__board_id__invitations_post","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBoardInvitationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/InvitationResponse"},{"$ref":"#/components/schemas/AlreadyMemberResponse"}],"title":"Response Create Board Invitation V1 Boards  Board Id  Invitations Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"get":{"tags":["board-access"],"summary":"List Board Invitations","description":"Invitations sent to this board. Board manager only.\n\nDefaults to the pending slice — what the share dialog shows next to the\npeople who already have access, so an operator can tell \"invited, waiting\"\napart from \"in\". ``?status=all`` returns the history.","operationId":"list_board_invitations_v1_boards__board_id__invitations_get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"default":"pending","title":"Status"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_InvitationResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/invitations/{invitation_id}":{"delete":{"tags":["board-access"],"summary":"Revoke Board Invitation","description":"Withdraw a pending invitation to this board. Board manager only.\n\nExists alongside the admin-gated workspace revoke because the manager who\nsent it may not be an org admin, and whoever can send has to be able to take\nit back. Terminal and seatless — and since the grant materializes only at\naccept, a revoked invitation leaves no board access behind.","operationId":"revoke_board_invitation_v1_boards__board_id__invitations__invitation_id__delete","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Invitation Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/share-links":{"post":{"tags":["share-links"],"summary":"Create Board Share Link","operationId":"create_board_share_link_v1_boards__board_id__share_links_post","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateShareLinkRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareLinkCreatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"get":{"tags":["share-links"],"summary":"List Board Share Links","operationId":"list_board_share_links_v1_boards__board_id__share_links_get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ShareLinkResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["share-links"],"summary":"Revoke All Board Share Links","description":"Revoke every active link on the board — the NOV-219 kill-switch.\n\nManager-gated. Returns the links revoked by this call (empty when the board\nhad no active links). Closing external access here never touches the board's\n``visibility`` or internal access.","operationId":"revoke_all_board_share_links_v1_boards__board_id__share_links_delete","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ShareLinkResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/share-links/{link_id}":{"delete":{"tags":["share-links"],"summary":"Revoke Board Share Link","operationId":"revoke_board_share_link_v1_boards__board_id__share_links__link_id__delete","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"link_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Link Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareLinkResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/public/config":{"get":{"tags":["public"],"summary":"Get Public Config","description":"Publish the runtime policy an unauthenticated client needs (NOV-512).\n\nDeliberately NOT folded into ``/healthz``: that route is ops/meta (liveness,\nbuild sha, migration state) and this is a product contract the SPA reads on\nthe signup and password-reset forms. Keeping them apart also keeps this one\ninside ``/v1``, so it carries the same ``X-Board-Version`` gate and the same\nversioning promise as every other client-facing read.\n\nThe handler resolves no principal and reads no org-scoped table: the\nresponse is identical for every caller on a deploy and can never leak\ntenant data. (The app-level dependencies — ``enforce_billing_writable`` and\nfriends — do take a ``db`` session, but the Session is lazy and their org\nlookups are gated on ``principal.org_id``, which is ``None`` for anonymous\ntraffic: no query runs, no connection is checked out.)\nValues come from :func:`app.services.auth.password_policy`, the same\nfunction ``validate_password_strength`` validates through — so an operator\nwho overrides ``PASSWORD_MIN_LENGTH`` moves the client's rules with the\nserver's instead of leaving them stale.","operationId":"get_public_config_v1_public_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicConfigResponse"}}}}},"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/public/boards/{board_id}":{"get":{"tags":["public"],"summary":"Get Public Board","description":"Read a shared board (board-scoped): metadata, columns, non-archived cards.\n\nProgrammatic / back-compat surface — the token rides as the ``token`` query\nparam and the path pins the board. Kept for callers that already hold a\nboard id; the client-facing web route uses the token-only sibling below.","operationId":"get_public_board_v1_public_boards__board_id__get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Public Board V1 Public Boards  Board Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/public/share/{token}":{"get":{"tags":["public"],"summary":"Get Public Board By Token","description":"Resolve a share-link ``token`` and serve ITS bound board (token-only, NOV-200).\n\nThe client-facing share URL is ``{app_base_url}/share/{token}`` (a web route\nrendering the read-only board); this endpoint backs it. The token resolver\nis board-agnostic — it resolves the raw token via the ``last4`` index +\nargon2 verify and returns a principal carrying the bound ``board_id`` — so\nwe serve the principal's OWN board with no board id in the URL.\n\n404 (never reveal) when the token is absent, malformed, unknown, expired, or\nrevoked. The bound org is set as the tenant scope so the board/card queries\nare org-filtered; board *access* is still granted only on the bound board by\n``require_board_access`` inside :func:`_public_board_payload`.","operationId":"get_public_board_by_token_v1_public_share__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Public Board By Token V1 Public Share  Token  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/public/invitations/{token}":{"get":{"tags":["public"],"summary":"Get Invitation By Token","description":"Resolve an emailed invitation token to its preview (unauthenticated, §4.8).\n\nBacks the ``{app_base_url}/invitations/{token}`` deep link the invitation\nemail carries. The recipient has to be able to see *which workspace is\nasking* before deciding whether to sign in — or create an account at all —\nso this read cannot require a session. It is unauthenticated but not\nunauthorized: the token IS the credential, argon2id-hashed at rest and\nresolvable only while the invitation is pending.\n\n404 — never a distinguishable error — when the token is absent, malformed,\nunknown, expired, revoked, or already used. All six look identical, so the\nendpoint is not an oracle for guessing tokens, and the payload deliberately\nsays nothing about whether the invited address has an account: presenting a\nvalid token must not become account enumeration either.\n\nAccepting still requires signing in as the invited address\n(``POST /v1/invitations/{id}:accept``). This endpoint only tells the SPA\nwhich invitation to accept and whom to ask the visitor to sign in as.","operationId":"get_invitation_by_token_v1_public_invitations__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationPreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/public/boards/{board_id}/cards/{card_id}/comments":{"post":{"tags":["public"],"summary":"Create Public Comment","description":"Post a comment through a ``commenter`` share link.\n\n``require_board_access(min_role='commenter')`` rejects a ``viewer`` link with\n403 and a cross-board token with 404. The card must live on the bound board.\nThe comment is authored anonymously (no User, no seat).\n\nThe app-level ``enforce_billing_writable`` dependency falls out at its \"no\nprincipal\" gate for a share-link token (it isn't a bearer), so the §18.8\nread-only-when-past_due invariant is enforced here explicitly: a past_due org\nrefuses this write with the same ``402 billing_past_due`` an internal write\nreturns. Checked after the role gate so a ``viewer`` link still 403s (and an\nunauthorized caller never learns the org's billing state).","operationId":"create_public_comment_v1_public_boards__board_id__cards__card_id__comments_post","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"card_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Card Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCommentRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/public/boards/{board_id}/agent-activity":{"get":{"tags":["public"],"summary":"Get Public Agent Activity","description":"Client-shareable, read-only agent-activity report for a shared board (NOV-271).\n\nBoard-scoped surface: the token rides as ``?token=`` and the path pins the\nboard. A viewer OR commenter token for THIS board sees the all-agents rollup;\na cross-board / expired / revoked token 404s (existence hidden); an\nun-entitled org 403s ``governance_not_entitled`` AFTER the board gate. GATED\nto the Scale/Agency tier (the agency's entitlement). ``window`` is ``24h`` or\n``7d`` (400 otherwise).","operationId":"get_public_agent_activity_v1_public_boards__board_id__agent_activity_get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"window","in":"query","required":false,"schema":{"type":"string","default":"24h","title":"Window"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Limit"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicAgentActivityResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/public/share/{token}/agent-activity":{"get":{"tags":["public"],"summary":"Get Public Agent Activity By Token","description":"Token-only sibling of :func:`get_public_agent_activity` (NOV-200 pattern).\n\nResolves the share-link ``token`` board-agnostically and serves the report\nfor ITS bound board — no board id in the URL, mirroring\n:func:`get_public_board_by_token`. Same gates, same 404-on-invalid-token\ncontract.","operationId":"get_public_agent_activity_by_token_v1_public_share__token__agent_activity_get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"window","in":"query","required":false,"schema":{"type":"string","default":"24h","title":"Window"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Limit"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicAgentActivityResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/beacon":{"post":{"tags":["beacon"],"summary":"Post Beacon","description":"Accept one batch of behavioral events (WIRE-1..6).\n\nReturns 204 on success and on every drop. See the module docstring for why.","operationId":"post_beacon_v1_beacon_post","responses":{"204":{"description":"Successful Response"}}}},"/v1/beacon/snapshot":{"post":{"tags":["beacon"],"summary":"Post Snapshot","description":"Accept one masked DOM canvas for a (route × bucket × epoch) (HEAT-2).\n\nThe heatmap's backdrop, not an event: P2c renders stored click coordinates\nover this document in a sandboxed iframe, which is why §26 forbids a\nscreenshot service and asks for a serialized DOM instead.\n\n**The gate order is the same contract as ``post_beacon``**, and the two\ndeliberately share every helper that implements it — kill switch, then\nparse, then property + origin, then principal, then the bot filter, then\nidentity. What differs is three checks this payload needs and a batch of\nevents does not:\n\n* **The recording purpose is required** (PRIV-1, REP-2). A DOM snapshot is\n  replay-class content — it is a serialization of the visitor's actual\n  screen — so the aggregate floor does not authorize it, and neither does an\n  analytics-only grant. This mirrors §26.4's rule for ``/v1/beacon/replay``\n  and is the reason the tracker only ever schedules a capture in ``full``\n  mode: the client gate and this one are two independent readings of the\n  same requirement, and either alone would be a single point of failure.\n* **The declared masking level must meet the property's** (REP-2). Weaker is\n  dropped; stronger is accepted, because a client that masked harder than\n  asked has not violated anything.\n* **Route exclusions apply exactly as they do to events** (TRK-8). A route\n  we promised never to capture must not arrive as a full serialization of\n  itself, and the exclusion list is server-side truth — a stale tracker that\n  has not polled the new config yet gets caught here.\n\n**The bot filter has no integration-token exemption here**, unlike the event\nbeacon. That exemption exists because an agent's HTTP client legitimately\nhas a bot-shaped UA and its behavioral events are a segmentation dimension\nwe want. A DOM snapshot is different in kind: a token-bearing agent has no\nDOM to serialize, so a snapshot arriving under one is either a headless\nbrowser in CI or something forged, and either would overwrite the canonical\ncanvas for a route with markup no human ever saw.\n\nReturns 204 on success and on every drop, exactly like the event beacon —\na differentiated response would turn this route into an oracle for which\nproperties exist and what they are configured to allow.","operationId":"post_snapshot_v1_beacon_snapshot_post","responses":{"204":{"description":"Successful Response"}}}},"/v1/beacon/replay":{"post":{"tags":["beacon"],"summary":"Post Replay","description":"Accept one chunk of one session's recording (REP-1..6, §26.8).\n\n**The gate order is the same contract as the other two routes**, built out\nof the same helpers, and the two checks that differ from the snapshot route\ndiffer because a recording is not a canvas:\n\n* **The property's replay mode must permit it.** ``off`` means this property\n  records nothing, and that has to be enforced here and not only in the\n  tracker — the config endpoint is a request a hand-rolled client can simply\n  not make. This is the replay analogue of the property gate, and it is the\n  one control an operator has that takes effect without a deploy.\n* **There is no route gate on the payload, only on the chunk's label.** A\n  recording crosses routes by construction, so \"was this chunk taken on an\n  excluded route\" has no answer — the events inside it may span five. What\n  the route exclusion means for replay is therefore enforced on the *client*\n  (the recorder stops when it enters an excluded route) and the label a\n  chunk arrives with is checked here as a backstop against a stale tracker,\n  not as a claim about every event inside.\n\nEverything else is deliberately identical to ``post_snapshot``: the recording\npurpose is required (PRIV-1, WIRE-4) and checked server-side because the\nclient gate and this one are two independent readings of one requirement; the\ndeclared masking level must meet the property's (REP-2); and **the bot filter\ngrants no integration-token exemption**, because a token-bearing agent has no\nDOM to record, so a recording arriving under one is either a headless browser\nin CI or something forged.\n\nChunks are written **server-side** to S3 rather than through §25's browser\npresign-POST handshake, and that is a design decision rather than an\nomission: a presigned PUT would put the browser in direct contact with the\nbucket, and every gate above — consent, masking, mode, budget — would become\nadvice the client could decline to take. Passing the bytes through here costs\na hop and keeps enforcement server-visible.\n\nReturns 204 on success and on every drop, like its siblings.","operationId":"post_replay_v1_beacon_replay_post","responses":{"204":{"description":"Successful Response"}}}},"/v1/beacon/config":{"get":{"tags":["beacon"],"summary":"Get Beacon Config","description":"Serve one property's runtime knobs — the tracker's kill switch (TRK-8).\n\nThe query parameter is ``?property=`` because that is what the NOV-615\ntracker sends. It resolves against ``BxProperty.key``; ``name`` is a display\nlabel and is never addressable.\n\nAn unknown property, a disabled one, and a deployment with ``bx_enabled``\noff all return the same well-formed ``enabled: false`` body. That is the\ncontract the tracker is written against: it must be able to go inert on a\nresponse it can parse, and a 404 for unknown properties would let anyone\nenumerate which ones are registered.\n\n``property_key`` is echoed only when the property is actually live. The\ntracker treats an absent key as disabled, so nulling it is a second kill\nswitch that works even if ``enabled`` were somehow misread — and because\n\"registered but disabled\" and \"never existed\" both answer ``null``, the\nfield cannot be used to probe which properties exist.\n\nReads no org-scoped table and takes no principal — the response depends only\non the property, so it is identical for every caller and can never leak\ntenant data.","operationId":"get_beacon_config_v1_beacon_config_get","parameters":[{"name":"property","in":"query","required":false,"schema":{"type":"string","maxLength":64,"default":"","title":"Property"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BeaconConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/cards":{"post":{"tags":["cards"],"summary":"Create Card","operationId":"create_card_v1_cards_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCardRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}]}},"/v1/cards/{card_id}":{"get":{"tags":["cards"],"summary":"Get Card","operationId":"get_card_v1_cards__card_id__get","parameters":[{"name":"card_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Card Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"patch":{"tags":["cards"],"summary":"Update Card","description":"Patch a card. ``If-Match: \"v17\"`` against the etag honored (spec §8.1).\n\n``Prefer: merge-properties`` (spec §7.3, NOV-352): ``properties`` merges\nkey-by-key instead of replacing the map, and array-typed properties\n(``multi_select`` / ``people`` / ``files`` per the board schema)\nelement-UNION with the stored value — deduped by option id/name, user id,\nor file id/url/name — so concurrent agent appends never clobber each\nother. Explicit removal per key: ``{\"add\": [...], \"remove\": [...]}``\n(either optional). Without the header, writes replace (Notion default).","operationId":"update_card_v1_cards__card_id__patch","parameters":[{"name":"card_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Card Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCardRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["cards"],"summary":"Delete Card","description":"Soft-archive a card. ``If-Match: \"v17\"`` honored when supplied (spec §8.1).","operationId":"delete_card_v1_cards__card_id__delete","parameters":[{"name":"card_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Card Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/cards/{card_id}/audit":{"get":{"tags":["cards"],"summary":"List Card Audit","operationId":"list_card_audit_v1_cards__card_id__audit_get","parameters":[{"name":"card_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Card Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":10,"title":"Limit"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_CardAuditEntry_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/cards/{card_id}/autopilot-runs":{"get":{"tags":["cards"],"summary":"List Card Autopilot Runs","description":"Autopilot runs that wrote content on this card, newest first (§25.9).\n\nGated on **board access**, not the org admin role — unlike\n``GET /v1/orgs/{org_id}/ai-run-usage``, which is the governance feed and\ncarries token counts and dollar costs. This route carries neither, and it\nexists so the §25.9 Autopilot thumbs can sit next to the content on the card\nfor whoever is reading it.\n\n**Not cursor-paginated, but ``has_more`` is still honest.** The row cap is\nfixed and ``next_cursor`` is always null — there is no second page to ask\nfor. ``has_more`` reports whether the cap truncated anything, because it is\na claim about the data rather than about the wire shape, and an envelope\nthat says a list is complete when it is not is worse than one with no\npagination fields at all.","operationId":"list_card_autopilot_runs_v1_cards__card_id__autopilot_runs_get","parameters":[{"name":"card_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Card Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":10,"minimum":1,"default":10,"title":"Limit"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_AutopilotRunProvenanceResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/cards/{card_id}:move":{"post":{"tags":["cards"],"summary":"Move Card","description":"Move a card. ``If-Match: \"v17\"`` against the etag honored (spec §8.1).","operationId":"move_card_v1_cards__card_id__move_post","parameters":[{"name":"card_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Card Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveCardRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/cards:batchUpdate":{"post":{"tags":["cards"],"summary":"Batch Update Cards","description":"Transactional bulk ops. ``Prefer: merge-properties`` applies the same\nper-key merge + array element-union semantics as ``PATCH /v1/cards/{id}``\n(spec §7.3) to every ``set`` op in the batch.","operationId":"batch_update_cards_v1_cards_batchUpdate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchUpdateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchUpdateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}]}},"/v1/cards/{card_id}/agent_activity":{"post":{"tags":["cards"],"summary":"Set Card Agent Activity","description":"Publish an agent's current claim on a card (NOV-91).\n\nLast-write-wins. The server stamps ``started_at`` to drive the\n10-minute lazy expiry on read. Broadcasts\n``card.agent_activity.set`` so the kanban board's WS subscribers\nflip the agent-motion frame on within ~200ms.","operationId":"set_card_agent_activity_v1_cards__card_id__agent_activity_post","parameters":[{"name":"card_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Card Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetAgentActivityRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["cards"],"summary":"Clear Card Agent Activity","description":"Clear an agent's claim on a card (NOV-91).\n\nIdempotent — re-clearing an already-empty field is a no-op\n(no realtime event fires). The next card mutation also clears the\nfield automatically; this DELETE is the explicit \"I'm done\" path.","operationId":"clear_card_agent_activity_v1_cards__card_id__agent_activity_delete","parameters":[{"name":"card_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Card Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/columns/{column_id}:claim-next":{"post":{"tags":["leases"],"summary":"Claim Next","description":"Lease the next eligible card in a column.\n\nOn success returns ``200`` with the lease + full card. When the queue is\ndrained (or the column's ``concurrency_limit`` is reached) returns ``200``\n``{object:\"lease\", card:null}`` (a single content-type, cheap to busy-poll) —\nnever a ``204``/``404`` (spec §7.5.3).\n\nAn optional ``Idempotency-Key`` header makes the claim safe to retry after a\ndropped response: a retry with the SAME key returns the SAME lease (never a\nsecond one) or replays the original ``no_work`` (NOV-259, spec §7.5.4).","operationId":"claim_next_v1_boards__board_id__columns__column_id__claim_next_post","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"column_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Column Id"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ClaimNextRequest"},{"type":"null"}],"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/leases/{lease_id}:heartbeat":{"post":{"tags":["leases"],"summary":"Renew Lease","description":"Extend a held lease's TTL + stamp its heartbeat. ``:heartbeat`` is an\nalias of ``:renew`` (same handler). ``409 lease_lost`` if the caller no\nlonger holds the active lease (spec §7.5.3).","operationId":"renew_lease_v1_leases__lease_id__heartbeat_post","parameters":[{"name":"lease_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Lease Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenewLeaseRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/leases/{lease_id}:renew":{"post":{"tags":["leases"],"summary":"Renew Lease","description":"Extend a held lease's TTL + stamp its heartbeat. ``:heartbeat`` is an\nalias of ``:renew`` (same handler). ``409 lease_lost`` if the caller no\nlonger holds the active lease (spec §7.5.3).","operationId":"renew_lease_v1_leases__lease_id__renew_post","parameters":[{"name":"lease_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Lease Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenewLeaseRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/leases/{lease_id}:complete":{"post":{"tags":["leases"],"summary":"Complete Lease","description":"Complete a held lease: optionally patch the card, advance it to the\ncontract's ``on_success_column_id``, release the lease. Idempotent on a\ncompleted lease with the same token; ``409 lease_lost`` on a stale token\n(spec §7.5.3).\n\nThe ``properties`` patch applies MERGE semantics (spec §7.3, NOV-352):\nkeys merge instead of replacing the map, and array-typed properties\n(``multi_select`` / ``people`` / ``files``) element-union with the stored\nvalue — an agent's result tags never wipe tags already on the card.\nPer-key ``{\"add\": [...], \"remove\": [...]}`` is honored.","operationId":"complete_lease_v1_leases__lease_id__complete_post","parameters":[{"name":"lease_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Lease Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteLeaseRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/leases/{lease_id}:fail":{"post":{"tags":["leases"],"summary":"Fail Lease","description":"Release a held lease as failed; requeue with backoff while attempts\nremain, else dead-letter in place. Idempotent on an already-failed lease\nwith the same token; ``409 lease_lost`` on a stale token (spec §7.5.3).","operationId":"fail_lease_v1_leases__lease_id__fail_post","parameters":[{"name":"lease_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Lease Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailLeaseRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/leases/{lease_id}":{"get":{"tags":["leases"],"summary":"Get Lease","description":"Fetch one lease. Gated on board READ access via the lease's card — a\nnon-grantee of a restricted board (or a cross-org caller) gets ``404\nobject_not_found`` (existence never revealed). A read: never past_due-blocked\n(spec §7.5.3 observability).","operationId":"get_lease_v1_leases__lease_id__get","parameters":[{"name":"lease_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Lease Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/leases":{"get":{"tags":["leases"],"summary":"List Board Leases","description":"List a board's leases (Notion-shape envelope). Requires board READ access\n(viewer+); a non-grantee of a restricted board 404s. ``state`` is one of\n``active`` (default — currently held), ``expired`` (lapsed, awaiting the\nreaper — \"who is stuck\"), or ``all`` (full history incl. released); an\nunknown value is ``422``. ``column_id`` narrows to one column's queue.\nOrdered soonest-to-expire first. A read: never past_due-blocked.","operationId":"list_board_leases_v1_boards__board_id__leases_get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"column_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"}},{"name":"state","in":"query","required":false,"schema":{"type":"string","default":"active","title":"State"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_LeaseResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/columns/{column_id}/contract":{"get":{"tags":["leases"],"summary":"Get Column Contract","description":"Read a column's job contract (the durable-work queue config). MANAGER-only\n— a non-manager board role is ``403``; a missing/cross-org board or a column\nnot on this board is ``404`` (existence never revealed). Returns the fields as\nstored (a column with no contract reads back as the empty shape). A read:\nnever past_due-blocked (spec §7.5.3 admin).","operationId":"get_column_contract_v1_boards__board_id__columns__column_id__contract_get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"column_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Column Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobContractResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"put":{"tags":["leases"],"summary":"Set Column Contract","description":"Write a column's job contract (full replace). MANAGER-only (same 404/403\ncontract as the GET). Rejects a malformed ``input_schema``/``output_schema``\nor an off-board ``on_success_column_id``/``on_failure_column_id`` with ``422\ninvalid_contract``, and an unknown field with ``422`` (extra keys forbidden).\nEmits ``column.contract.updated``. The app-level past_due guard already blocks\nthis PUT for a delinquent org (spec §7.5.3 admin).","operationId":"set_column_contract_v1_boards__board_id__columns__column_id__contract_put","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"column_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Column Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobContractRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobContractResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/card-templates":{"post":{"tags":["card-templates"],"summary":"Create Template","operationId":"create_template_v1_card_templates_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCardTemplateRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardTemplateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}]}},"/v1/boards/{board_id}/card-templates":{"get":{"tags":["card-templates"],"summary":"List Templates","operationId":"list_templates_v1_boards__board_id__card_templates_get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_CardTemplateResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/card-templates/{template_id}":{"get":{"tags":["card-templates"],"summary":"Get Template","operationId":"get_template_v1_card_templates__template_id__get","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Template Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardTemplateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"patch":{"tags":["card-templates"],"summary":"Update Template","operationId":"update_template_v1_card_templates__template_id__patch","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Template Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCardTemplateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardTemplateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["card-templates"],"summary":"Delete Template","operationId":"delete_template_v1_card_templates__template_id__delete","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Template Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/cards/{card_id}:make-recurring":{"post":{"tags":["card-templates"],"summary":"Make Card Recurring","operationId":"make_card_recurring_v1_cards__card_id__make_recurring_post","parameters":[{"name":"card_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Card Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MakeRecurringRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardTemplateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/comments":{"get":{"tags":["comments"],"summary":"List Comments","description":"List comments on a card.\n\nAccepts ``block_id`` as a Notion-shape alias for ``card_id`` (NOV-63 B5).\nNotion's API uses ``GET /v1/comments?block_id=<page_id>`` to list\npage-level comments — content-forge does this verbatim. We treat the\ntwo parameters as equivalent so a Notion-shape client works unchanged.\nIf both are present, ``card_id`` wins.","operationId":"list_comments_v1_comments_get","parameters":[{"name":"card_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Card Id"}},{"name":"block_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Block Id"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Comments V1 Comments Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"post":{"tags":["comments"],"summary":"Create Comment","operationId":"create_comment_v1_comments_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCommentRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}]}},"/v1/comments/{comment_id}":{"patch":{"tags":["comments"],"summary":"Update Comment","description":"Patch a comment. ``If-Match: \"v17\"`` against the etag honored (spec §8.1).","operationId":"update_comment_v1_comments__comment_id__patch","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Comment Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCommentRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["comments"],"summary":"Delete Comment","description":"Soft-archive a comment. ``If-Match: \"v17\"`` honored when supplied.","operationId":"delete_comment_v1_comments__comment_id__delete","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Comment Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/databases/{database_id}":{"get":{"tags":["notion-compat"],"summary":"Get Database","operationId":"get_database_v1_databases__database_id__get","parameters":[{"name":"database_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Database Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DatabaseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"patch":{"tags":["notion-compat"],"summary":"Update Database","operationId":"update_database_v1_databases__database_id__patch","parameters":[{"name":"database_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Database Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBoardRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DatabaseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/databases/{database_id}/query":{"post":{"tags":["notion-compat"],"summary":"Query Database","operationId":"query_database_v1_databases__database_id__query_post","parameters":[{"name":"database_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Database Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardQueryRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_CardResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/data_sources/{data_source_id}":{"get":{"tags":["notion-compat"],"summary":"Get Data Source","operationId":"get_data_source_v1_data_sources__data_source_id__get","parameters":[{"name":"data_source_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Data Source Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DataSourceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/data_sources/{data_source_id}/query":{"post":{"tags":["notion-compat"],"summary":"Query Data Source","operationId":"query_data_source_v1_data_sources__data_source_id__query_post","parameters":[{"name":"data_source_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Data Source Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardQueryRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_CardResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/pages":{"post":{"tags":["notion-compat"],"summary":"Create Page","operationId":"create_page_v1_pages_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCardRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}]}},"/v1/pages/{page_id}":{"get":{"tags":["notion-compat"],"summary":"Get Page","operationId":"get_page_v1_pages__page_id__get","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Page Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"patch":{"tags":["notion-compat"],"summary":"Update Page","description":"Update a card via the Notion-shape `PATCH /v1/pages/{id}` alias.\n\nPromotes built-in Notion properties (Status, Assignees, Due, Labels) to\nfirst-class card columns before delegating (NOV-63 B6). Unknown\nproperty names pass through unchanged.\n\nNOV-82: a Status property write resolves to the named board column and\nmoves the card via the canonical move path. A name that matches no\ncolumn on the card's board is rejected as 400. The single emitted event\nis ``card.moved`` — the legacy ``card.status_changed`` event is gone\n(the column move IS the status change).","operationId":"update_page_v1_pages__page_id__patch","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Page Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCardRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/pages/{page_id}/properties/{prop_id}":{"get":{"tags":["notion-compat"],"summary":"Get Page Property","operationId":"get_page_property_v1_pages__page_id__properties__prop_id__get","parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Page Id"}},{"name":"prop_id","in":"path","required":true,"schema":{"type":"string","title":"Prop Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PropertyItemResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/blocks/{parent_id}/children":{"get":{"tags":["blocks"],"summary":"List Block Children","description":"Fetch a card's body tree, recursing up to ``depth`` levels (spec §6.4).","operationId":"list_block_children_v1_blocks__parent_id__children_get","parameters":[{"name":"parent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Parent Id"}},{"name":"depth","in":"query","required":false,"schema":{"type":"integer","maximum":10,"minimum":1,"default":1,"title":"Depth"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Block Children V1 Blocks  Parent Id  Children Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"patch":{"tags":["blocks"],"summary":"Append Block Children","description":"Append a run of blocks under ``parent_id`` (card or block).\n\nOptional ``after`` selects a sibling to insert AFTER (Notion shape);\notherwise blocks land at the end.\n\nNOV-168: an optional ``Idempotency-Key`` header makes the append safe to\nretry. Absent ⇒ today's behavior (append normally). Present ⇒ a replay\nwith the same key returns the original response without re-appending.","operationId":"append_block_children_v1_blocks__parent_id__children_patch","parameters":[{"name":"parent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Parent Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppendBlocksRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Append Block Children V1 Blocks  Parent Id  Children Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/blocks/{block_id}":{"patch":{"tags":["blocks"],"summary":"Update Block","description":"Modify a block. ``If-Match`` is ACCEPTED but ADVISORY (NOV-169, spec §8.1).\n\nBody blocks are server-authoritative last-write-wins: a stale/wrong\n``If-Match`` never 412s, the write merges. The header is still read and\nforwarded for back-compat (the frontend keeps sending it until P4/NOV-170);\n``update_block_for_principal`` parses-and-ignores it.","operationId":"update_block_v1_blocks__block_id__patch","parameters":[{"name":"block_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Block Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBlockRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Block V1 Blocks  Block Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["blocks"],"summary":"Delete Block","description":"Soft-archive a block + its descendants.","operationId":"delete_block_v1_blocks__block_id__delete","parameters":[{"name":"block_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Block Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/blocks/{block_id}:move":{"post":{"tags":["blocks"],"summary":"Move Block","description":"Re-parent and/or reorder a block (NOV-498, spec §8.2).\n\nMirrors ``POST /v1/cards/{id}:move``. Returns the moved block's envelope —\nthe same shape ``PATCH /v1/blocks/{id}`` returns.\n\nNo ``If-Match`` here, unlike the card ``:move``: the body surface resolves\nlast-write-wins and never 412s (§8.1), and this endpoint postdates that\ndecision, so there is no legacy header to parse-and-ignore.","operationId":"move_block_v1_blocks__block_id__move_post","parameters":[{"name":"block_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Block Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveBlockRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Move Block V1 Blocks  Block Id  Move Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/files:upload":{"post":{"tags":["files"],"summary":"Presign Upload","description":"Mint a presigned PUT URL for a card attachment.\n\nBody: ``{card_id, filename, content_type, size_bytes?}``. Returns the\nfile envelope plus ``upload_url`` + ``upload_url_expires_in``. The\nclient then PUTs the bytes to ``upload_url`` (NOT through us — direct\nto S3) and calls ``:commit`` once done.","operationId":"presign_upload_v1_files_upload_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFileUploadRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Presign Upload V1 Files Upload Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}]}},"/v1/files/{file_id}:commit":{"post":{"tags":["files"],"summary":"Commit File","description":"Finalize an upload: HeadObject the bytes + flip status to available.","operationId":"commit_file_v1_files__file_id__commit_post","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"File Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Commit File V1 Files  File Id  Commit Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/files/{file_id}":{"get":{"tags":["files"],"summary":"Get File","description":"Metadata + presigned GET URL for an ``available`` file.","operationId":"get_file_v1_files__file_id__get","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"File Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get File V1 Files  File Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["files"],"summary":"Delete File","description":"Soft-archive a file + fire `card.updated` webhook for the removal.","operationId":"delete_file_v1_files__file_id__delete","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"File Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/cards/{card_id}/attachments":{"get":{"tags":["files"],"summary":"List Card Attachments","description":"Notion-shape list of a card's `available` attachments.","operationId":"list_card_attachments_v1_cards__card_id__attachments_get","parameters":[{"name":"card_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Card Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Card Attachments V1 Cards  Card Id  Attachments Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/orgs":{"get":{"tags":["admin"],"summary":"List Admin Orgs","operationId":"list_admin_orgs_v1_admin_orgs_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_AdminOrgRow_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/orgs/{org_id}":{"get":{"tags":["admin"],"summary":"Get Admin Org","operationId":"get_admin_org_v1_admin_orgs__org_id__get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminOrgRow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/billing/orgs":{"get":{"tags":["admin"],"summary":"List Admin Billing Orgs","description":"Per-org billing state across all tenants (spec §18.10).\n\nPaginated + searchable (name/slug). ``?status=past_due`` is the\nfailed-payment list. ``billable_seats`` is recomputed LIVE; revenue is a\nper-kind estimate (``humans × $1 + billable_agents × $1`` at the annual\nmonthly-equivalent display rates — NOV-370) on Pro, 0 on free; ``drift`` is\nthe reconciliation descriptor recorded by the daily cron.","operationId":"list_admin_billing_orgs_v1_admin_billing_orgs_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_AdminBillingOrgRow_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/orgs/{org_id}/grants":{"post":{"tags":["admin"],"summary":"Create Org Grant","operationId":"create_org_grant_v1_admin_orgs__org_id__grants_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantOrgCompRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgGrantRow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"get":{"tags":["admin"],"summary":"List Org Grants","operationId":"list_org_grants_v1_admin_orgs__org_id__grants_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"include_history","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Include History"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_OrgGrantRow_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/orgs/{org_id}/grants/{grant_id}":{"delete":{"tags":["admin"],"summary":"Revoke Org Grant","operationId":"revoke_org_grant_v1_admin_orgs__org_id__grants__grant_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"grant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Grant Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RevokeOrgGrantRequest"},{"type":"null"}],"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgGrantRow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/users":{"get":{"tags":["admin"],"summary":"List Admin Users","operationId":"list_admin_users_v1_admin_users_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_AdminUserRow_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/users/{user_id}":{"get":{"tags":["admin"],"summary":"Get Admin User","operationId":"get_admin_user_v1_admin_users__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUserRow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/audit":{"get":{"tags":["admin"],"summary":"List Admin Audit","operationId":"list_admin_audit_v1_admin_audit_get","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Org Id"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"}},{"name":"event_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Event Type"}},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Since"}},{"name":"until","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Until"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_AdminAuditRow_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/admins":{"get":{"tags":["admin"],"summary":"List Platform Admins Short","operationId":"list_platform_admins_short_v1_admin_admins_get","parameters":[{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Page Size"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_PlatformAdminRow_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"post":{"tags":["admin"],"summary":"Grant Platform Admin Short","operationId":"grant_platform_admin_short_v1_admin_admins_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantPlatformAdminRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformAdminRow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/admin/platform-admins":{"get":{"tags":["admin"],"summary":"List Platform Admins","operationId":"list_platform_admins_v1_admin_platform_admins_get","parameters":[{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Page Size"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_PlatformAdminRow_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"post":{"tags":["admin"],"summary":"Grant Platform Admin","operationId":"grant_platform_admin_v1_admin_platform_admins_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantPlatformAdminRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformAdminRow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/admin/admins/{user_id}":{"delete":{"tags":["admin"],"summary":"Revoke Platform Admin Short","operationId":"revoke_platform_admin_short_v1_admin_admins__user_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/platform-admins/{user_id}":{"delete":{"tags":["admin"],"summary":"Revoke Platform Admin","operationId":"revoke_platform_admin_v1_admin_platform_admins__user_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/feedback":{"get":{"tags":["admin"],"summary":"List Admin Feedback","description":"Cross-org triage list, EVERY filter applied server-side (§25.7).\n\nMulti-value filters are repeated parameters, not comma-joined lists:\n``?type=bug_report&type=idea&status=new``. A comma-joined encoding would\nhave to pick a separator that no enum value can contain and then defend\nthat choice forever; repetition is what HTTP already means by \"several of\nthese\", and FastAPI parses it natively.\n\n``sort`` is ``created_time`` | ``rating`` | ``status`` and ``order`` is\n``asc`` | ``desc`` (default ``created_time``/``desc``, the inbox reading).\nEach key names the response field it sorts on. ``status`` sorts by\nthe triage lifecycle, not the alphabet — see the service.","operationId":"list_admin_feedback_v1_admin_feedback_get","parameters":[{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Repeatable. Filter by feedback type.","title":"Type"},"description":"Repeatable. Filter by feedback type."},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Repeatable. Filter by status.","title":"Status"},"description":"Repeatable. Filter by status."},{"name":"jira_status","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Repeatable. Filter by Jira sync state.","title":"Jira Status"},"description":"Repeatable. Filter by Jira sync state."},{"name":"severity","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Repeatable. Filter by bug severity.","title":"Severity"},"description":"Repeatable. Filter by bug severity."},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Org Id"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"}},{"name":"submitted_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Submitted From"}},{"name":"submitted_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Submitted To"}},{"name":"rating_min","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating Min"}},{"name":"rating_max","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating Max"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","default":"created_time","title":"Sort"}},{"name":"order","in":"query","required":false,"schema":{"type":"string","default":"desc","title":"Order"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_AdminFeedbackRow_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/feedback/count":{"get":{"tags":["admin"],"summary":"Count Admin Feedback","description":"``{new: n}`` — the §25.7.1 nav-badge probe, polled every 60 s.\n\nA plain ``COUNT`` behind the same filter helper the list uses, which is what\nmakes a deep-linked dashboard tile's number agree with the table it opens.\n\n``?status=`` is deliberately absent from the signature rather than accepted\nand ignored: the endpoint counts ``new`` by definition, and a parameter that\nsilently does nothing is worse than one that isn't there.","operationId":"count_admin_feedback_v1_admin_feedback_count_get","parameters":[{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Repeatable. Filter by feedback type.","title":"Type"},"description":"Repeatable. Filter by feedback type."},{"name":"jira_status","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Repeatable. Filter by Jira sync state.","title":"Jira Status"},"description":"Repeatable. Filter by Jira sync state."},{"name":"severity","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Repeatable. Filter by bug severity.","title":"Severity"},"description":"Repeatable. Filter by bug severity."},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Org Id"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"}},{"name":"submitted_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Submitted From"}},{"name":"submitted_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Submitted To"}},{"name":"rating_min","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating Min"}},{"name":"rating_max","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating Max"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminFeedbackCount"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/feedback/stats":{"get":{"tags":["admin"],"summary":"Admin Feedback Stats","description":"Date-ranged rollups AND the three daily series (§25.7).\n\nThe series are the point. A rollup tells you where you are; only the daily\nline tells you that last Tuesday's deploy doubled the bug rate — so\n``daily.volume`` / ``daily.nps`` / ``daily.autopilot_rating`` are always\npresent, gap-filled across every day in the range including the zeros.\n\nWith no ``submitted_from`` / ``submitted_to`` the range defaults to the last\n30 days, and the resolved window is echoed back as ``range_start`` /\n``range_end`` so a reader never has to infer which range produced the\nnumbers.","operationId":"admin_feedback_stats_v1_admin_feedback_stats_get","parameters":[{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Repeatable. Filter by feedback type.","title":"Type"},"description":"Repeatable. Filter by feedback type."},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Repeatable. Filter by status.","title":"Status"},"description":"Repeatable. Filter by status."},{"name":"jira_status","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Repeatable. Filter by Jira sync state.","title":"Jira Status"},"description":"Repeatable. Filter by Jira sync state."},{"name":"severity","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Repeatable. Filter by bug severity.","title":"Severity"},"description":"Repeatable. Filter by bug severity."},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Org Id"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"}},{"name":"submitted_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Submitted From"}},{"name":"submitted_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Submitted To"}},{"name":"rating_min","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating Min"}},{"name":"rating_max","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating Max"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminFeedbackStats"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/feedback/{feedback_id}":{"get":{"tags":["admin"],"summary":"Get Admin Feedback","description":"One report in full, with a short-TTL presigned screenshot GET (§25.7).\n\nThe screenshot URL forces ``Content-Disposition: attachment`` — a security\ncontrol, not a UX choice (§25.16 A05); see\n:func:`app.services.files.presign_admin_screenshot_download`.\n\nReading a ``new`` row does NOT triage it. §25.7.1's auto-triage is a PATCH\nthe detail page issues, which is what makes it audited and what makes it\ncorrectly unavailable to ``platform_support``.","operationId":"get_admin_feedback_v1_admin_feedback__feedback_id__get","parameters":[{"name":"feedback_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Feedback Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminFeedbackDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"patch":{"tags":["admin"],"summary":"Update Admin Feedback","description":"Set ``status`` and/or ``admin_notes`` (§25.7).\n\nAlso the §25.7.1 auto-triage call: the detail page PATCHes ``new →\ntriaged`` on open, which is why that transition is audited like any other\nand why ``platform_support`` — refused at :func:`_require_mutator` — cannot\nsilently drain the queue by browsing it.\n\nA no-op PATCH (both fields already at the submitted values) returns the row\nunchanged and writes NO audit row. An audit trail whose entries do not\ncorrespond to changes is a trail nobody can reason about.","operationId":"update_admin_feedback_v1_admin_feedback__feedback_id__patch","parameters":[{"name":"feedback_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Feedback Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFeedbackRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminFeedbackDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/feedback:bulkUpdate":{"post":{"tags":["admin"],"summary":"Bulk Update Admin Feedback","description":"One status across up to 200 ids, in ONE transaction (§25.7).\n\nTriaging a screenful must be one request, not N parallel PATCHes — each of\nwhich would be its own transaction, so a failure halfway through would leave\nthe operator looking at a half-triaged page with no way to tell which half.\nHere every row lands or none does: the single ``db.commit()`` at the end is\nthe whole of it, and any raise before it leaves the session for ``get_db``\nto close and roll back.\n\nOne audit row per **changed** id — not per submitted id. An id that already\nholds the target status is an idempotent no-op and writes nothing, which is\nwhat keeps \"count the audit rows\" a valid way to ask what a bulk call did.","operationId":"bulk_update_admin_feedback_v1_admin_feedback_bulkUpdate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUpdateFeedbackRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/admin/feedback/{feedback_id}:file-issue":{"post":{"tags":["admin"],"summary":"File Feedback Issue","description":"Promote a `skipped` row into the Jira queue (§25.7).\n\nThe NPS comment that turned out to be a bug. Idempotent no-op when the row\nis already ``synced``.","operationId":"file_feedback_issue_v1_admin_feedback__feedback_id__file_issue_post","parameters":[{"name":"feedback_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Feedback Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminFeedbackDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/feedback/{feedback_id}:retry-jira":{"post":{"tags":["admin"],"summary":"Retry Feedback Jira","description":"Re-drive a `failed` row (§25.7).\n\nMechanically identical to ``:file-issue`` — both put the row back in the\nworker's queue — but a DISTINCT endpoint and a distinct audit event,\nbecause the two answer different operator questions (\"this survey response\nis actually a bug\" vs \"Jira was down, try again\") and §25.7 names them\nseparately. Collapsing them would make the audit trail unable to say which\nhappened.","operationId":"retry_feedback_jira_v1_admin_feedback__feedback_id__retry_jira_post","parameters":[{"name":"feedback_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Feedback Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminFeedbackDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/feedback:bulkFileIssue":{"post":{"tags":["admin"],"summary":"Bulk File Feedback Issues","description":"Re-drive up to 200 `skipped` / `failed` rows into Jira, in one transaction.\n\n**This endpoint is a spec delta** — see the module docstring. §25.7 gives\nonly the per-row ``:file-issue``, which is a fine answer to \"this one survey\nresponse is actually a bug\" and no answer at all to the situation we are\nactually shipping into: Jira deliberately unconfigured, the §25.6 worker\nfail-softing every row to ``skipped``, and a backlog that grows for as long\nas the API token is missing. When the token lands, the recovery has to be\none request.\n\nSame cap, same single transaction, same ``feedback.issue_filed`` event as\nthe per-row form — it is the bulk shape of an operation §25.7 already\nsanctions, not a new capability. ``synced`` and ``pending`` rows are\nidempotent no-ops (see :func:`_redrive_jira`), so the natural recovery\ngesture — select everything, re-drive — is safe to repeat.","operationId":"bulk_file_feedback_issues_v1_admin_feedback_bulkFileIssue_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkFileIssueRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/admin/feedback/{feedback_id}:reply":{"post":{"tags":["admin"],"summary":"Reply To Feedback","description":"Email the submitter through the §22.7 durable outbox (§25.8).\n\nNever sent inline on the request thread: the operator gets one fast 200 and\nthe SES call, its retries and its dead-lettering all happen out-of-band —\nwhich is why the response says ``queued`` rather than ``sent``.\n\nRequires a submitter with ``kind='human'`` and a deliverable address,\notherwise 400. All three refusals are real cases, not defensive padding: an\nagent files reports through §25.11 and has no inbox; a §25.10 erasure NULLs\n``user_id`` and leaves a row nobody can be written back to; and a service\nprincipal never had a user at all.\n\nThe message is HTML-escaped in the template\n(:func:`app.services.email._feedback_reply_html`) — paperlives interpolated\nthe raw operator string into ``<p>{message}</p>``, an HTML-injection bug we\ndo not reproduce.","operationId":"reply_to_feedback_v1_admin_feedback__feedback_id__reply_post","parameters":[{"name":"feedback_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Feedback Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackReplyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackReplyResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/properties":{"get":{"tags":["admin"],"summary":"List Bx Properties","description":"The property registry, for the viewer's property selector (§26.3).\n\nUnpaginated — the registry holds one row today and is not a growth surface —\nbut wrapped in the house list envelope anyway, so a client never has to know\nwhich lists in this API paginate and which do not. ``has_more`` is a\ntruthful ``false`` rather than a placeholder.","operationId":"list_bx_properties_v1_admin_bx_properties_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_BxPropertyRow_"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/admin/bx/config":{"get":{"tags":["admin"],"summary":"Get Bx Config","description":"One property's editable configuration (§26.7, VIEW-7).\n\n``?property=`` is the **slug**, like everywhere else on this surface. A key\nis not accepted here even though it would resolve unambiguously today: the\nkey is the half that rotates, and a config editor addressable by it would\nstop working at the moment it was used to rotate.","operationId":"get_bx_config_v1_admin_bx_config_get","parameters":[{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxPropertyConfig"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"patch":{"tags":["admin"],"summary":"Patch Bx Config","description":"Edit the registry without a deploy — TRK-8's standing requirement.\n\nThe tracker picks the change up on its next config poll, because\n``GET /v1/beacon/config`` serves these columns directly; nothing is cached\nbetween the two. That is the whole mechanism, and it is why masking, mode,\nsampling and the route exclusions are columns rather than settings.\n\n**A request that changes nothing writes no audit row.** SEC-4 wants config\nchanges audited, and a trail that records \"changed masking to strict\" when\nmasking was already strict is a trail with false entries in it — which\nmatters precisely because this is the log someone reads after an incident\nto find out when a property's masking was weakened.","operationId":"patch_bx_config_v1_admin_bx_config_patch","parameters":[{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxPropertyConfigPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxPropertyConfig"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/config:rotate-key":{"post":{"tags":["admin"],"summary":"Rotate Bx Key","description":"Mint a new ingest key for one property (SEC-5, WIRE-2).\n\nThe colon-verb shape is the house one — see\n``POST /v1/webhooks/{id}:rotate-secret`` — and so is the guarantee: the old\nkey stops resolving from the next beacon on. Unlike a webhook secret there\nis no graceful overlap, and there cannot be one: the key identifies the\nproperty rather than authenticating a message, so accepting two at once\nwould mean accepting the key we are trying to stop accepting.\n\n**This makes the property's own pages go quiet** until they ship the new\nkey, which is deliberate — the key lives in the page (WIRE-2) and rotating\nit is how an operator cuts off a caller who copied it. The property's\nhistory is untouched: rows, objects, links and audit entries all cite the\nslug, which does not move.","operationId":"rotate_bx_key_v1_admin_bx_config_rotate_key_post","parameters":[{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxKeyRotation"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/emergency-stop":{"get":{"tags":["admin"],"summary":"Get Bx Emergency Stop","description":"Is capture globally halted, and who halted it (TRK-8)?\n\nSupport tier, unlike the rest of the config surface: this is the sentence\nthat explains why every chart on the dashboard has flatlined, and making an\noperator escalate to read it would mean the stop looks exactly like an\noutage to the people most likely to be investigating one.","operationId":"get_bx_emergency_stop_v1_admin_bx_emergency_stop_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxEmergencyStopState"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]},"post":{"tags":["admin"],"summary":"Engage Bx Emergency Stop","description":"Stop capture on every property at once (TRK-8).\n\nBlunter than ``enabled`` on a property row and reached for when nobody yet\nknows which property is the problem — a beacon carrying a token in a route\nid, a masking rule that turned out not to hold. Live trackers go quiet\nwithin one config poll; anything already in flight is dropped on arrival.\n\n**Idempotent, and re-engaging writes no second audit row.** The stop that\nis already in force keeps its original reason and its original author,\nwhich is the §4.7 agent-pause precedent (\"already-paused ⇒ no-op, no\ndouble-emit\"): a second row would claim a second person stopped capture\nthat was already stopped.\n\nWhat keeps running: the rollup and retention workers. \"Stop collecting\" is\nnever \"stop deleting\" — retention is what bounds the data an incident\nalready produced.","operationId":"engage_bx_emergency_stop_v1_admin_bx_emergency_stop_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxEmergencyStopRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxEmergencyStopState"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]},"delete":{"tags":["admin"],"summary":"Release Bx Emergency Stop","description":"Resume capture (TRK-8). Idempotent; releasing a running system is a no-op.\n\nAnswers 200 either way rather than 404-ing when nothing was stopped: the\ncaller's intent is \"capture should be running\", and that is the state they\nget. A 404 here would also be the one place on this surface where the\nstatus code carried information about platform state to a caller already\nentitled to read it from the GET.","operationId":"release_bx_emergency_stop_v1_admin_bx_emergency_stop_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxEmergencyStopState"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/admin/bx/overview":{"get":{"tags":["admin"],"summary":"Bx Overview","description":"VIEW-2's dashboard: traffic, friction trend, top signals, capture health.\n\nOne request rather than five, because the five are one screen and a\ndashboard that paints in five waves reads as broken. Every series is\ngap-filled across the resolved window — a day with no rows is a day with no\ntraffic, and dropping it would draw a line that skips over the outage it is\nsupposed to reveal.\n\nThe resolved window is echoed back as ``range_start`` / ``range_end`` so a\nreader never has to infer which range produced the numbers (the §25.7 stats\nconvention).","operationId":"bx_overview_v1_admin_bx_overview_get","parameters":[{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"days","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":400,"minimum":1},{"type":"null"}],"description":"Window length in days, ending today (UTC). Defaults to 14.","title":"Days"},"description":"Window length in days, ending today (UTC). Defaults to 14."},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxOverview"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/routes":{"get":{"tags":["admin"],"summary":"List Bx Routes","description":"Every (route × viewport bucket) with traffic, and whether it can be mapped.\n\nThe heatmap picker's source. It carries ``floor_met`` and ``has_snapshot``\nso the operator learns *before* navigating that a route has 12 pageviews or\nno stored canvas — HEAT-5's floor is a normal state for a young property,\nand discovering it after the click makes the product feel broken rather than\nhonest.","operationId":"list_bx_routes_v1_admin_bx_routes_get","parameters":[{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"days","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":400,"minimum":1},{"type":"null"}],"description":"Window length in days, ending today (UTC). Defaults to 14.","title":"Days"},"description":"Window length in days, ending today (UTC). Defaults to 14."},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_BxRouteCoverage_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/insights":{"get":{"tags":["admin"],"summary":"List Bx Insights","description":"VIEW-3's ranked stuck points, highest score first.\n\n**No floor is applied here.** HEAT-5's insight floors (≥10 affected sessions\nAND ≥1% of the route's sessions) are enforced by the rollup worker's INSERT,\nso a below-floor card is not a row. Re-applying them at read time would put\nthe same rule in two places, where the two would eventually disagree and the\nviewer would silently hide cards the worker had already blessed. An empty\nlist here means no stuck point cleared the bar — which is a result, not a\nfloor state, and the UI says so differently.\n\nPaged by offset rather than keyset, the ``admin_feedback`` precedent for a\nnon-datetime sort: the worker replaces a property's whole card set each\npass, so every row shares a ``created_time`` and the keyset cursor would be\nordering by a column the query does not sort on.","operationId":"list_bx_insights_v1_admin_bx_insights_get","parameters":[{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"route_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Route Id"}},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Kind"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_BxInsightCard_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/heatmap":{"get":{"tags":["admin"],"summary":"Bx Heatmap","description":"VIEW-4: density, elements, scrollmap and the canvas to draw them over.\n\n``route_id`` and ``viewport_bucket`` are both required and neither has a\ndefault. HEAT-1 forbids mixing buckets, and a defaulted bucket would quietly\nanswer a question the caller did not ask — \"the desktop heatmap\" served to\nsomeone who thought they were looking at all traffic.\n\n``epoch`` narrows to one deploy; omitted, the payload spans every epoch in\nthe window. Two calls with different epochs are the compare mode, which is\nwhy this is a parameter rather than a separate endpoint: the two halves of a\ncomparison must be produced by identical code or the difference between them\nis not attributable to the deploy.","operationId":"bx_heatmap_v1_admin_bx_heatmap_get","parameters":[{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"route_id","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":255,"title":"Route Id"}},{"name":"viewport_bucket","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":2,"title":"Viewport Bucket"}},{"name":"days","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":400,"minimum":1},{"type":"null"}],"description":"Window length in days, ending today (UTC). Defaults to 14.","title":"Days"},"description":"Window length in days, ending today (UTC). Defaults to 14."},{"name":"epoch","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Epoch"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxHeatmap"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/sessions":{"get":{"tags":["admin"],"summary":"List Bx Sessions","description":"Filtered session search — every filter server-side (VIEW-5's list half).\n\nRoutes are filtered as ``entry_route`` / ``exit_route`` and not as a single\nfuzzy \"route\", because ``bx_sessions`` records only those two: a session's\nfull route history lives in ``bx_events``, which this handler must not read.\nTwo named filters that mean exactly what they say beat one that quietly\nmeans less than the operator assumes.\n\n``sort=started_at`` (default) is the keyset walk on ``(started_at, id)``;\n``sort=friction`` is VIEW-5's worst-first ordering and takes the offset\ncursor, the same split ``admin_feedback`` makes for its non-date sorts. Both\nhand back an opaque ``next_cursor``, so a client never sees the difference.\n\n``has_replay`` and the ``has_replay`` column on every row are resolved\nagainst ``bx_replay_chunks`` — the filter as a semi-join inside the page\nquery, the column as one indexed lookup over the page's ids afterwards.\nBoth answer from the same table, so a row that the filter admitted always\nrenders as watchable.","operationId":"list_bx_sessions_v1_admin_bx_sessions_get","parameters":[{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"entry_route","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Entry Route"}},{"name":"exit_route","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Exit Route"}},{"name":"frustrated","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Frustrated"}},{"name":"min_friction","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"title":"Min Friction"}},{"name":"has_errors","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Errors"}},{"name":"device_class","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Device Class"}},{"name":"user_kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}],"title":"User Kind"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Org Id"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"}},{"name":"visitor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Visitor"}},{"name":"started_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started From"}},{"name":"started_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started To"}},{"name":"include_internal","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Internal"}},{"name":"has_replay","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Replay"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","default":"started_at","title":"Sort"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_BxSessionRow_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/sessions/{session_id}":{"get":{"tags":["admin"],"summary":"Get Bx Session","description":"One visit, keyed by the **client-generated session id**, not the row UUID.\n\nThat choice is what makes an insight card's ``sample_session_id`` a link:\nthe worker stores the wire id, and a detail route keyed on the surrogate\nwould force the viewer to run a lookup query to follow its own deep link.\nThe id is unique only within a property (§26.3), which is why ``?property=``\nis part of the lookup rather than a check performed after it.\n\nNo event timeline here, and no audit row. Both belong to\n``/sessions/{id}/replay``: reading a visit's *counts* is not watching the\nvisit, and SEC-4 audits the watching. Keeping them apart is what lets an\noperator triage a session list without every glance becoming an entry in\nthe record of who watched whose screen.","operationId":"get_bx_session_v1_admin_bx_sessions__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxSessionDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["admin"],"summary":"Delete Bx Session","description":"Destroy one session — objects, chunk rows, and the session row (PRIV-7).\n\nCNIL's S2 tier requires an operator who opens a recording and sees something\nthey should not be watching to be able to destroy it without filing a\nticket. So this is a viewer feature, not a DBA task, and it is immediate:\nafter it returns, the session 404s in the list, the detail and the chunk\nendpoints, because the row itself is gone rather than merely emptied.\n\n``delete_events`` is off by default. This deletes a *recording*, and the\nevents are what every rollup above it was computed from — destroying them\nby default would leave the session missing from the list while its clicks\nstayed in the charts. The DSAR cascade turns it on; see\n``DELETE /users/{user_id}``.","operationId":"delete_bx_session_v1_admin_bx_sessions__session_id__delete","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"delete_events","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Delete Events"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxDeletion"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/sessions/{session_id}/replay":{"get":{"tags":["admin"],"summary":"Open Bx Replay","description":"Open one recording: chunk index, presigned downloads, REP-7 timeline.\n\n**This request IS the view, and it writes exactly one audit row** (SEC-4 /\nCNIL S2). Everything the player needs arrives here so that \"a view\" is one\nwell-defined event: split across a chunk-list call and a timeline call, one\nviewing would write two rows or leave the trail depending on which endpoint\nthe client happened to call first.\n\nA session with no stored chunks is a **404, and writes no audit row.** There\nis nothing to watch, so nobody watched anything; an audit trail that\nrecorded attempts alongside viewings would make the count of \"who has seen\nthis person's screen\" an over-count, which is the one direction a privacy\nrecord must not err in. It is also the answer a session whose recording\nretention has already reclaimed gives, which is correct: the recording is\ngone, and the row that survives it is a set of counts, not a screen.\n\nThe audit row is committed before the response is built. ``get_db`` does not\ncommit — it opens a session and closes it — so a handler that merely\n``log_audit``-ed here would flush a row that is rolled back on close, and\nthe trail would be empty in production while a test sharing the session's\ntransaction saw it perfectly well.","operationId":"open_bx_replay_v1_admin_bx_sessions__session_id__replay_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxReplayOpen"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/sessions/{session_id}/replay/chunks/{seq}":{"get":{"tags":["admin"],"summary":"Get Bx Replay Chunk","description":"One chunk's bytes, same-origin, still gzipped.\n\nThe twin of ``/snapshots/{id}/document`` and it exists for the identical\nreason (§26.7): the player must *read* these bytes to hand rrweb an event\narray, and a ``fetch()`` of the presigned S3 URL is cross-origin against a\nbucket with no CORS rules — which we decline to provision, because doing so\nwould make every object in that bucket script-readable from our origin to\nsolve a problem one endpoint has. SEC-3 names both mechanisms in one breath\n(\"short-TTL signed URLs **or** authenticated streaming proxy\"), so the\nstandard is met and no S3 credential reaches the browser.\n\nNot audited, deliberately — see the module docstring. One viewing pulls\nevery chunk, and forty rows per view would destroy the trail's meaning.\n\nThe body is passed through **exactly as stored**: nothing decompresses a\nvisitor's recording server-side, which keeps REP-2 true on the read path and\nkeeps a gzip bomb in the bucket from being materialized here.","operationId":"get_bx_replay_chunk_v1_admin_bx_sessions__session_id__replay_chunks__seq__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"seq","in":"path","required":true,"schema":{"type":"integer","title":"Seq"}},{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/users/{user_id}":{"delete":{"tags":["admin"],"summary":"Erase Bx User","description":"PRIV-7's delete-by-user cascade — the DSAR erasure, across every property.\n\nNot ``?property=``-scoped, and that is the one deliberate exception to this\nmodule's scoping rule. A subject request is about a person, not about a\nproperty: an erasure that covered the property the operator happened to have\nselected would be an erasure that silently missed the rest, which is the\nfailure mode with legal consequences.\n\n**The rollups are untouched, by design.** They carry no user dimension —\na rollup row is a count of clicks on a route on a day — so there is nothing\nin them to erase, and rewriting them would retroactively rewrite last\nquarter's numbers every time somebody closed an account. §25.10 made exactly\nthis call for the NPS trend; ``tests/security`` asserts it here.\n\n**This is the function the account-deletion flow will call**, and today it\nis reached by an operator answering a subject request by hand: §24.6 defers\nright-to-erasure, so there is no account-deletion flow to hook into yet.\nReturning a 200 with zeroes for a user who was never recorded is correct and\ndeliberate — a 404 would turn this endpoint into an oracle for whether a\ngiven user id has ever browsed the product.","operationId":"erase_bx_user_v1_admin_bx_users__user_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"delete_events","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Delete Events"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxDeletion"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/snapshots":{"get":{"tags":["admin"],"summary":"List Bx Snapshots","description":"Canvases for one property, newest first (§26.3).\n\nNo presigned URLs on the rows: this is a browse surface, and minting one\nlive S3 credential per row to answer \"what exists\" would hand out N of them\nto open zero canvases. The detail route issues exactly one, for the canvas\nactually being opened.\n\nKeyset pagination on ``(captured_at, id)`` via the shared\n:mod:`app.core.pagination` helpers, the same shape ``admin_feedback`` uses.\nThe sort column is ``captured_at`` rather than ``created_time`` because an\nupsert moves the former and leaves the latter — ordering by the column the\npage does not sort on would put a re-captured canvas back in the middle of\nthe list. The tiebreaker on ``id`` is what makes the keyset total: two\ncanvases captured in the same millisecond are common (one route, three\nbuckets, one flush) and without it a page boundary landing between them\nwould drop or repeat rows.\n\nOne consequence worth naming: ``captured_at`` is mutable, so a canvas\nre-captured mid-pagination moves to the front and can be seen twice or not\nat all. That is inherent to keyseting on a column that moves, and it is the\nright trade here — the alternative sorts a \"newest canvas\" list by a\ntimestamp that is not the canvas's age.","operationId":"list_bx_snapshots_v1_admin_bx_snapshots_get","parameters":[{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"route_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Route Id"}},{"name":"viewport_bucket","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2},{"type":"null"}],"title":"Viewport Bucket"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Page Size"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_BxSnapshotSummary_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/snapshots/{snapshot_id}":{"get":{"tags":["admin"],"summary":"Get Bx Snapshot","description":"One canvas plus a short-TTL presigned GET (§26.8).\n\nThe URL forces ``Content-Disposition: attachment``, mirroring the feedback\nscreenshot presign it is modeled on — a security control there and the same\none here, since a stored canvas is a visitor's page serialized and is\ntherefore attacker-influenced content on an AWS-hosted origin. The viewer\n``fetch()``es these bytes rather than navigating to them, so the header\ncosts it nothing.\n\n``url`` is ``None`` when presigning failed. A storage blip degrades one\ncanvas to \"cannot open right now\" instead of 500-ing the page around it.","operationId":"get_bx_snapshot_v1_admin_bx_snapshots__snapshot_id__get","parameters":[{"name":"snapshot_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Snapshot Id"}},{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BxSnapshotDownload"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/admin/bx/snapshots/{snapshot_id}/document":{"get":{"tags":["admin"],"summary":"Get Bx Snapshot Document","description":"The stored canvas's bytes, same-origin (NOV-619).\n\n**Why a proxy exists beside a perfectly good presigned URL.** The heatmap\nview has to *read* the document — rebuild it into a sandboxed iframe and\ndraw coordinates over it — and a browser ``fetch()`` of an S3 URL is a\ncross-origin request that needs CORS rules on the uploads bucket. None are\nprovisioned, and provisioning them is neither this ticket's work nor\nobviously desirable: they would make every object in that bucket\nscript-readable from our origin to solve a problem one endpoint has. So the\nviewer reads canvases through the API and no S3 credential reaches the\nbrowser at all. The presign route is untouched and remains the download\npath. (NOV-618's docstring assumed the fetch would work directly; it would\nhave, with bucket CORS.)\n\nThe body is passed through **exactly as stored** — still gzipped, declared\nas such — so nothing decompresses a visitor-supplied document server-side.\nThat keeps REP-2's \"no server-side pass over the DOM\" true of the read path\nas well as the write path, and it keeps a gzip bomb in the bucket from being\na bomb here.\n\n``Content-Disposition: attachment`` matches the presign for the same reason\nit is set there: this is attacker-influenced markup, and pasting the URL\ninto an address bar must download it rather than render it on our origin.\nA ``fetch()`` ignores the header entirely, so the viewer pays nothing.","operationId":"get_bx_snapshot_document_v1_admin_bx_snapshots__snapshot_id__document_get","parameters":[{"name":"snapshot_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Snapshot Id"}},{"name":"property","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Property"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/search":{"post":{"tags":["search"],"summary":"Search","description":"Full-text search across the caller's org (spec §8.2).\n\nNOV-457: the service still assembles the Notion-shape envelope as a dict —\nthe MCP wrapper consumes it in that form — so the router validates it into\nthe declared model. That is deliberate: the published schema is then the\nsame object the response is serialized from, rather than a promise made\nalongside an untyped blob.","operationId":"search_v1_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_SearchHitResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}]}},"/v1/orgs/{org_id}/export":{"get":{"tags":["export"],"summary":"Export Org","description":"Export the entire workspace as one JSON document (owner/admin only).\n\nKeeps the plain ``get_principal`` gate on purpose (§4.8): the workspace is\nalready named in the path, and ``build_org_export`` binds the tenant scope\nto it and probes membership itself. Layering ``X-Novum-Org`` on top of a\npath that already says which workspace it means could only ever contradict\nit — the board-level sibling below, which has no org in its path, is the one\nthat takes the header.","operationId":"export_org_v1_orgs__org_id__export_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Org Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/boards/{board_id}/export":{"get":{"tags":["export"],"summary":"Export Board","description":"Export a single board (columns + cards + block bodies + comments).","operationId":"export_board_v1_boards__board_id__export_get","parameters":[{"name":"board_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Board Id"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."},{"name":"X-Novum-Org","in":"header","required":false,"schema":{"type":"string","format":"uuid"},"description":"Optional. Selects the workspace this request acts in. An integration token is **pinned** to its own workspace — a conflicting value is `404`. For a browser session the value is re-checked against live membership on every request: it can only ever narrow to a workspace you already belong to, never widen. Omit it to act in the session's active workspace."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/webhooks":{"get":{"tags":["webhooks"],"summary":"List Webhooks","description":"List webhooks visible to the caller's org. Newest first.","operationId":"list_webhooks_v1_webhooks_get","parameters":[{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_WebhookResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"post":{"tags":["webhooks"],"summary":"Create Webhook","operationId":"create_webhook_v1_webhooks_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}],"parameters":[{"name":"X-Board-Version","in":"header","required":true,"schema":{"type":"string","default":"2026-06-19"},"description":"Required on every `/v1` request. Send `2026-06-19`. The middleware validates *presence*, not the value, so an older pinned date keeps working; `Notion-Version` is accepted as a Notion-SDK compatibility fallback when this header is absent."}]}},"/v1/webhooks/{webhook_id}":{"get":{"tags":["webhooks"],"summary":"Get Webhook","operationId":"get_webhook_v1_webhooks__webhook_id__get","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Webhook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"patch":{"tags":["webhooks"],"summary":"Update Webhook","operationId":"update_webhook_v1_webhooks__webhook_id__patch","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Webhook Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]},"delete":{"tags":["webhooks"],"summary":"Delete Webhook","operationId":"delete_webhook_v1_webhooks__webhook_id__delete","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Webhook Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/webhooks/{webhook_id}:rotate-secret":{"post":{"tags":["webhooks"],"summary":"Rotate Webhook Secret","description":"Mint a new secret. Old one is unusable from the next request on.\n\nReturns the new plaintext exactly once. There's a brief window between\nrotation and the receiver knowing about the new key, during which\nin-flight deliveries (already signed with the OLD secret) keep flowing.\nThat's intentional — pending rows were signed before rotation and the\nreceiver should still accept them; in steady state new events use the\nnew key.","operationId":"rotate_webhook_secret_v1_webhooks__webhook_id__rotate_secret_post","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Webhook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/webhooks/{webhook_id}/deliveries":{"get":{"tags":["webhooks"],"summary":"List Deliveries","operationId":"list_deliveries_v1_webhooks__webhook_id__deliveries_get","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Webhook Id"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}},{"name":"start_cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_WebhookDeliveryResponse_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/webhooks/{webhook_id}/deliveries/{delivery_id}:replay":{"post":{"tags":["webhooks"],"summary":"Replay Delivery","description":"Re-enqueue a past delivery. Same event_id so receivers dedup correctly.","operationId":"replay_delivery_v1_webhooks__webhook_id__deliveries__delivery_id__replay_post","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Webhook Id"}},{"name":"delivery_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Delivery Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"IntegrationToken":[]}]}},"/v1/sse/notifications":{"get":{"tags":["realtime"],"summary":"Notifications Sse","description":"Stream the caller's PRIVATE notifications over SSE (spec §22.3, NOV-362).\n\nThe SSE fallback for ``/v1/ws/notifications`` — same auth model, same\nper-user channel derived from the authenticated principal (never a\nclient-supplied id), for environments where WebSockets are blocked. 401 if\nanonymous; 404 if the principal has no attached user (no inbox), mirroring\nthe REST posture (defense-in-depth: the auth layer already rejects\nuserless integration tokens, NOV-299, so real tokens land on 401 first).\nOrg-bound integration tokens receive ONLY their org's frames\n(``user_frame_permitted``); credentials are re-validated mid-stream every\n``USER_CHANNEL_REAUTH_INTERVAL_S`` and the stream ends with\n``subscription.closed`` (``unauthorized``) once they no longer resolve.","operationId":"notifications_sse_v1_sse_notifications_get","parameters":[{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sse":{"get":{"tags":["realtime"],"summary":"Board Sse","description":"Stream a board's realtime events over SSE (spec §22.3, NOV-28).\n\nNOV-392 / spec §9.2: ``?last_sequence=N`` resumes a reconnecting client by\nreplaying events with ``sequence > N`` before live streaming starts — full\nparity with the ``/v1/ws`` gateway (the SSE path previously had ZERO replay\nand silently lost every event created during a reconnect gap). If ``N``\npredates the retention window the server emits a ``snapshot_required`` frame\nand closes, and the client falls back to a full REST refetch.","operationId":"board_sse_v1_sse_get","parameters":[{"name":"board_id","in":"query","required":true,"schema":{"type":"string","title":"Board Id"}},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"}},{"name":"last_sequence","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Last Sequence"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/healthz":{"get":{"tags":["meta"],"summary":"Health","operationId":"health_healthz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":"object","title":"Response Health Healthz Get"}}}}}}}},"components":{"schemas":{"AccessUser":{"properties":{"object":{"type":"string","title":"Object","default":"user"},"id":{"type":"string","format":"uuid","title":"Id"},"kind":{"type":"string","title":"Kind"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"avatar":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar"}},"additionalProperties":false,"type":"object","required":["id","kind"],"title":"AccessUser","description":"The grantee, shaped like the Notion person reference (§5.1)."},"ActiveWorkspaceResponse":{"properties":{"object":{"type":"string","title":"Object","default":"active_workspace"},"id":{"type":"string","format":"uuid","title":"Id"},"slug":{"type":"string","title":"Slug"}},"type":"object","required":["id","slug"],"title":"ActiveWorkspaceResponse","description":"The workspace a fresh page load lands in (§4.8, NOV-534).\n\nDeliberately just the identity of the workspace: the id the SPA binds to\n``X-Novum-Org`` and the slug it navigates to, so learning where to land\ncosts no second id-to-slug round trip. Everything else about the workspace\nalready comes from ``GET /v1/orgs``."},"AddAgentTokenRequest":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"role":{"type":"string","enum":["member","admin"],"title":"Role","default":"member"}},"type":"object","required":["name"],"title":"AddAgentTokenRequest","description":"Body for POST /v1/orgs/{org_id}/agents/{agent_user_id}/tokens (NOV-190).\n\nMints an additional Integration (credential) owned by an EXISTING agent —\nthe rotation / multi-Lambda-fleet case (spec §4.2, §4.5). Tokens are\nchildren of an agent; adding one does not add a seat (the agent already\ncounts), so there is no billing recompute."},"AddMemberRequest":{"properties":{"email":{"type":"string","title":"Email"},"role":{"type":"string","title":"Role","default":"member"}},"type":"object","required":["email"],"title":"AddMemberRequest"},"AdminAuditRow":{"properties":{"object":{"type":"string","title":"Object","default":"audit_event"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Org Id"},"actor_kind":{"type":"string","title":"Actor Kind"},"actor_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Actor Id"},"event_type":{"type":"string","title":"Event Type"},"target_kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Kind"},"target_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Target Id"},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"occurred_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Occurred At"}},"type":"object","required":["id","actor_kind","event_type","created_at"],"title":"AdminAuditRow"},"AdminBillingOrgRow":{"properties":{"object":{"type":"string","title":"Object","default":"billing_org"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"plan":{"type":"string","title":"Plan","default":"free"},"status":{"type":"string","title":"Status","default":"active"},"billable_seats":{"type":"integer","title":"Billable Seats","default":0},"monthly_revenue_usd":{"type":"integer","title":"Monthly Revenue Usd","default":0},"stripe_customer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Customer Id"},"stripe_subscription_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Subscription Id"},"current_period_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Current Period End"},"cancel_at_period_end":{"type":"boolean","title":"Cancel At Period End","default":false},"seats_synced_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Seats Synced At"},"drift":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Drift"}},"type":"object","required":["org_id","name","slug"],"title":"AdminBillingOrgRow","description":"One row of the admin billing overview (spec §18.10).\n\n``billable_seats`` is recomputed LIVE via ``count_billable_seats`` —\nnever the stale ``billing_seats`` cache. ``monthly_revenue_usd`` is a\nper-kind estimate on Pro (``humans × $1 + billable_agents × $1`` at the\nannual monthly-equivalent display rates — NOV-370), ``0`` on free. ``drift``\nis the reconciliation-cron descriptor (``None`` when reconciled)."},"AdminFeedbackCount":{"properties":{"object":{"type":"string","title":"Object","default":"feedback_count"},"new":{"type":"integer","title":"New","default":0},"poll_interval_seconds":{"type":"integer","title":"Poll Interval Seconds"}},"type":"object","required":["poll_interval_seconds"],"title":"AdminFeedbackCount","description":"``GET /v1/admin/feedback/count`` — the §25.7.1 nav-badge probe."},"AdminFeedbackDetail":{"properties":{"object":{"type":"string","title":"Object","default":"feedback"},"id":{"type":"string","format":"uuid","title":"Id"},"reference":{"type":"string","title":"Reference"},"type":{"type":"string","title":"Type"},"status":{"type":"string","title":"Status"},"severity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"},"exit_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exit Reason"},"rating":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating"},"surface":{"type":"string","title":"Surface"},"route":{"type":"string","title":"Route"},"content_preview":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content Preview"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"org_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Name"},"org_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Slug"},"org_plan":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Plan"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"submitter_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Submitter Email"},"submitter_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Submitter Name"},"principal_kind":{"type":"string","title":"Principal Kind"},"jira_status":{"type":"string","title":"Jira Status"},"jira_issue_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jira Issue Key"},"jira_issue_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jira Issue Url"},"has_screenshot":{"type":"boolean","title":"Has Screenshot","default":false},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"resolved_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Resolved At"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"},"expected":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expected"},"org_billing_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Billing Status"},"org_billing_seats":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Org Billing Seats"},"context":{"additionalProperties":true,"type":"object","title":"Context"},"admin_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Admin Notes"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"card_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Card Id"},"automation_run_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Automation Run Id"},"app_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"App Version"},"api_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Version"},"jira_attempts":{"type":"integer","title":"Jira Attempts","default":0},"jira_next_attempt_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Jira Next Attempt At"},"jira_last_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jira Last Error"},"screenshot_file_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Screenshot File Id"},"screenshot_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Screenshot Url"},"screenshot_url_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Screenshot Url Expires At"}},"type":"object","required":["id","reference","type","status","surface","route","org_id","principal_kind","jira_status","created_time"],"title":"AdminFeedbackDetail","description":"The full row plus everything the §25.7 detail page reads.\n\n``screenshot_url`` is a short-TTL presigned GET forced to\n``Content-Disposition: attachment`` (§25.7 / §25.16 A05) and is ``None``\nwhen there is no screenshot, when the upload never committed, or when the\n§25.3 prune has already dropped the object."},"AdminFeedbackRow":{"properties":{"object":{"type":"string","title":"Object","default":"feedback"},"id":{"type":"string","format":"uuid","title":"Id"},"reference":{"type":"string","title":"Reference"},"type":{"type":"string","title":"Type"},"status":{"type":"string","title":"Status"},"severity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"},"exit_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exit Reason"},"rating":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating"},"surface":{"type":"string","title":"Surface"},"route":{"type":"string","title":"Route"},"content_preview":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content Preview"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"org_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Name"},"org_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Slug"},"org_plan":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Plan"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"submitter_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Submitter Email"},"submitter_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Submitter Name"},"principal_kind":{"type":"string","title":"Principal Kind"},"jira_status":{"type":"string","title":"Jira Status"},"jira_issue_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jira Issue Key"},"jira_issue_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jira Issue Url"},"has_screenshot":{"type":"boolean","title":"Has Screenshot","default":false},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"resolved_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Resolved At"}},"type":"object","required":["id","reference","type","status","surface","route","org_id","principal_kind","jira_status","created_time"],"title":"AdminFeedbackRow","description":"One feedback row as the §25.7 triage list shows it."},"AdminFeedbackStats":{"properties":{"object":{"type":"string","title":"Object","default":"feedback_stats"},"range_start":{"type":"string","format":"date-time","title":"Range Start"},"range_end":{"type":"string","format":"date-time","title":"Range End"},"total":{"type":"integer","title":"Total","default":0},"volume_by_type":{"additionalProperties":{"type":"integer"},"type":"object","title":"Volume By Type"},"counts_by_status":{"additionalProperties":{"type":"integer"},"type":"object","title":"Counts By Status"},"nps":{"$ref":"#/components/schemas/NpsRollup"},"autopilot":{"$ref":"#/components/schemas/AutopilotRollup"},"unresolved_bugs_by_severity":{"additionalProperties":{"type":"integer"},"type":"object","title":"Unresolved Bugs By Severity"},"jira_sync_failures":{"type":"integer","title":"Jira Sync Failures","default":0},"top_orgs":{"items":{"$ref":"#/components/schemas/OrgVolumeRow"},"type":"array","title":"Top Orgs"},"top_routes":{"items":{"$ref":"#/components/schemas/RouteVolumeRow"},"type":"array","title":"Top Routes"},"response_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Response Rate"},"daily":{"$ref":"#/components/schemas/FeedbackDailySeries"}},"type":"object","required":["range_start","range_end"],"title":"AdminFeedbackStats","description":"``GET /v1/admin/feedback/stats`` (§25.7).\n\n``response_rate`` is the share of rows in the range that have left ``new``\n— the operator's own SLA proxy, and the reason it sits next to the volume\nnumbers rather than inside ``counts_by_status`` where it would have to be\nrecomputed by every reader."},"AdminOrgRow":{"properties":{"object":{"type":"string","title":"Object","default":"organization"},"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"primary_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Domain"},"billing_plan":{"type":"string","title":"Billing Plan","default":"free"},"billing_status":{"type":"string","title":"Billing Status","default":"active"},"billing_seats":{"type":"integer","title":"Billing Seats","default":0},"member_count":{"type":"integer","title":"Member Count","default":0},"board_count":{"type":"integer","title":"Board Count","default":0},"last_active_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Active At"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"}},"type":"object","required":["id","name","slug"],"title":"AdminOrgRow"},"AdminUserRow":{"properties":{"object":{"type":"string","title":"Object","default":"user"},"id":{"type":"string","format":"uuid","title":"Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"email_verified":{"type":"boolean","title":"Email Verified","default":false},"disabled":{"type":"boolean","title":"Disabled","default":false},"signup_source":{"type":"string","title":"Signup Source","default":"email"},"last_login_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Login At"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"orgs":{"items":{"$ref":"#/components/schemas/MembershipResponse"},"type":"array","title":"Orgs"}},"type":"object","required":["id"],"title":"AdminUserRow"},"AgentActivity":{"properties":{"agent_name":{"type":"string","title":"Agent Name"},"doing":{"type":"string","title":"Doing"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"}},"additionalProperties":false,"type":"object","required":["agent_name","doing"],"title":"AgentActivity","description":"NOV-86 / NOV-91: \"an agent is working on this card right now\" payload.\n\nStored as ``cards.agent_activity`` JSONB. Set via\n``POST /v1/cards/{id}/agent_activity``, cleared via the matching\nDELETE, the next mutation on the card, or a 10-minute TTL on read\n(NOV-91 lifecycle).\n\nThe model is strict (``extra=\"forbid\"`` + required fields) but\n``started_at`` is optional so legacy blobs written before NOV-91\ndon't fail validation. The outer ``CardResponse.agent_activity``\nvalidator silently downgrades a malformed stored blob to ``None``\nso a bad write from the agent runtime can't 500 every card read."},"AgentActivityEntry":{"properties":{"object":{"type":"string","title":"Object","default":"agent_activity_entry"},"action":{"type":"string","title":"Action"},"label":{"type":"string","title":"Label"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"card_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Card Id"},"outcome":{"type":"string","title":"Outcome"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["action","label","outcome","created_at"],"title":"AgentActivityEntry","description":"One reverse-chron feed row — a single successful agent action."},"AgentActivityResponse":{"properties":{"object":{"type":"string","title":"Object","default":"agent_activity"},"agent_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Agent User Id"},"summary":{"$ref":"#/components/schemas/AgentActivitySummary"},"entries":{"items":{"$ref":"#/components/schemas/AgentActivityEntry"},"type":"array","title":"Entries"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["summary","entries"],"title":"AgentActivityResponse","description":"Response for the per-agent and all-agents activity endpoints (spec §4.7 ledger).\n\n``agent_user_id`` is set for the per-agent view and ``None`` for the org-wide\nrollup. ``entries`` is the cursor-paginated feed; ``next_cursor`` is present\nonly when ``has_more``."},"AgentActivitySummary":{"properties":{"object":{"type":"string","title":"Object","default":"agent_activity_summary"},"by_action":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Action"},"errors":{"type":"integer","title":"Errors"},"window":{"type":"string","title":"Window"}},"type":"object","required":["by_action","errors","window"],"title":"AgentActivitySummary","description":"Rollup line for the activity drawer — \"47 drafts, 3 card-moves, 0 errors · 24h\".\n\n``by_action`` maps a human label (``draft`` / ``card-move`` / ``card-create``\n/ ``card-update``) to its count within ``window``. ``errors`` = ledger\nfailures (0 in MVP) PLUS ``agent.request_denied`` budget denials in-window\n(see :func:`app.services.agent_ledger.agent_activity_summary`)."},"AgentBudgetResponse":{"properties":{"object":{"type":"string","title":"Object","default":"agent_budget"},"agent_user_id":{"type":"string","format":"uuid","title":"Agent User Id"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"writes_per_day":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Writes Per Day"},"requests_per_sec":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Requests Per Sec"},"auto_pause":{"type":"boolean","title":"Auto Pause","default":false},"paused":{"type":"boolean","title":"Paused"},"paused_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Paused Reason"},"paused_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Paused At"},"usage":{"$ref":"#/components/schemas/AgentBudgetUsage"}},"type":"object","required":["agent_user_id","paused","usage"],"title":"AgentBudgetResponse","description":"Response for GET / PUT of an agent's budget (spec §4.7 budgets).\n\nSurfaces the two caps plus the read-only kill-switch state (``paused`` /\n``paused_reason`` / ``paused_at`` — the kill switch is mutated only via\n:pause / :resume, but is DISCOVERABLE here per the team-lead ruling) and live\ncounter ``usage``. ``board_id`` is ``null`` for the ORG-WIDE budget and set\nfor a per-board override (NOV-271)."},"AgentBudgetUpdateRequest":{"properties":{"writes_per_day":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Writes Per Day"},"requests_per_sec":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Requests Per Sec"},"auto_pause":{"type":"boolean","title":"Auto Pause","default":false}},"type":"object","title":"AgentBudgetUpdateRequest","description":"Body for PUT /v1/orgs/{org_id}/agents/{agent_user_id}/budget.\n\nPUT-replace semantics: each cap is set to the supplied value; a field set to\n``null`` (or omitted) CLEARS that cap (unlimited / fall back to the flat\nintegration limit). Each must be ``null`` or a non-negative int (``ge=0``);\na negative value fails validation (422). This never touches paused /\nprovenance — that is the kill switch's job (:pause / :resume).\n\n``auto_pause`` (NOV-272) toggles whether tripping this row's writes/day cap\nauto-pauses the agent (its own kill switch). PUT-replace like the caps:\nomitted / ``false`` clears it. It is the one non-cap governance knob set here\nbecause it is a property of the budget, not a manual pause action."},"AgentBudgetUsage":{"properties":{"object":{"type":"string","title":"Object","default":"agent_budget_usage"},"writes_24h":{"type":"integer","title":"Writes 24H"},"requests_last_sec":{"type":"integer","title":"Requests Last Sec"}},"type":"object","required":["writes_24h","requests_last_sec"],"title":"AgentBudgetUsage","description":"Live per-agent counter usage, read from the limiter's Redis windows.\n\nBoth counts come from :func:`app.core.rate_limit.peek_window_count` (read-only\n— surfacing them never consumes budget), and both fail open to ``0`` when\nRedis is unset/unreachable."},"AgentKillSwitchResponse":{"properties":{"object":{"type":"string","title":"Object","default":"agent_kill_switch"},"agent_user_id":{"type":"string","format":"uuid","title":"Agent User Id"},"paused":{"type":"boolean","title":"Paused"},"paused_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Paused At"},"paused_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Paused Reason"}},"type":"object","required":["agent_user_id","paused"],"title":"AgentKillSwitchResponse","description":"Response for :pause / :resume (spec §4.7 kill switch).\n\nMirrors the reversible ``agent_budgets`` kill-switch columns so the caller\n(UI / MCP wrapper) can render the new paused state without a re-read."},"AgentPresence":{"properties":{"object":{"type":"string","title":"Object","default":"agent_presence"},"user_id":{"type":"string","title":"User Id"},"display_name":{"type":"string","title":"Display Name"}},"type":"object","required":["user_id","display_name"],"title":"AgentPresence","description":"One live-agent row, returned by GET /v1/orgs/{org_id}/presence/agents."},"AiRunUsageResponse":{"properties":{"object":{"type":"string","title":"Object","default":"ai_run_usage"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"card_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Card Id"},"column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"},"playbook_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Playbook Id"},"automation_run_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Automation Run Id"},"model":{"type":"string","title":"Model"},"step_count":{"type":"integer","title":"Step Count"},"tokens_in":{"type":"integer","title":"Tokens In"},"tokens_out":{"type":"integer","title":"Tokens Out"},"cost_estimate_usd":{"type":"string","title":"Cost Estimate Usd"},"status":{"type":"string","title":"Status"},"created_time":{"type":"string","format":"date-time","title":"Created Time"}},"type":"object","required":["id","org_id","board_id","card_id","column_id","playbook_id","automation_run_id","model","step_count","tokens_in","tokens_out","cost_estimate_usd","status","created_time"],"title":"AiRunUsageResponse","description":"One metered Autopilot run in the run-history feed (spec §23.6)."},"AlreadyMemberResponse":{"properties":{"object":{"type":"string","title":"Object","default":"invitation_noop"},"already_member":{"type":"boolean","title":"Already Member","default":true},"membership":{"$ref":"#/components/schemas/MembershipResponse"},"board_grant":{"anyOf":[{"$ref":"#/components/schemas/BoardGrantResponse"},{"type":"null"}]}},"type":"object","required":["membership"],"title":"AlreadyMemberResponse","description":"The §4.8 \"an invitation to an existing member is a no-op\" outcome.\n\nReturned (HTTP **200**, not 201) when the invited address already holds a\nlive membership in the workspace. No invitation row is written and no seat\nmoves — the spec calls this \"a no-op, not a duplicate row\", so it is neither\nan error nor a second invitation. The existing membership rides along so the\ncaller can render \"they're already in\" without a follow-up request."},"AppendBlocksRequest":{"properties":{"children":{"items":{"$ref":"#/components/schemas/BlockInput"},"type":"array","maxItems":100,"minItems":1,"title":"Children"},"after":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"After"}},"additionalProperties":false,"type":"object","required":["children"],"title":"AppendBlocksRequest","description":"`PATCH /v1/blocks/{parent_id}/children` body."},"AuthSessionResponse":{"properties":{"object":{"type":"string","title":"Object","default":"auth_session"},"user":{"$ref":"#/components/schemas/UserResponse"},"memberships":{"items":{"$ref":"#/components/schemas/MembershipResponse"},"type":"array","title":"Memberships"}},"type":"object","required":["user","memberships"],"title":"AuthSessionResponse","description":"Returned by login / verify-email: who you are + your orgs."},"AutomationResponse":{"properties":{"object":{"type":"string","title":"Object","default":"automation"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"name":{"type":"string","title":"Name"},"is_active":{"type":"boolean","title":"Is Active"},"trigger":{"additionalProperties":true,"type":"object","title":"Trigger"},"conditions":{"items":{},"type":"array","title":"Conditions"},"actions":{"items":{},"type":"array","title":"Actions"},"run_as_agent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Run As Agent Id"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"}},"type":"object","required":["id","org_id","board_id","name","is_active","trigger","conditions","actions","run_as_agent_id","created_time","last_edited_time"],"title":"AutomationResponse"},"AutomationRunResponse":{"properties":{"object":{"type":"string","title":"Object","default":"automation_run"},"id":{"type":"string","format":"uuid","title":"Id"},"automation_id":{"type":"string","format":"uuid","title":"Automation Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"card_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Card Id"},"source_sequence":{"type":"integer","title":"Source Sequence"},"status":{"type":"string","title":"Status"},"attempts":{"type":"integer","title":"Attempts"},"next_retry_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Retry At"},"chain_depth":{"type":"integer","title":"Chain Depth"},"action_results":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Action Results"},"created_time":{"type":"string","format":"date-time","title":"Created Time"}},"type":"object","required":["id","automation_id","org_id","card_id","source_sequence","status","attempts","next_retry_at","chain_depth","action_results","created_time"],"title":"AutomationRunResponse"},"AutopilotRollup":{"properties":{"up":{"type":"integer","title":"Up","default":0},"down":{"type":"integer","title":"Down","default":0},"ratio":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ratio"},"responses":{"type":"integer","title":"Responses","default":0}},"type":"object","title":"AutopilotRollup","description":"Thumbs up/down over the range. ``ratio`` is up / (up + down)."},"AutopilotRunProvenanceResponse":{"properties":{"object":{"type":"string","title":"Object","default":"autopilot_run_provenance"},"id":{"type":"string","format":"uuid","title":"Id"},"automation_run_id":{"type":"string","format":"uuid","title":"Automation Run Id"},"playbook_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Playbook Id"},"model":{"type":"string","title":"Model"},"status":{"type":"string","title":"Status"},"created_time":{"type":"string","format":"date-time","title":"Created Time"}},"type":"object","required":["id","automation_run_id","playbook_id","model","status","created_time"],"title":"AutopilotRunProvenanceResponse","description":"One Autopilot run that wrote content on a card (NOV-447, §25.9 / §23).\n\nThe card-reader's view of a run, served by\n``GET /v1/cards/{card_id}/autopilot-runs`` to anyone who can read the card.\nWhat it leaves out is the point: ``tokens_in`` / ``tokens_out`` /\n``cost_estimate_usd`` are on :class:`AiRunUsageResponse` and stay behind the\nadmin/owner gate on ``/v1/orgs/{org_id}/ai-run-usage``, because what a run\n*cost* is governance data and what a run *wrote* is card data. ``model`` is\nhere deliberately — \"written by claude-haiku on Tuesday\" is provenance the\nreader is entitled to, and it is not a spend figure.\n\n``automation_run_id`` is non-null by construction (the query filters it),\nbecause it is the whole point: it is what makes the §25.9\n``autopilot_rating`` joinable back against ``ai_run_usage``."},"BatchOp":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"op":{"type":"string","enum":["set","move","delete"],"title":"Op"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"properties":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Properties"},"assignees":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Assignees"},"due_date":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Due Date"},"labels":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Labels"},"remove_labels":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Remove Labels"},"is_archived":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Archived"},"column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"},"position":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Position"}},"additionalProperties":false,"type":"object","required":["id","op"],"title":"BatchOp"},"BatchOpResult":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"op":{"type":"string","enum":["set","move","delete"],"title":"Op"},"ok":{"type":"boolean","title":"Ok"},"card":{"anyOf":[{"$ref":"#/components/schemas/CardResponse"},{"type":"null"}]},"error":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Error"}},"additionalProperties":false,"type":"object","required":["id","op","ok"],"title":"BatchOpResult"},"BatchUpdateRequest":{"properties":{"ops":{"items":{"$ref":"#/components/schemas/BatchOp"},"type":"array","maxItems":200,"minItems":1,"title":"Ops"}},"additionalProperties":false,"type":"object","required":["ops"],"title":"BatchUpdateRequest"},"BatchUpdateResponse":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/BatchOpResult"},"type":"array","title":"Results"}},"additionalProperties":false,"type":"object","required":["results"],"title":"BatchUpdateResponse"},"BeaconConfigResponse":{"properties":{"object":{"type":"string","title":"Object","default":"beacon_config"},"enabled":{"type":"boolean","title":"Enabled"},"property_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Property Key"},"sample_rate":{"type":"number","title":"Sample Rate"},"replay_mode":{"type":"string","title":"Replay Mode"},"replay_sample_rate":{"type":"number","title":"Replay Sample Rate","default":0.0},"masking_level":{"type":"string","title":"Masking Level"},"route_exclusions":{"items":{"type":"string"},"type":"array","title":"Route Exclusions"},"flush_interval_ms":{"type":"integer","title":"Flush Interval Ms"},"max_batch":{"type":"integer","title":"Max Batch"},"config_poll_s":{"type":"integer","title":"Config Poll S"},"allow_webdriver":{"type":"boolean","title":"Allow Webdriver","default":false}},"type":"object","required":["enabled","sample_rate","replay_mode","masking_level","route_exclusions","flush_interval_ms","max_batch","config_poll_s"],"title":"BeaconConfigResponse","description":"``GET /v1/beacon/config?property=<key>`` — the tracker's kill switch (TRK-8).\n\nServed for any property, known or not. An unknown or disabled one gets a\nwell-formed ``enabled: false`` body rather than a 404, for two reasons: a\ntracker must be able to go inert on a config it can parse (a 404 would leave\nit retrying or, worse, falling back to defaults), and a differentiated\nresponse would turn this route into an oracle for which properties exist.\n\n``property_key`` is echoed back for the tracker to put in its envelopes, and\nis ``None`` for anything not actively enabled. The tracker treats an absent\nkey as disabled, so that nulling is a second, independent kill switch — and\nit keeps the anti-oracle property, because \"exists but disabled\" and \"never\nexisted\" produce byte-identical bodies."},"BillingResponse":{"properties":{"object":{"type":"string","title":"Object","default":"billing"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"plan":{"type":"string","title":"Plan"},"status":{"type":"string","title":"Status"},"billable_seats":{"type":"integer","title":"Billable Seats"},"seat_breakdown":{"$ref":"#/components/schemas/SeatBreakdown"},"free_caps":{"$ref":"#/components/schemas/FreeCaps"},"current_period_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Current Period End"},"cancel_at_period_end":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Cancel At Period End"},"has_payment_method":{"type":"boolean","title":"Has Payment Method","default":false},"governance_enabled":{"type":"boolean","title":"Governance Enabled","default":false}},"type":"object","required":["org_id","plan","status","billable_seats","seat_breakdown","free_caps"],"title":"BillingResponse","description":"Billing status object for ``GET /v1/orgs/{org_id}/billing`` (spec §18.9).\n\n``billable_seats`` is the **live** count (``count_billable_seats``), never\nthe denormalized ``billing_seats`` cache. ``seat_breakdown`` is the\nserver-computed humans/agents split (the first-agent-free math lives there,\nnot the client — NOV-189). On a free org ``current_period_end`` and\n``cancel_at_period_end`` are null and ``has_payment_method`` is false."},"BlockInput":{"properties":{"object":{"type":"string","title":"Object","default":"block"},"type":{"type":"string","title":"Type"}},"additionalProperties":true,"type":"object","required":["type"],"title":"BlockInput","description":"One node in a `PATCH /v1/blocks/{id}/children` request body.\n\nMatches Notion's append-children shape: ``{\"object\": \"block\", \"type\":\n\"paragraph\", \"paragraph\": {...}}``. ``object`` is optional — clients\nsometimes omit it."},"BlockParent":{"properties":{"type":{"type":"string","title":"Type"},"card_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Card Id"},"block_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Block Id"}},"additionalProperties":false,"type":"object","required":["type"],"title":"BlockParent","description":"Parent reference for a block — either the card or another block."},"BoardAccessListResponse":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"visibility":{"type":"string","title":"Visibility"},"workspace_access":{"type":"string","title":"Workspace Access","default":"edit"},"permissions":{"additionalProperties":{"type":"string"},"type":"object","title":"Permissions"},"your_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Your Role"},"can_manage":{"type":"boolean","title":"Can Manage","default":false},"results":{"items":{"$ref":"#/components/schemas/BoardGrantResponse"},"type":"array","title":"Results"}},"additionalProperties":false,"type":"object","required":["visibility","results"],"title":"BoardAccessListResponse","description":"List of grants plus the board's access posture (§4.6).\n\nIncludes the board's ``visibility`` (NOV-195) and the ``workspace_access`` /\n``permissions`` configuration (NOV-216) so a sharing client — REST or an MCP\nagent — has the full picture in one call.\n\nNOV-218: also carries the CALLER's effective board role (``your_role``) and a\nderived ``can_manage`` flag, so a client can gate manage affordances on the\nbackend's real board-role resolution instead of approximating from org role.\n``your_role`` is one of ``viewer``/``commenter``/``editor``/``manager`` (or\n``None`` when the caller has no access — in practice never ``None`` here,\nsince listing already requires viewer); ``can_manage`` is exactly\n``role_satisfies(your_role, \"manager\")``."},"BoardColumnResponse":{"properties":{"object":{"type":"string","title":"Object","default":"board_column"},"id":{"type":"string","format":"uuid","title":"Id"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"name":{"type":"string","title":"Name"},"color":{"type":"string","title":"Color"},"role":{"type":"string","title":"Role"},"position":{"type":"number","title":"Position"},"is_default":{"type":"boolean","title":"Is Default"},"status_group":{"type":"string","title":"Status Group"},"autopilot":{"type":"boolean","title":"Autopilot","default":false},"playbook_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Playbook Id"},"autopilot_label_gate":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Autopilot Label Gate"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"},"etag":{"type":"integer","title":"Etag"}},"additionalProperties":false,"type":"object","required":["id","board_id","name","color","role","position","is_default","status_group","created_time","last_edited_time","etag"],"title":"BoardColumnResponse"},"BoardGrantResponse":{"properties":{"object":{"type":"string","title":"Object","default":"board_grant"},"id":{"type":"string","format":"uuid","title":"Id"},"user":{"$ref":"#/components/schemas/AccessUser"},"role":{"type":"string","title":"Role"},"granted_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Granted By"},"granted_at":{"type":"string","format":"date-time","title":"Granted At"}},"additionalProperties":false,"type":"object","required":["id","user","role","granted_at"],"title":"BoardGrantResponse"},"BoardLabelResponse":{"properties":{"object":{"type":"string","title":"Object","default":"board_label"},"name":{"type":"string","title":"Name"},"color":{"type":"string","title":"Color"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["name","color","count"],"title":"BoardLabelResponse","description":"One entry in a board's label vocabulary (NOV-437 / §7).\n\nLabels are a card built-in, not a board schema property. Nothing stops\nsomeone creating a ``multi_select`` property named \"Labels\" — only ``Status``\nis reserved (``app/services/properties.py``) — but the built-in shadows it on\nthe card read, so a schema entry can never describe a card's actual labels.\nGET /v1/boards/{id}/labels aggregates the labels\nactually in use on the board's non-archived cards: one entry per distinct\nname (merged case-insensitively), carrying the name's most-used ``color``\nand the total ``count`` across every color it has been written with."},"BoardQueryRequest":{"properties":{"filter":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Filter"},"sorts":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Sorts"},"page_size":{"type":"integer","title":"Page Size","default":25},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},"additionalProperties":false,"type":"object","title":"BoardQueryRequest"},"BoardResponse":{"properties":{"object":{"type":"string","title":"Object","default":"board"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"description":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Description"},"icon":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Icon"},"cover":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Cover"},"default_view_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Default View Id"},"status_groups":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Status Groups"},"properties":{"additionalProperties":{"additionalProperties":true,"type":"object"},"type":"object","title":"Properties"},"visibility":{"type":"string","title":"Visibility","default":"org"},"workspace_access":{"type":"string","title":"Workspace Access","default":"edit"},"permissions":{"additionalProperties":{"type":"string"},"type":"object","title":"Permissions"},"property_validation":{"type":"string","title":"Property Validation","default":"permissive"},"has_active_share_links":{"type":"boolean","title":"Has Active Share Links","default":false},"is_archived":{"type":"boolean","title":"Is Archived"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"},"etag":{"type":"integer","title":"Etag"}},"additionalProperties":false,"type":"object","required":["id","org_id","name","slug","status_groups","is_archived","created_time","last_edited_time","etag"],"title":"BoardResponse"},"BoardTemplateColumnPreview":{"properties":{"name":{"type":"string","title":"Name"},"status_group":{"type":"string","title":"Status Group"},"color":{"type":"string","title":"Color"},"role":{"type":"string","title":"Role"}},"type":"object","required":["name","status_group","color","role"],"title":"BoardTemplateColumnPreview","description":"One column in a board-template discovery preview (NOV-350 / §7.8)."},"BoardTemplatePropertyPreview":{"properties":{"name":{"type":"string","title":"Name"},"type":{"type":"string","title":"Type"},"show_on_card":{"type":"boolean","title":"Show On Card","default":false},"options":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Options"}},"type":"object","required":["name","type"],"title":"BoardTemplatePropertyPreview","description":"One property in a board-template discovery preview (NOV-350 / §7.8)."},"BoardTemplateResponse":{"properties":{"object":{"type":"string","title":"Object","default":"board_template"},"key":{"type":"string","title":"Key"},"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"property_validation":{"type":"string","title":"Property Validation"},"columns":{"items":{"$ref":"#/components/schemas/BoardTemplateColumnPreview"},"type":"array","title":"Columns"},"properties":{"items":{"$ref":"#/components/schemas/BoardTemplatePropertyPreview"},"type":"array","title":"Properties"}},"type":"object","required":["key","name","description","property_validation"],"title":"BoardTemplateResponse","description":"A board-template preset as exposed by GET /v1/board-templates and the\n``list_board_templates`` MCP tool (NOV-350 / §7.8). A curated preview —\nthe actual instantiation happens via POST /v1/boards ``template``."},"BulkFileIssueRequest":{"properties":{"ids":{"items":{"type":"string","format":"uuid"},"type":"array","minItems":1,"title":"Ids"}},"additionalProperties":false,"type":"object","required":["ids"],"title":"BulkFileIssueRequest","description":"``POST /v1/admin/feedback:bulkFileIssue`` — re-drive ≤ 200 rows into Jira.\n\nThe spec delta this ticket carries (see the router docstring): §25.7 gives\nonly a per-row ``:file-issue``, and production accumulates ``skipped`` rows\nfor as long as Jira stays unconfigured. Recovering those one click at a\ntime is not a recovery path."},"BulkResult":{"properties":{"object":{"type":"string","title":"Object","default":"bulk_result"},"requested":{"type":"integer","title":"Requested","default":0},"updated":{"type":"integer","title":"Updated","default":0},"unchanged":{"type":"integer","title":"Unchanged","default":0},"not_found":{"type":"integer","title":"Not Found","default":0},"updated_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Updated Ids"},"not_found_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Not Found Ids"}},"type":"object","title":"BulkResult","description":"What a bulk call actually did — never just ``{\"ok\": true}``.\n\nThe three buckets are disjoint and together account for every submitted id,\nwhich is the property that makes the response auditable: ``updated`` rows\nchanged and wrote an audit row, ``unchanged`` rows matched but already held\nthe target state (idempotent no-ops, no audit row), and ``not_found`` ids\nmatched nothing at all. A caller that submitted 200 ids and reads back 40\nupdated needs to know which of the other two buckets the remaining 160 fell\ninto — \"40 updated\" alone cannot distinguish a no-op from a typo."},"BulkUpdateFeedbackRequest":{"properties":{"ids":{"items":{"type":"string","format":"uuid"},"type":"array","minItems":1,"title":"Ids"},"status":{"type":"string","title":"Status"}},"additionalProperties":false,"type":"object","required":["ids","status"],"title":"BulkUpdateFeedbackRequest","description":"``POST /v1/admin/feedback:bulkUpdate`` — one status across ≤ 200 ids."},"BxCaptureHealth":{"properties":{"object":{"type":"string","title":"Object","default":"bx_capture_health"},"days":{"items":{"$ref":"#/components/schemas/BxCaptureHealthDay"},"type":"array","title":"Days"},"sessions_started":{"type":"integer","title":"Sessions Started","default":0},"sessions_stored":{"type":"integer","title":"Sessions Stored","default":0},"events_stored":{"type":"integer","title":"Events Stored","default":0},"events_skipped":{"type":"integer","title":"Events Skipped","default":0},"events_excluded":{"type":"integer","title":"Events Excluded","default":0},"client_dropped":{"type":"integer","title":"Client Dropped","default":0},"session_capture_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Session Capture Rate"},"event_skip_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Event Skip Rate"},"skipped_by_reason":{"additionalProperties":{"type":"integer"},"type":"object","title":"Skipped By Reason"}},"type":"object","title":"BxCaptureHealth","description":"The capture-health tile (TRK-9): \"Smartlook silently dropped recordings;\nwe will not\" (§26.6).\n\n``events_excluded`` is reported beside the skip counters and never folded\ninto them. Skipped means malfunction; excluded means a route exclusion did\nexactly what it was configured to do, and a property that excludes one busy\nroute must not read as permanently unhealthy (§26.5)."},"BxCaptureHealthDay":{"properties":{"day":{"type":"string","format":"date","title":"Day"},"sessions_started":{"type":"integer","title":"Sessions Started","default":0},"sessions_stored":{"type":"integer","title":"Sessions Stored","default":0},"events_stored":{"type":"integer","title":"Events Stored","default":0},"events_skipped":{"type":"integer","title":"Events Skipped","default":0},"events_excluded":{"type":"integer","title":"Events Excluded","default":0},"client_dropped":{"type":"integer","title":"Client Dropped","default":0}},"type":"object","required":["day"],"title":"BxCaptureHealthDay","description":"One day of TRK-9's pipeline self-report.\n\n``day`` here is the **server** ``received_at`` day, not the client ``ts``\nday the other series use (§26.6). The two disagree at the edges on purpose:\nthis measures our pipeline and has to line up with server-side incident\ntimestamps."},"BxClickPoint":{"properties":{"x_bucket":{"type":"integer","title":"X Bucket"},"y_bucket":{"type":"integer","title":"Y Bucket"},"clicks":{"type":"integer","title":"Clicks"},"selector_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Selector Hash"}},"type":"object","required":["x_bucket","y_bucket","clicks"],"title":"BxClickPoint","description":"One coordinate cell of the density map (HEAT-1's coordinate family).\n\n``x_bucket`` is a percent-of-viewport-width column (0..99) and ``y_bucket``\nis an absolute-document-pixel band, because a click 3000 px down a long page\nis at a fixed place in the document and at no fixed percent of anything. The\nresponse echoes both scales so the renderer never hardcodes them.\n\n``selector_hash`` is carried because ``bx_clicks_daily`` already keys on the\nelement *and* the coordinate in the same row, and it is what makes VIEW-4's\nelement overlay possible at all: the viewer cannot measure the stored canvas\n(a sandboxed frame without ``allow-same-origin`` has no readable layout, and\nthe inert rebuild it comes from has no layout at all), so the only way to\nshow *where* an element was clicked is to carry the cells that belong to it.\nThe density view sums over this field; the overlay filters on it."},"BxDailyPoint":{"properties":{"day":{"type":"string","format":"date","title":"Day"},"pageviews":{"type":"integer","title":"Pageviews","default":0},"sessions":{"type":"integer","title":"Sessions","default":0},"visitors":{"type":"integer","title":"Visitors","default":0},"frustrated_sessions":{"type":"integer","title":"Frustrated Sessions","default":0},"avg_friction":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Friction"}},"type":"object","required":["day"],"title":"BxDailyPoint","description":"One day on the dashboard's time series, gap-filled including the zeros.\n\nVIEW-2 is explicit that a regression must be *visible*, which a point-in-time\nrollup cannot show. ``pageviews`` comes from ``bx_pageviews_daily`` and the\nrest from ``bx_sessions``; the two agree on the calendar because both key on\nthe client ``ts`` day (§26.6's \"two calendars\" rule — capture health is the\none that does not, and it has its own series)."},"BxDeletion":{"properties":{"object":{"type":"string","title":"Object","default":"bx_deletion"},"sessions":{"type":"integer","title":"Sessions","default":0},"chunks":{"type":"integer","title":"Chunks","default":0},"objects":{"type":"integer","title":"Objects","default":0},"events":{"type":"integer","title":"Events","default":0}},"type":"object","title":"BxDeletion","description":"What a privacy-ops delete destroyed (VIEW-7/PRIV-7).\n\nReal counts, not a plan: the same numbers go into the SEC-4 audit row, so\n\"erased 4 sessions\" has to mean four rows left the table."},"BxElementRow":{"properties":{"selector":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Selector"},"selector_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Selector Hash"},"tag":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tag"},"clicks":{"type":"integer","title":"Clicks"},"share":{"type":"number","title":"Share"},"ctr":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ctr"},"collection":{"type":"boolean","title":"Collection","default":false},"unidentified":{"type":"boolean","title":"Unidentified","default":false}},"type":"object","required":["clicks","share"],"title":"BxElementRow","description":"One row of the element overlay / list view (HEAT-1's per-element family).\n\n``ctr`` is clicks per pageview of this (route × bucket × window) — the badge\nVIEW-4 asks for, and computable exactly because both halves sum honestly.\nThere is deliberately no session count; see the module docstring."},"BxEmergencyStopRequest":{"properties":{"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason"}},"additionalProperties":false,"type":"object","title":"BxEmergencyStopRequest","description":"Why capture is being halted. Optional — an emergency is a bad time for a form."},"BxEmergencyStopState":{"properties":{"object":{"type":"string","title":"Object","default":"bx_emergency_stop"},"stopped":{"type":"boolean","title":"Stopped"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"engaged_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Engaged At"},"engaged_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Engaged By"}},"type":"object","required":["stopped"],"title":"BxEmergencyStopState","description":"TRK-8's global stop, as the viewer reads it.\n\n``stopped`` is false and every other field null when capture is running:\nthe row that carries the provenance is deleted on release, so there is no\n\"last stop\" to report and reporting one would describe a stop not in force.\n``audit_log`` is where the history lives."},"BxEpochOption":{"properties":{"epoch":{"type":"string","title":"Epoch"},"views":{"type":"integer","title":"Views"},"first_day":{"type":"string","format":"date","title":"First Day"},"last_day":{"type":"string","format":"date","title":"Last Day"}},"type":"object","required":["epoch","views","first_day","last_day"],"title":"BxEpochOption","description":"One deploy epoch present in this route×bucket's window (VIEW-4 compare).\n\nEpochs are commit SHAs, so they have no order of their own; ``last_day``\nsupplies one. ``views`` is here because an epoch with four pageviews is not\na comparison, it is a rounding error, and the picker should say so."},"BxHeatmap":{"properties":{"object":{"type":"string","title":"Object","default":"bx_heatmap"},"property":{"type":"string","title":"Property"},"route_id":{"type":"string","title":"Route Id"},"viewport_bucket":{"type":"string","title":"Viewport Bucket"},"range_start":{"type":"string","format":"date","title":"Range Start"},"range_end":{"type":"string","format":"date","title":"Range End"},"epoch":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Epoch"},"floor":{"$ref":"#/components/schemas/BxHeatmapFloor"},"pageviews":{"type":"integer","title":"Pageviews","default":0},"clicks":{"type":"integer","title":"Clicks","default":0},"unplaceable_clicks":{"type":"integer","title":"Unplaceable Clicks","default":0},"points":{"items":{"$ref":"#/components/schemas/BxClickPoint"},"type":"array","title":"Points"},"points_truncated":{"type":"boolean","title":"Points Truncated","default":false},"elements":{"items":{"$ref":"#/components/schemas/BxElementRow"},"type":"array","title":"Elements"},"elements_truncated":{"type":"boolean","title":"Elements Truncated","default":false},"scroll":{"$ref":"#/components/schemas/BxScrollMap"},"snapshot":{"anyOf":[{"$ref":"#/components/schemas/BxSnapshotRef"},{"type":"null"}]},"epochs":{"items":{"$ref":"#/components/schemas/BxEpochOption"},"type":"array","title":"Epochs"},"x_buckets":{"type":"integer","title":"X Buckets"},"y_bucket_px":{"type":"integer","title":"Y Bucket Px"},"depth_bucket_pct":{"type":"integer","title":"Depth Bucket Pct"}},"type":"object","required":["property","route_id","viewport_bucket","range_start","range_end","floor","scroll","x_buckets","y_bucket_px","depth_bucket_pct"],"title":"BxHeatmap","description":"``GET /v1/admin/bx/heatmap`` — one (route × bucket × window × epoch)."},"BxHeatmapFloor":{"properties":{"met":{"type":"boolean","title":"Met"},"pageviews":{"type":"integer","title":"Pageviews"},"required":{"type":"integer","title":"Required"}},"type":"object","required":["met","pageviews","required"],"title":"BxHeatmapFloor","description":"HEAT-5's statistical floor, as a fact the client cannot mis-read.\n\nUnder the floor the heatmap payload is **absent**, not merely flagged. A\nclient that received the points and was trusted to remember not to draw them\nis one refactor away from drawing them, and a confident-looking map over\nnine pageviews is the specific failure HEAT-5 exists to prevent."},"BxInsightCard":{"properties":{"object":{"type":"string","title":"Object","default":"bx_insight"},"id":{"type":"string","title":"Id"},"kind":{"type":"string","title":"Kind"},"route_id":{"type":"string","title":"Route Id"},"selector":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Selector"},"selector_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Selector Hash"},"tag":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tag"},"window_start":{"type":"string","format":"date","title":"Window Start"},"window_end":{"type":"string","format":"date","title":"Window End"},"affected_sessions":{"type":"integer","title":"Affected Sessions"},"route_sessions":{"type":"integer","title":"Route Sessions"},"affected_share":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Affected Share"},"occurrences":{"type":"integer","title":"Occurrences"},"severity":{"type":"integer","title":"Severity"},"score":{"type":"number","title":"Score"},"first_seen_at":{"type":"string","format":"date-time","title":"First Seen At"},"last_seen_at":{"type":"string","format":"date-time","title":"Last Seen At"},"sample_session_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sample Session Id"},"collection":{"type":"boolean","title":"Collection","default":false}},"type":"object","required":["id","kind","route_id","window_start","window_end","affected_sessions","route_sessions","occurrences","severity","score","first_seen_at","last_seen_at"],"title":"BxInsightCard","description":"One ranked stuck point, read straight off ``bx_insights``.\n\nThe floors (HEAT-5: ≥10 affected sessions AND ≥1% of the route's sessions)\nwere applied by the rollup worker's INSERT — a below-floor card does not\nexist as a row, so there is nothing to filter here and no second floor to\nkeep in sync.\n\n``collection`` is HEAT-6's honesty label: TRK-3 collapses repeated siblings\nto a literal ``:nth-of-type(n)`` marker so \"users click cards\" aggregates as\none row instead of one row per position. That marker is what makes the count\ntrue *and* what makes it not a pixel-accurate claim about any single card,\nso the viewer says so rather than pretending otherwise."},"BxKeyRotation":{"properties":{"object":{"type":"string","title":"Object","default":"bx_key_rotation"},"slug":{"type":"string","title":"Slug"},"previous_key":{"type":"string","title":"Previous Key"},"key":{"type":"string","title":"Key"}},"type":"object","required":["slug","previous_key","key"],"title":"BxKeyRotation","description":"The result of a rotation — both keys, because both are identifiers.\n\nThe old one is returned so the operator can grep for it in whatever page,\nsnippet or CDN config still carries it. Neither is a secret (WIRE-2), which\nis why this response can exist at all."},"BxOverview":{"properties":{"object":{"type":"string","title":"Object","default":"bx_overview"},"property":{"type":"string","title":"Property"},"range_start":{"type":"string","format":"date","title":"Range Start"},"range_end":{"type":"string","format":"date","title":"Range End"},"pageviews":{"type":"integer","title":"Pageviews","default":0},"sessions":{"type":"integer","title":"Sessions","default":0},"visitors":{"type":"integer","title":"Visitors","default":0},"frustrated_sessions":{"type":"integer","title":"Frustrated Sessions","default":0},"avg_friction":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Friction"},"signals":{"type":"integer","title":"Signals","default":0},"daily":{"items":{"$ref":"#/components/schemas/BxDailyPoint"},"type":"array","title":"Daily"},"top_routes":{"items":{"$ref":"#/components/schemas/BxRouteTraffic"},"type":"array","title":"Top Routes"},"routes_truncated":{"type":"boolean","title":"Routes Truncated","default":false},"top_signals":{"items":{"$ref":"#/components/schemas/BxSignalTotal"},"type":"array","title":"Top Signals"},"capture_health":{"$ref":"#/components/schemas/BxCaptureHealth"}},"type":"object","required":["property","range_start","range_end","capture_health"],"title":"BxOverview","description":"``GET /v1/admin/bx/overview`` — the dashboard in one read (VIEW-2)."},"BxPropertyConfig":{"properties":{"object":{"type":"string","title":"Object","default":"bx_property_config"},"slug":{"type":"string","title":"Slug"},"key":{"type":"string","title":"Key"},"name":{"type":"string","title":"Name"},"enabled":{"type":"boolean","title":"Enabled"},"origins":{"items":{"type":"string"},"type":"array","title":"Origins"},"masking_level":{"type":"string","title":"Masking Level"},"replay_mode":{"type":"string","title":"Replay Mode"},"sample_rate":{"type":"number","title":"Sample Rate"},"replay_sample_rate":{"type":"number","title":"Replay Sample Rate"},"route_exclusions":{"items":{"type":"string"},"type":"array","title":"Route Exclusions"},"allow_webdriver":{"type":"boolean","title":"Allow Webdriver"},"retention_overrides":{"additionalProperties":{"type":"integer"},"type":"object","title":"Retention Overrides"},"epoch_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Epoch Note"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"}},"type":"object","required":["slug","key","name","enabled","origins","masking_level","replay_mode","sample_rate","replay_sample_rate","route_exclusions","allow_webdriver","retention_overrides","last_edited_time"],"title":"BxPropertyConfig","description":"One property's full editable state (§26.7's `/v1/admin/bx/config`).\n\nCarries the operational fields `BxPropertyRow` deliberately omits — the\norigin allow-list that WIRE-2 makes the real security boundary, the\nretention overrides NOV-617's worker reads, the second sampling die REP-4\nrolls for recordings, and TRK-10's webdriver escape hatch.\n\n``slug`` and ``key`` are both here and only one of them can be written:\nsee the model's own docstring for why they are separate columns at all."},"BxPropertyConfigPatch":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Name"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"origins":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":50},{"type":"null"}],"title":"Origins"},"masking_level":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Masking Level"},"replay_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Replay Mode"},"sample_rate":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Sample Rate"},"replay_sample_rate":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Replay Sample Rate"},"route_exclusions":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":200},{"type":"null"}],"title":"Route Exclusions"},"allow_webdriver":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Allow Webdriver"},"retention_overrides":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Retention Overrides"},"epoch_note":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Epoch Note"}},"additionalProperties":false,"type":"object","title":"BxPropertyConfigPatch","description":"A partial edit. Absent field = leave alone; explicit null = clear it.\n\n``extra=\"forbid\"`` is doing real work: the fields this model does *not*\ndeclare are ``slug`` (immutable — it is the join key of every bx row) and\n``key`` (rotated through its own endpoint, never typed by hand). Ignoring\nthem silently would let an operator watch a request succeed and believe a\nproperty had been renamed.\n\nEvery field is optional and none has a default value, so\n``model_dump(exclude_unset=True)`` distinguishes \"not mentioned\" from\n\"set to null\" — which ``epoch_note`` needs, being the one nullable column\nan operator clears rather than sets."},"BxPropertyRow":{"properties":{"object":{"type":"string","title":"Object","default":"bx_property"},"slug":{"type":"string","title":"Slug"},"key":{"type":"string","title":"Key"},"name":{"type":"string","title":"Name"},"enabled":{"type":"boolean","title":"Enabled"},"masking_level":{"type":"string","title":"Masking Level"},"replay_mode":{"type":"string","title":"Replay Mode"},"sample_rate":{"type":"number","title":"Sample Rate"},"route_exclusions":{"items":{"type":"string"},"type":"array","title":"Route Exclusions"},"epoch_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Epoch Note"}},"type":"object","required":["slug","key","name","enabled","masking_level","replay_mode","sample_rate","route_exclusions"],"title":"BxPropertyRow","description":"One registered property (§26.3), for the viewer's property selector.\n\nThe registry is multi-property by design even though we register one today,\nso the viewer picks from this list rather than hardcoding ``novumos``.\nDeliberately omits the property *key*'s siblings that are operational rather\nthan descriptive (retention overrides, origin allow-list): this is a\nselector's payload, not a config editor's."},"BxReplayChunkRef":{"properties":{"object":{"type":"string","title":"Object","default":"bx_replay_chunk"},"seq":{"type":"integer","title":"Seq"},"bytes":{"type":"integer","title":"Bytes"},"event_count":{"type":"integer","title":"Event Count"},"mode":{"type":"string","title":"Mode"},"masking_level":{"type":"string","title":"Masking Level"},"route_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Route Id"},"stored_at":{"type":"string","format":"date-time","title":"Stored At"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"url_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Url Expires At"}},"type":"object","required":["seq","bytes","event_count","mode","masking_level","stored_at"],"title":"BxReplayChunkRef","description":"One stored chunk, with both ways of reading its bytes.\n\n``url`` is P3a's short-TTL presigned GET (SEC-3), forced to an attachment\ndisposition — the **download** path, for an operator who needs the raw\nevents out of the system. It is ``None`` when presigning failed, which\ndegrades one chunk to \"cannot download right now\" rather than 500-ing the\npage around it.\n\nThe player does not use it. A presigned S3 URL is cross-origin and reading\nit with ``fetch()`` needs bucket CORS that is not provisioned, so playback\ngoes through the same-origin proxy at\n``/sessions/{id}/replay/chunks/{seq}`` — the identical arrangement §26.7\nsettled for canvases, and one SEC-3 sanctions in the same sentence as the\npresign (\"short-TTL signed URLs **or** authenticated streaming proxy\")."},"BxReplayOpen":{"properties":{"object":{"type":"string","title":"Object","default":"bx_replay"},"property":{"type":"string","title":"Property"},"session_id":{"type":"string","title":"Session Id"},"chunk_count":{"type":"integer","title":"Chunk Count"},"max_seq":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Seq"},"bytes":{"type":"integer","title":"Bytes"},"event_count":{"type":"integer","title":"Event Count"},"modes":{"items":{"type":"string"},"type":"array","title":"Modes"},"masking_level":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Masking Level"},"complete":{"type":"boolean","title":"Complete","default":true},"manifest_stale":{"type":"boolean","title":"Manifest Stale","default":false},"chunks":{"items":{"$ref":"#/components/schemas/BxReplayChunkRef"},"type":"array","title":"Chunks"},"timeline":{"items":{"$ref":"#/components/schemas/BxTimelineMarker"},"type":"array","title":"Timeline"},"timeline_truncated":{"type":"boolean","title":"Timeline Truncated","default":false}},"type":"object","required":["property","session_id","chunk_count","bytes","event_count"],"title":"BxReplayOpen","description":"Everything one replay view needs, in one response — and one audit row.\n\nDeliberately a single endpoint rather than a chunk list plus a timeline\nfetch: SEC-4 requires an audit row per replay *view*, and \"a view\" is only a\nwell-defined thing if exactly one request means it. Two endpoints would\neither write two rows for one viewing or leave the operator to guess which\nof them counted.\n\nThe manifest fields are computed from the chunk rows that exist right now.\n``manifest_stale`` says whether the summary stored on the session row still\nagrees with them — it will not, for a session whose recording retention has\nalready reclaimed, and a viewer that silently trusted the stored copy would\nrender that as data loss rather than as expiry.\n\n``complete`` is false when the chunk indices have a hole in them: chunk 0\ncarries rrweb's full snapshot and every later chunk is a delta, so a gap is\na session the player can only partly show, and it says so."},"BxRouteCoverage":{"properties":{"route_id":{"type":"string","title":"Route Id"},"viewport_bucket":{"type":"string","title":"Viewport Bucket"},"views":{"type":"integer","title":"Views"},"floor_met":{"type":"boolean","title":"Floor Met"},"has_snapshot":{"type":"boolean","title":"Has Snapshot"},"snapshot_captured_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Snapshot Captured At"}},"type":"object","required":["route_id","viewport_bucket","views","floor_met","has_snapshot"],"title":"BxRouteCoverage","description":"One (route × bucket) the heatmap picker can offer, with its floor state.\n\nCarries ``floor_met`` and ``has_snapshot`` so the picker can be honest before\nthe operator clicks: a route with 12 pageviews and a route with no stored\ncanvas both open to something other than a heatmap, and finding that out\nafter the navigation is a worse experience than being told."},"BxRouteTraffic":{"properties":{"route_id":{"type":"string","title":"Route Id"},"views":{"type":"integer","title":"Views","default":0}},"type":"object","required":["route_id"],"title":"BxRouteTraffic","description":"Traffic for one route over the window — views only, and that is the point.\n\nSee the module docstring: a route's session count cannot be summed out of\n``bx_pageviews_daily`` without counting returning visitors twice, and\n``bx_sessions`` records only the entry and exit route, so it cannot supply\none either. ``views`` sums exactly, so ``views`` is what this reports."},"BxScrollBucket":{"properties":{"depth_bucket":{"type":"integer","title":"Depth Bucket"},"visits":{"type":"integer","title":"Visits"},"reach_pct":{"type":"number","title":"Reach Pct"}},"type":"object","required":["depth_bucket","visits","reach_pct"],"title":"BxScrollBucket","description":"One 5%-wide depth band of the scrollmap (HEAT-3).\n\n``reach_pct`` is the survival curve: the share of visits whose maximum depth\nreached **at least** this band. It is monotonically non-increasing by\nconstruction, which is what makes a fold line meaningful."},"BxScrollMap":{"properties":{"visits":{"type":"integer","title":"Visits","default":0},"buckets":{"items":{"$ref":"#/components/schemas/BxScrollBucket"},"type":"array","title":"Buckets"},"fold_pct":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Fold Pct"},"quartiles":{"additionalProperties":{"type":"number"},"type":"object","title":"Quartiles"},"avg_doc_height_px":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Avg Doc Height Px"}},"type":"object","title":"BxScrollMap","description":"HEAT-3 in full: the curve, the fold, and the quartile reach counts."},"BxSessionDetail":{"properties":{"object":{"type":"string","title":"Object","default":"bx_session"},"id":{"type":"string","title":"Id"},"property":{"type":"string","title":"Property"},"session_id":{"type":"string","title":"Session Id"},"visitor":{"type":"string","title":"Visitor"},"org_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Org Id"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"user_kind":{"type":"string","title":"User Kind"},"started_at":{"type":"string","format":"date-time","title":"Started At"},"ended_at":{"type":"string","format":"date-time","title":"Ended At"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms"},"active_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Active Ms"},"device_class":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Device Class"},"browser":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Browser"},"os":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Os"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"entry_route":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entry Route"},"exit_route":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exit Route"},"event_count":{"type":"integer","title":"Event Count","default":0},"click_count":{"type":"integer","title":"Click Count","default":0},"error_count":{"type":"integer","title":"Error Count","default":0},"rage_count":{"type":"integer","title":"Rage Count","default":0},"dead_count":{"type":"integer","title":"Dead Count","default":0},"quickback_count":{"type":"integer","title":"Quickback Count","default":0},"friction_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Friction Score"},"frustrated":{"type":"boolean","title":"Frustrated","default":false},"internal_traffic":{"type":"boolean","title":"Internal Traffic","default":false},"has_replay":{"type":"boolean","title":"Has Replay","default":false},"consent":{"additionalProperties":true,"type":"object","title":"Consent"},"replay_manifest":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Replay Manifest"}},"type":"object","required":["id","property","session_id","visitor","user_kind","started_at","ended_at"],"title":"BxSessionDetail","description":"One session plus the two blobs the list has no room for.\n\n``consent`` is the WIRE-4 assertion the visit was captured under — the\nproof-of-consent record, and the first thing anyone auditing a capture\ndecision asks for. ``replay_manifest`` is the summary P3a stores on the\nsession row; it is ``None`` for a session that was never recorded, and it\ncan outlive the chunks it describes — the player's own endpoint reports the\nlive count beside it."},"BxSessionRow":{"properties":{"object":{"type":"string","title":"Object","default":"bx_session"},"id":{"type":"string","title":"Id"},"property":{"type":"string","title":"Property"},"session_id":{"type":"string","title":"Session Id"},"visitor":{"type":"string","title":"Visitor"},"org_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Org Id"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"user_kind":{"type":"string","title":"User Kind"},"started_at":{"type":"string","format":"date-time","title":"Started At"},"ended_at":{"type":"string","format":"date-time","title":"Ended At"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms"},"active_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Active Ms"},"device_class":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Device Class"},"browser":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Browser"},"os":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Os"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"entry_route":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entry Route"},"exit_route":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exit Route"},"event_count":{"type":"integer","title":"Event Count","default":0},"click_count":{"type":"integer","title":"Click Count","default":0},"error_count":{"type":"integer","title":"Error Count","default":0},"rage_count":{"type":"integer","title":"Rage Count","default":0},"dead_count":{"type":"integer","title":"Dead Count","default":0},"quickback_count":{"type":"integer","title":"Quickback Count","default":0},"friction_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Friction Score"},"frustrated":{"type":"boolean","title":"Frustrated","default":false},"internal_traffic":{"type":"boolean","title":"Internal Traffic","default":false},"has_replay":{"type":"boolean","title":"Has Replay","default":false}},"type":"object","required":["id","property","session_id","visitor","user_kind","started_at","ended_at"],"title":"BxSessionRow","description":"One visit, straight off ``bx_sessions`` (STOR-2).\n\n``visitor`` is carried because it is the handle every follow-up query needs\n— \"the rest of this person's visits\", and the §25.10 erasure path — and it\nis not an identity: for an anonymous visitor it is an HMAC of a truncated IP\nunder a salt that is destroyed nightly (IDENT/§26.4), so it cannot be\nresolved back to anyone even by us, and it stops being a join key at\nmidnight."},"BxSignalTotal":{"properties":{"signal_type":{"type":"string","title":"Signal Type"},"signals":{"type":"integer","title":"Signals","default":0}},"type":"object","required":["signal_type"],"title":"BxSignalTotal","description":"One frustration signal type over the window (SIG-1..4 as named by the\nrollup: ``rage_click``, ``dead_click``, ``error_click``, ``quickback``)."},"BxSnapshotDownload":{"properties":{"object":{"type":"string","title":"Object","default":"bx_snapshot"},"id":{"type":"string","title":"Id"},"property":{"type":"string","title":"Property"},"route_id":{"type":"string","title":"Route Id"},"viewport_bucket":{"type":"string","title":"Viewport Bucket"},"epoch":{"type":"string","title":"Epoch"},"masking_level":{"type":"string","title":"Masking Level"},"captured_at":{"type":"string","format":"date-time","title":"Captured At"},"bytes":{"type":"integer","title":"Bytes"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"url_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Url Expires At"}},"type":"object","required":["id","property","route_id","viewport_bucket","epoch","masking_level","captured_at","bytes"],"title":"BxSnapshotDownload","description":"A canvas plus a short-lived presigned GET (§26.8, mirroring screenshots).\n\n``url`` is ``None`` when presigning failed — a storage outage degrades the\nviewer to \"this canvas exists but cannot be opened right now\", which is the\nhonest answer and a much better one than a 500 on an admin page."},"BxSnapshotRef":{"properties":{"id":{"type":"string","title":"Id"},"route_id":{"type":"string","title":"Route Id"},"viewport_bucket":{"type":"string","title":"Viewport Bucket"},"epoch":{"type":"string","title":"Epoch"},"masking_level":{"type":"string","title":"Masking Level"},"captured_at":{"type":"string","format":"date-time","title":"Captured At"},"bytes":{"type":"integer","title":"Bytes"},"epoch_matches":{"type":"boolean","title":"Epoch Matches","default":true}},"type":"object","required":["id","route_id","viewport_bucket","epoch","masking_level","captured_at","bytes"],"title":"BxSnapshotRef","description":"The canvas the density layer is drawn over (HEAT-2).\n\n``epoch`` is on the ref for a reason that matters at read time: the newest\ncanvas for a route×bucket may predate the epoch being viewed (§26.3 serves\nlatest-across-epochs, because the previous build's backdrop beats none), and\n``epoch_matches`` tells the viewer to say so instead of implying the drawing\nand the background describe the same build."},"BxSnapshotSummary":{"properties":{"object":{"type":"string","title":"Object","default":"bx_snapshot"},"id":{"type":"string","title":"Id"},"property":{"type":"string","title":"Property"},"route_id":{"type":"string","title":"Route Id"},"viewport_bucket":{"type":"string","title":"Viewport Bucket"},"epoch":{"type":"string","title":"Epoch"},"masking_level":{"type":"string","title":"Masking Level"},"captured_at":{"type":"string","format":"date-time","title":"Captured At"},"bytes":{"type":"integer","title":"Bytes"}},"type":"object","required":["id","property","route_id","viewport_bucket","epoch","masking_level","captured_at","bytes"],"title":"BxSnapshotSummary","description":"One stored canvas as the admin list reports it (§26.3, VIEW).\n\nDeliberately no presigned URL: the list is a browse surface and minting a\nlive S3 URL per row would hand out N credentials to answer \"what exists\".\nThe detail route issues exactly one, for the canvas actually being opened."},"BxTimelineMarker":{"properties":{"object":{"type":"string","title":"Object","default":"bx_timeline_marker"},"kind":{"type":"string","title":"Kind"},"label":{"type":"string","title":"Label"},"ts":{"type":"string","format":"date-time","title":"Ts"},"route_id":{"type":"string","title":"Route Id"},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail"}},"type":"object","required":["kind","label","ts","route_id"],"title":"BxTimelineMarker","description":"One annotated moment on the player's timeline (REP-7).\n\n``ts`` is the **client's** clock — the same clock that stamped the rrweb\nevents — and there is deliberately no playback offset on the wire. The\nserver never parses a replay chunk (REP-2 on the read path), so it does not\nknow what moment the recording starts at; the player does, from the\nreplayer's own metadata, and computing the offset where both numbers are\nknown is what makes the marker land on the frame it describes.\n\n``kind`` is one of ``signal`` (SIG-1..4), ``error``, ``product`` (EVT-1) or\n``pageview``. ``detail`` is the element a signal fired on or the message an\nerror carried — visitor-supplied text, bounded on the way out."},"CardAuditActor":{"properties":{"kind":{"type":"string","title":"Kind"},"id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Id"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"}},"additionalProperties":false,"type":"object","required":["kind"],"title":"CardAuditActor","description":"Actor inline-expansion for card audit entries."},"CardAuditEntry":{"properties":{"object":{"type":"string","title":"Object","default":"audit_entry"},"id":{"type":"string","format":"uuid","title":"Id"},"event_type":{"type":"string","title":"Event Type"},"actor":{"$ref":"#/components/schemas/CardAuditActor"},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata"},"created_time":{"type":"string","format":"date-time","title":"Created Time"}},"additionalProperties":false,"type":"object","required":["id","event_type","actor","metadata","created_time"],"title":"CardAuditEntry","description":"One row of card audit_log, with the actor joined into display form."},"CardResponse":{"properties":{"object":{"type":"string","title":"Object","default":"card"},"id":{"type":"string","format":"uuid","title":"Id"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"},"title":{"type":"string","title":"Title"},"properties":{"additionalProperties":true,"type":"object","title":"Properties"},"position":{"type":"number","title":"Position"},"assignees":{"items":{"type":"string"},"type":"array","title":"Assignees"},"due_date":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Due Date"},"labels":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Labels"},"is_archived":{"type":"boolean","title":"Is Archived"},"agent_activity":{"anyOf":[{"$ref":"#/components/schemas/AgentActivity"},{"type":"null"}]},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"},"body_last_edited_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Body Last Edited Time"},"etag":{"type":"integer","title":"Etag"},"attachment_count":{"type":"integer","title":"Attachment Count","default":0},"unique_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unique Id"}},"additionalProperties":false,"type":"object","required":["id","board_id","title","properties","position","assignees","labels","is_archived","created_time","last_edited_time","etag"],"title":"CardResponse"},"CardTemplateResponse":{"properties":{"object":{"type":"string","title":"Object","default":"card_template"},"id":{"type":"string","format":"uuid","title":"Id"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"},"title":{"type":"string","title":"Title"},"properties":{"additionalProperties":true,"type":"object","title":"Properties"},"body_blocks":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Body Blocks"},"assignees":{"items":{"type":"string"},"type":"array","title":"Assignees"},"labels":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Labels"},"recurrence_rule":{"additionalProperties":true,"type":"object","title":"Recurrence Rule"},"next_materialization_at":{"type":"string","format":"date-time","title":"Next Materialization At"},"until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Until"},"count_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Count Remaining"},"is_active":{"type":"boolean","title":"Is Active"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"},"etag":{"type":"integer","title":"Etag"}},"additionalProperties":false,"type":"object","required":["id","board_id","title","properties","body_blocks","assignees","labels","recurrence_rule","next_materialization_at","is_active","created_time","last_edited_time","etag"],"title":"CardTemplateResponse"},"ChangePasswordRequest":{"properties":{"current_password":{"type":"string","minLength":1,"title":"Current Password"},"new_password":{"type":"string","minLength":1,"title":"New Password"}},"type":"object","required":["current_password","new_password"],"title":"ChangePasswordRequest"},"CheckoutRequest":{"properties":{"interval":{"type":"string","enum":["annual","monthly"],"title":"Interval","default":"annual"}},"type":"object","title":"CheckoutRequest","description":"Body for ``POST /v1/orgs/{org_id}/billing:checkout`` (spec §18.6, NOV-242).\n\n``interval`` selects the billing cadence for the Pro subscription: ``\"annual\"``\n(the promoted default — §18.1) or ``\"monthly\"`` (the deliberate premium). It\ndetermines which of the four per-kind Stripe prices Checkout uses. The body is\noptional on the wire; an omitted body defaults to annual."},"CheckoutResponse":{"properties":{"checkout_url":{"type":"string","title":"Checkout Url"}},"type":"object","required":["checkout_url"],"title":"CheckoutResponse","description":"Response for ``POST /v1/orgs/{org_id}/billing:checkout`` (spec §18.6).\n\nThe owner is redirected to ``checkout_url`` (a Stripe-hosted Checkout\nSession) to enter payment details. The plan flip to Pro is **webhook-driven**\n(``checkout.session.completed`` → §18.7), never set by this endpoint — the\nredirect can be lost, the webhook can't."},"ClaimNextRequest":{"properties":{"lease_ttl_seconds":{"anyOf":[{"type":"integer","maximum":86400.0,"minimum":1.0},{"type":"null"}],"title":"Lease Ttl Seconds"}},"additionalProperties":false,"type":"object","title":"ClaimNextRequest","description":"Body for ``POST /v1/boards/{board_id}/columns/{column_id}:claim-next``.\n\n``lease_ttl_seconds`` overrides the column contract's TTL for this claim\n(and every subsequent renew defaults to it). Absent → the contract's\n``lease_ttl_seconds`` else the platform default."},"ClaimResponse":{"properties":{"object":{"type":"string","title":"Object","default":"lease"},"id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Id"},"card_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Card Id"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"},"lease_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lease Token"},"attempt":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Attempt"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"card":{"anyOf":[{"$ref":"#/components/schemas/CardResponse"},{"type":"null"}]}},"type":"object","title":"ClaimResponse","description":"``:claim-next`` result. All lease fields are Optional so the drained-queue\ncase serializes as ``{object:\"lease\", card:null}`` with a single\ncontent-type (spec §7.5.3)."},"CommentResponse":{"properties":{"object":{"type":"string","title":"Object","default":"comment"},"id":{"type":"string","format":"uuid","title":"Id"},"card_id":{"type":"string","format":"uuid","title":"Card Id"},"author_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Author User Id"},"body":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Body"},"is_archived":{"type":"boolean","title":"Is Archived"},"is_edited":{"type":"boolean","title":"Is Edited"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"},"etag":{"type":"integer","title":"Etag"},"parent":{"additionalProperties":true,"type":"object","title":"Parent","readOnly":true},"created_by":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Created By","readOnly":true},"rich_text":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Rich Text","readOnly":true}},"additionalProperties":false,"type":"object","required":["id","card_id","body","is_archived","is_edited","created_time","last_edited_time","etag","parent","created_by","rich_text"],"title":"CommentResponse"},"CompleteLeaseRequest":{"properties":{"lease_token":{"type":"string","maxLength":64,"minLength":1,"title":"Lease Token"},"output":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Output"},"properties":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Properties"}},"additionalProperties":false,"type":"object","required":["lease_token"],"title":"CompleteLeaseRequest","description":"Body for ``POST /v1/leases/{lease_id}:complete``.\n\n``output`` is stored on the lease row (validated against the column's\n``output_schema`` in P1). ``properties`` is an optional card field-patch\napplied through the normal card update path before the card is advanced to\n``on_success_column_id``."},"CompleteResponse":{"properties":{"object":{"type":"string","title":"Object","default":"lease"},"id":{"type":"string","format":"uuid","title":"Id"},"card_id":{"type":"string","format":"uuid","title":"Card Id"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"},"lease_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lease Token"},"attempt":{"type":"integer","title":"Attempt"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"claimed_at":{"type":"string","format":"date-time","title":"Claimed At"},"last_heartbeat_at":{"type":"string","format":"date-time","title":"Last Heartbeat At"},"released_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Released At"},"release_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Release Reason"},"not_before":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Not Before"},"deadlettered":{"type":"boolean","title":"Deadlettered","default":false},"card":{"$ref":"#/components/schemas/CardResponse"}},"type":"object","required":["id","card_id","board_id","attempt","expires_at","claimed_at","last_heartbeat_at","card"],"title":"CompleteResponse","description":"``:complete`` result — the released lease plus the updated card (which has\nbeen advanced to ``on_success_column_id`` when the contract sets one)."},"ConnectAgentRequest":{"properties":{"display_name":{"type":"string","maxLength":200,"minLength":1,"title":"Display Name"},"role":{"type":"string","title":"Role","default":"member"},"avatar_url":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Avatar Url"},"runtime_url":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Runtime Url"}},"type":"object","required":["display_name"],"title":"ConnectAgentRequest","description":"Body for POST /v1/orgs/{org_id}/agents."},"ConnectAgentResponse":{"properties":{"object":{"type":"string","title":"Object","default":"agent_connection"},"agent":{"$ref":"#/components/schemas/UserResponse"},"membership":{"$ref":"#/components/schemas/MembershipResponse"},"integration":{"$ref":"#/components/schemas/CreateIntegrationResponse"},"agent_dispatch_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Dispatch Secret"}},"type":"object","required":["agent","membership","integration"],"title":"ConnectAgentResponse","description":"Response on agent creation.\n\nThe integration's plaintext token is included exactly once — the admin\nmust capture it now or rotate. Subsequent reads only see ``last_4``."},"CreateAutomationRequest":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"trigger":{"additionalProperties":true,"type":"object","title":"Trigger"},"conditions":{"items":{},"type":"array","title":"Conditions"},"actions":{"items":{},"type":"array","title":"Actions"},"run_as_agent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Run As Agent Id"}},"type":"object","required":["name","trigger","actions"],"title":"CreateAutomationRequest","description":"Body for ``POST /v1/orgs/{org_id}/automations`` (§20.8)."},"CreateBoardInvitationRequest":{"properties":{"email":{"type":"string","title":"Email"},"board_role":{"type":"string","title":"Board Role","default":"commenter"}},"additionalProperties":false,"type":"object","required":["email"],"title":"CreateBoardInvitationRequest","description":"Invite an email address **straight to one board** (NOV-532, §4.6/§4.8).\n\nThe one-action form of \"share this board with my client's reviewer\": it\nmints a ``guest`` invitation that carries the board grant, so accepting\nlands the person on a workspace where that board is already there instead of\nthe empty workspace §4.4 describes.\n\nThere is no org-role field on purpose. A board invitation always mints a\n**guest** — the board-scoped, external-collaborator role. Someone who should\nsee the whole workspace is a member, and that is the workspace invite\n(``POST /v1/orgs/{id}/invitations``), a different decision made in a\ndifferent place by an admin.\n\n``board_role`` is one of ``viewer`` | ``commenter`` | ``editor``.\n``manager`` is not invitable — see\n:data:`app.models.invitation.VALID_INVITATION_BOARD_ROLES`."},"CreateBoardRequest":{"properties":{"name":{"type":"string","title":"Name"},"slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slug"},"description":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Description"},"icon":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Icon"},"cover":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Cover"},"status_groups":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Status Groups"},"properties":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Properties"},"create_default_columns":{"type":"boolean","title":"Create Default Columns","default":true},"visibility":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Visibility"},"workspace_access":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workspace Access"},"permissions":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Permissions"},"property_validation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Property Validation"},"template":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Template"},"unique_id_prefix":{"anyOf":[{"type":"string","maxLength":16},{"type":"null"}],"title":"Unique Id Prefix"}},"additionalProperties":false,"type":"object","required":["name"],"title":"CreateBoardRequest"},"CreateCardRequest":{"properties":{"board_id":{"type":"string","format":"uuid","title":"Board Id"},"column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"},"title":{"type":"string","title":"Title"},"properties":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Properties"},"position":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Position"},"assignees":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Assignees"},"due_date":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Due Date"},"labels":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Labels"}},"additionalProperties":false,"type":"object","required":["board_id","title"],"title":"CreateCardRequest","description":"POST /v1/cards body.\n\nNOV-104: no ``body_blocks`` field. Cards are created empty; clients\nfollow up with ``PATCH /v1/blocks/{card_id}/children`` to seed body\ncontent. The pre-NOV-104 one-shot seed field was always asymmetric\n(POST accepted it, PATCH rejected it) and the JSONB cache it wrote to\nis gone.\n\nNOV-379: ``column_id`` is optional — when omitted (or explicitly\n``null``) the card lands on the board's default column so it is visible\non the kanban board from birth. A column-less card only results from a\nboard that has no columns."},"CreateCardTemplateRequest":{"properties":{"board_id":{"type":"string","format":"uuid","title":"Board Id"},"column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"},"title":{"type":"string","title":"Title"},"properties":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Properties"},"body_blocks":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Body Blocks"},"assignees":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Assignees"},"labels":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Labels"},"recurrence_rule":{"additionalProperties":true,"type":"object","title":"Recurrence Rule"},"next_materialization_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Materialization At"},"until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Until"},"count":{"anyOf":[{"type":"integer","maximum":10000.0,"minimum":1.0},{"type":"null"}],"title":"Count"}},"additionalProperties":false,"type":"object","required":["board_id","title","recurrence_rule"],"title":"CreateCardTemplateRequest"},"CreateColumnRequest":{"properties":{"name":{"type":"string","title":"Name"},"status_group":{"type":"string","title":"Status Group"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"position":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Position"}},"additionalProperties":false,"type":"object","required":["name","status_group"],"title":"CreateColumnRequest"},"CreateCommentRequest":{"properties":{"card_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Card Id"},"body":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Body"}},"additionalProperties":true,"type":"object","title":"CreateCommentRequest","description":"Comment-create accepts both native and Notion-shape payloads (NOV-63 B5).\n\nNative:    ``{\"card_id\": \"<uuid>\", \"body\": [<block>, …]}``\nNotion:    ``{\"parent\": {\"page_id\": \"<uuid>\"}, \"rich_text\": [<rt>, …]}``\n\nThe Notion-shape input is normalized at validation time:\n\n- ``parent.page_id`` → ``card_id``\n- ``rich_text`` segments concatenated into a single paragraph block\n  ``[{\"object\": \"block\", \"type\": \"paragraph\", \"paragraph\":\n  {\"rich_text\": [...], \"color\": \"default\"}}]``.\n\nMixed payloads (both shapes present) prefer the native fields and log\nnothing — the API is permissive on extras because pydantic discards\nthem once normalization completes."},"CreateDomainClaimRequest":{"properties":{"domain":{"type":"string","title":"Domain"},"verification_type":{"type":"string","title":"Verification Type","default":"dns_txt"},"allowlist":{"items":{"type":"string"},"type":"array","title":"Allowlist","default":[]},"blocklist":{"items":{"type":"string"},"type":"array","title":"Blocklist","default":[]}},"type":"object","required":["domain"],"title":"CreateDomainClaimRequest"},"CreateFeedbackRequest":{"properties":{"type":{"type":"string","title":"Type"},"route":{"type":"string","title":"Route"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"},"expected":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expected"},"severity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"},"exit_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exit Reason"},"rating":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"card_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Card Id"},"automation_run_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Automation Run Id"},"context":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Context"},"screenshot_file_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Screenshot File Id"},"app_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"App Version"}},"additionalProperties":false,"type":"object","required":["type","route"],"title":"CreateFeedbackRequest","description":"Body for ``POST /v1/feedback`` (spec §25.4)."},"CreateFeedbackScreenshotUploadRequest":{"properties":{"filename":{"type":"string","maxLength":255,"minLength":1,"title":"Filename"},"content_type":{"type":"string","maxLength":255,"minLength":1,"title":"Content Type"},"size_bytes":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Size Bytes"}},"additionalProperties":false,"type":"object","required":["filename","content_type"],"title":"CreateFeedbackScreenshotUploadRequest","description":"Body for ``POST /v1/feedback/screenshots:presign`` (NOV-442, spec §25.4).\n\nDeliberately NOT a reuse of\n:class:`~app.schemas.file.CreateFileUploadRequest`: that one requires a\n``card_id``, and a feedback screenshot is card-less by construction. The\nthree fields it does share carry the same wire meaning and the same column\nwidths.\n\nNo ``org_id`` — the org comes from the principal (§25.10). ``content_type``\nis a plain ``str`` rather than a ``Literal`` of the four allowed types for\nthe same reason the submission body keeps its enums loose: the allow-list\nlives in :func:`app.services.files._validate_screenshot_content_type`, which\nanswers ``415 unsupported_content_type`` — the §13.1 shape for a rejected\nupload type — where a Literal would answer 422 and split the rule across two\nlayers. ``size_bytes`` is a hint: the real ceiling is the\n``content-length-range`` in the S3 policy, which the client cannot lie past."},"CreateFileUploadRequest":{"properties":{"card_id":{"type":"string","format":"uuid","title":"Card Id"},"filename":{"type":"string","maxLength":255,"minLength":1,"title":"Filename"},"content_type":{"type":"string","maxLength":255,"minLength":1,"title":"Content Type"},"size_bytes":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Size Bytes"}},"additionalProperties":false,"type":"object","required":["card_id","filename","content_type"],"title":"CreateFileUploadRequest","description":"`POST /v1/files:upload` body."},"CreateImportJobRequest":{"properties":{"source":{"type":"string","enum":["notion_api","export_file"],"title":"Source","default":"notion_api"},"token":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Token"},"file_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"File Id"},"database_id":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Database Id"},"column_property":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Column Property"},"whole_workspace":{"type":"boolean","title":"Whole Workspace","default":false}},"type":"object","title":"CreateImportJobRequest","description":"Body for ``POST /v1/orgs/{org_id}/import-jobs`` (spec §21.9 / §21.10).\n\nTwo transports, selected by ``source``:\n\n* **``notion_api``** (default) — ``token`` is the Notion internal-integration\n  token, a transient secret carried ONLY in this request body (never a query\n  string / URL, which proxies log; §21.10). ``file_id`` is forbidden. Two\n  sub-modes: single-DB (``database_id`` names the one database; optional\n  ``column_property`` names the Notion status/select property whose options\n  become columns — blank ⇒ auto-detect) or whole-workspace\n  (``whole_workspace: true`` — walk ALL shared databases → one board each with\n  best-effort inter-DB relation links; ``database_id`` omitted).\n* **``export_file``** (NOV-297) — import a previously-uploaded Notion export\n  ``.zip``. ``file_id`` names the committed File\n  (upload → commit → create; usable as soon as the file is ``status=available``;\n  see the ``:upload`` endpoint). ``token``/``database_id``/``whole_workspace``\n  are forbidden;\n  ``column_property`` is an optional per-database column-driver hint. Always a\n  whole-file import (one board per database in the export).\n\nThese seed the job's ``cursor_state`` exactly as the runner reads it."},"CreateImportUploadRequest":{"properties":{"filename":{"type":"string","maxLength":255,"minLength":1,"title":"Filename"},"content_type":{"type":"string","maxLength":255,"minLength":1,"title":"Content Type","default":"application/zip"},"size_bytes":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Size Bytes"}},"type":"object","required":["filename"],"title":"CreateImportUploadRequest","description":"Body for ``POST /v1/orgs/{org_id}/import-jobs:upload`` (NOV-297, §21.10).\n\nMints a presigned POST for the Notion export ``.zip``. ``content_type`` MUST\nbe ``application/zip`` (415 otherwise). ``size_bytes`` is an optional presign\nhint; the server caps via the configured upload limit regardless. The client\nPUTs the bytes, calls the existing ``POST /v1/files/{id}:commit``, then creates\nthe import job with ``source=export_file`` + this ``file_id`` (importable as\nsoon as the file is ``status=available``)."},"CreateIntegrationRequest":{"properties":{"name":{"type":"string","title":"Name"},"role":{"type":"string","enum":["member","admin"],"title":"Role","default":"member"}},"type":"object","required":["name"],"title":"CreateIntegrationRequest"},"CreateIntegrationResponse":{"properties":{"object":{"type":"string","title":"Object","default":"integration"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"name":{"type":"string","title":"Name"},"last_4":{"type":"string","title":"Last 4"},"role":{"type":"string","title":"Role","default":"member"},"owner_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner User Id"},"created_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created Time"},"token":{"type":"string","title":"Token"}},"type":"object","required":["id","org_id","name","last_4","token"],"title":"CreateIntegrationResponse","description":"Response on creation includes the plaintext token — shown once."},"CreateInvitationRequest":{"properties":{"email":{"type":"string","title":"Email"},"role":{"type":"string","title":"Role","default":"member"}},"additionalProperties":false,"type":"object","required":["email"],"title":"CreateInvitationRequest","description":"Invite an email address into a workspace (NOV-529, spec §4.8).\n\n``role`` is the org role the invitee will hold *once they accept* — the same\nladder as ``AddMemberRequest`` (``owner`` | ``admin`` | ``member`` |\n``guest``). Nothing here consumes a seat: seats move at accept."},"CreateOrganizationRequest":{"properties":{"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"primary_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Domain"}},"type":"object","required":["name","slug"],"title":"CreateOrganizationRequest"},"CreatePlaybookRequest":{"properties":{"name":{"type":"string","title":"Name"},"steps":{"items":{},"type":"array","title":"Steps"},"output_target":{"type":"string","title":"Output Target","default":"append_body"},"on_success_move_to":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"On Success Move To"}},"type":"object","required":["name","steps"],"title":"CreatePlaybookRequest","description":"Body for ``POST /v1/orgs/{org_id}/playbooks`` (§23.4)."},"CreateShareLinkRequest":{"properties":{"role":{"type":"string","title":"Role","default":"viewer"},"label":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Label"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"additionalProperties":false,"type":"object","title":"CreateShareLinkRequest","description":"Create a board share link (NOV-199, spec §4.6).\n\n``role`` is capped at ``commenter`` — a share link never edits structure or\nmanages access. ``label`` is an optional human tag for the management list\n(e.g. \"Client: Acme\"). ``expires_at`` is an optional hard expiry; omit for a\nlink that lives until revoked."},"CreateWebhookRequest":{"properties":{"url":{"type":"string","title":"Url"},"event_types":{"items":{"type":"string"},"type":"array","title":"Event Types"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"notion_handshake":{"type":"boolean","title":"Notion Handshake","default":false}},"type":"object","required":["url"],"title":"CreateWebhookRequest"},"CreateWebhookResponse":{"properties":{"object":{"type":"string","title":"Object","default":"webhook"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"url":{"type":"string","title":"Url"},"event_types":{"items":{"type":"string"},"type":"array","title":"Event Types"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"is_active":{"type":"boolean","title":"Is Active"},"secret_last4":{"type":"string","title":"Secret Last4"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"},"secret":{"type":"string","title":"Secret"}},"type":"object","required":["id","org_id","url","event_types","board_id","is_active","secret_last4","created_time","last_edited_time","secret"],"title":"CreateWebhookResponse","description":"Same as WebhookResponse plus the plaintext secret — returned ONCE."},"DailyAutopilotPoint":{"properties":{"date":{"type":"string","format":"date","title":"Date"},"up":{"type":"integer","title":"Up","default":0},"down":{"type":"integer","title":"Down","default":0},"average":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Average"}},"type":"object","required":["date"],"title":"DailyAutopilotPoint"},"DailyNpsPoint":{"properties":{"date":{"type":"string","format":"date","title":"Date"},"score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Score"},"responses":{"type":"integer","title":"Responses","default":0}},"type":"object","required":["date"],"title":"DailyNpsPoint"},"DailyVolumePoint":{"properties":{"date":{"type":"string","format":"date","title":"Date"},"count":{"type":"integer","title":"Count","default":0}},"type":"object","required":["date"],"title":"DailyVolumePoint"},"DataSourceColumn":{"properties":{"object":{"type":"string","title":"Object","default":"board_column"},"id":{"type":"string","format":"uuid","title":"Id"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"name":{"type":"string","title":"Name"},"color":{"type":"string","title":"Color"},"position":{"type":"number","title":"Position"},"is_default":{"type":"boolean","title":"Is Default"},"status_group":{"type":"string","title":"Status Group"},"role":{"type":"string","title":"Role"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"},"etag":{"type":"integer","title":"Etag"}},"additionalProperties":false,"type":"object","required":["id","board_id","name","color","position","is_default","status_group","role","created_time","last_edited_time","etag"],"title":"DataSourceColumn","description":"One column inside a data_source response. Mirrors BoardColumnResponse."},"DataSourceResponse":{"properties":{"object":{"type":"string","title":"Object","default":"data_source"},"id":{"type":"string","format":"uuid","title":"Id"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"columns":{"items":{"$ref":"#/components/schemas/DataSourceColumn"},"type":"array","title":"Columns"},"status_groups":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Status Groups"}},"additionalProperties":false,"type":"object","required":["id","board_id","columns","status_groups"],"title":"DataSourceResponse","description":"Notion's `data_source` concept = board.columns + status_groups bundled.\n\nFor v0 the data_source id equals the board id (we collapse Notion's\ndatabase/data_source split — see spec §5.4)."},"DatabaseResponse":{"properties":{"object":{"type":"string","title":"Object","default":"database"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"description":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Description"},"icon":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Icon"},"cover":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Cover"},"default_view_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Default View Id"},"status_groups":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Status Groups"},"properties":{"additionalProperties":{"additionalProperties":true,"type":"object"},"type":"object","title":"Properties"},"visibility":{"type":"string","title":"Visibility","default":"org"},"workspace_access":{"type":"string","title":"Workspace Access","default":"edit"},"permissions":{"additionalProperties":{"type":"string"},"type":"object","title":"Permissions"},"property_validation":{"type":"string","title":"Property Validation","default":"permissive"},"has_active_share_links":{"type":"boolean","title":"Has Active Share Links","default":false},"is_archived":{"type":"boolean","title":"Is Archived"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"},"etag":{"type":"integer","title":"Etag"}},"additionalProperties":false,"type":"object","required":["id","org_id","name","slug","status_groups","is_archived","created_time","last_edited_time","etag"],"title":"DatabaseResponse","description":"Notion-compat wrapper for a Board. Identical payload except `object`."},"DomainClaimResponse":{"properties":{"object":{"type":"string","title":"Object","default":"domain_claim"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"domain":{"type":"string","title":"Domain"},"verification_type":{"type":"string","title":"Verification Type"},"verification_code":{"type":"string","title":"Verification Code"},"verified":{"type":"boolean","title":"Verified","default":false},"verified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Verified At"},"allowlist":{"items":{"type":"string"},"type":"array","title":"Allowlist","default":[]},"blocklist":{"items":{"type":"string"},"type":"array","title":"Blocklist","default":[]},"verification_status":{"type":"string","enum":["pending","failed","verified"],"title":"Verification Status","default":"pending"},"last_checked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Checked At"},"expected_record":{"anyOf":[{"$ref":"#/components/schemas/ExpectedDnsRecord"},{"type":"null"}]}},"type":"object","required":["id","org_id","domain","verification_type","verification_code"],"title":"DomainClaimResponse"},"EmailOutboxRequeueResponse":{"properties":{"object":{"type":"string","title":"Object","default":"email_outbox"},"id":{"type":"string","format":"uuid","title":"Id"},"status":{"type":"string","title":"Status"},"attempts":{"type":"integer","title":"Attempts"},"next_attempt_at":{"type":"string","format":"date-time","title":"Next Attempt At"},"requeued":{"type":"boolean","title":"Requeued"}},"type":"object","required":["id","status","attempts","next_attempt_at","requeued"],"title":"EmailOutboxRequeueResponse","description":"Safe operator-facing state; recipient address and payload stay private."},"ExpectedDnsRecord":{"properties":{"object":{"type":"string","title":"Object","default":"expected_dns_record"},"type":{"type":"string","title":"Type","default":"TXT"},"host":{"type":"string","title":"Host"},"value":{"type":"string","title":"Value"}},"type":"object","required":["host","value"],"title":"ExpectedDnsRecord","description":"The exact DNS record an admin must publish to verify ownership (§4.3.1)."},"FailLeaseRequest":{"properties":{"lease_token":{"type":"string","maxLength":64,"minLength":1,"title":"Lease Token"},"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason"},"requeue":{"type":"boolean","title":"Requeue","default":true}},"additionalProperties":false,"type":"object","required":["lease_token"],"title":"FailLeaseRequest","description":"Body for ``POST /v1/leases/{lease_id}:fail``."},"FeedbackDailySeries":{"properties":{"volume":{"items":{"$ref":"#/components/schemas/DailyVolumePoint"},"type":"array","title":"Volume"},"nps":{"items":{"$ref":"#/components/schemas/DailyNpsPoint"},"type":"array","title":"Nps"},"autopilot_rating":{"items":{"$ref":"#/components/schemas/DailyAutopilotPoint"},"type":"array","title":"Autopilot Rating"}},"type":"object","title":"FeedbackDailySeries","description":"The three §25.7 daily series.\n\nThese are the point of the endpoint. A rollup tells you where you are; only\nthe series tells you that last Tuesday's deploy doubled the bug rate. Every\nday in the requested range is present, including the zero days — a gap-free\naxis is what makes a drop visible, and a client that has to synthesize the\nmissing days will get it wrong differently from the next client."},"FeedbackReplyRequest":{"properties":{"message":{"type":"string","maxLength":5000,"minLength":1,"title":"Message"}},"additionalProperties":false,"type":"object","required":["message"],"title":"FeedbackReplyRequest","description":"``POST /v1/admin/feedback/{id}:reply`` — email the submitter (§25.8)."},"FeedbackReplyResult":{"properties":{"object":{"type":"string","title":"Object","default":"feedback_reply"},"queued":{"type":"boolean","title":"Queued","default":true},"to_address":{"type":"string","title":"To Address"},"reference":{"type":"string","title":"Reference"}},"type":"object","required":["to_address","reference"],"title":"FeedbackReplyResult","description":"The reply was ENQUEUED, not sent — §22.7 is a durable outbox.\n\nNamed ``queued`` rather than ``sent`` on purpose: the SES call happens\nout-of-band in the outbox worker, and a response claiming \"sent\" would be a\nlie the operator could act on."},"FeedbackResponse":{"properties":{"object":{"type":"string","title":"Object","default":"feedback"},"id":{"type":"string","format":"uuid","title":"Id"},"reference":{"type":"string","title":"Reference"},"type":{"type":"string","title":"Type"},"status":{"type":"string","title":"Status"},"surface":{"type":"string","title":"Surface"},"severity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"},"exit_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exit Reason"},"rating":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"},"expected":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expected"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"card_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Card Id"},"automation_run_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Automation Run Id"},"route":{"type":"string","title":"Route"},"screenshot_file_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Screenshot File Id"},"app_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"App Version"},"api_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Version"},"created_time":{"type":"string","format":"date-time","title":"Created Time"}},"additionalProperties":false,"type":"object","required":["id","reference","type","status","surface","route","created_time"],"title":"FeedbackResponse","description":"One feedback row as its submitter sees it (spec §25.4).\n\n``org_id`` / ``user_id`` are omitted for the same reason\n:class:`~app.schemas.notification.NotificationResponse` omits them: the\ncaller IS the owner, so echoing the ownership keys back adds nothing and\nonly widens the surface.\n\n**No ``jira_*`` field at all.** Not the issue key or URL (§25.9: the user\nnever sees an internal issue id — it leaks backlog structure and means\nnothing to a customer), not ``jira_last_error`` (platform-admin-only,\n§25.10), and — since NOV-443 — not ``jira_status`` either. A submitter has\nno use for the state of an integration they cannot see, and every value it\ncan take is either uninformative or actively misleading: ``pending`` reads\nas \"not dealt with yet\", ``skipped`` and ``failed`` describe OUR\nmisconfiguration and OUR outage as though they were something that happened\nto the report. The report is durable in either case (§25.6), which is the\nonly fact the submitter needs, and the ``FB-`` reference is how they quote\nit. The ``jira_*`` columns belong to the §25.7 admin schemas.\n\n``context`` is likewise withheld — it is scrubbed diagnostic state for an\noperator, and re-serving it would hand a client back the server-side\nsnapshot (plan, seat counts) it never supplied."},"FreeCaps":{"properties":{"max_human_seats":{"type":"integer","title":"Max Human Seats"},"max_agents":{"type":"integer","title":"Max Agents"},"max_guests":{"type":"integer","title":"Max Guests","default":0}},"type":"object","required":["max_human_seats","max_agents"],"title":"FreeCaps","description":"Free-tier caps surfaced to the client (spec §18.1 / §18.9).\n\nMirrors ``settings.free_max_human_seats`` / ``free_max_agents`` so the\nfrontend can render the upgrade prompt without hardcoding 5/1. Boards are\nuncapped on Free (NOV-242), so there is no board field.\n\n``max_guests`` (NOV-533, §18.14) is the third Free cap — the one the\n``402 plan_limit_reached`` with ``limit.resource=\"guests\"`` actually cites.\nUnlike its two siblings it is **dynamic**: the guest allowance is\n``max(humans, max_human_seats)``, so a grandfathered Free org with 8 humans\ngets 8 guest slots, not 5. It equals ``seat_breakdown.guest_allowance`` by\nconstruction (both come from the one server-side formula). On **Pro** it is\nstill reported — guests there are counted, never capped — so read it as\n\"the free guest allowance\", which is what caps Free and what bills beyond\non Pro."},"GrantBoardAccessRequest":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"role":{"type":"string","title":"Role"}},"additionalProperties":false,"type":"object","required":["user_id","role"],"title":"GrantBoardAccessRequest"},"GrantOrgCompRequest":{"properties":{"grant_type":{"type":"string","title":"Grant Type"},"reason":{"type":"string","minLength":1,"title":"Reason"},"idempotency_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency Key"},"free_seat_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Free Seat Count"},"percent_off":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Percent Off"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata"}},"type":"object","required":["grant_type","reason"],"title":"GrantOrgCompRequest","description":"Create-grant request body (POST /v1/admin/orgs/{org_id}/grants).\n\n``grant_type`` selects the per-type spec; the optional fields are validated\nper type by the service (``app/services/grants.py``):\n\n* ``free_seats``        → ``free_seat_count`` required (>= 1).\n* ``percent_off_coupon``→ ``percent_off`` required (1..100).\n* ``comped_subscription``→ always 100% off (``percent_off`` forced to 100).\n* ``plan_override``     → no extra fields.\n\n``idempotency_key`` dedupes a retried create; the server generates a UUID\nwhen absent. ``reason`` is REQUIRED (every comp is auditable)."},"GrantPlatformAdminRequest":{"properties":{"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"role":{"type":"string","title":"Role","default":"support"}},"type":"object","title":"GrantPlatformAdminRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ImportJobResponse":{"properties":{"object":{"type":"string","title":"Object","default":"import_job"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"source":{"type":"string","title":"Source"},"status":{"type":"string","title":"Status"},"attempts":{"type":"integer","title":"Attempts"},"counts":{"additionalProperties":true,"type":"object","title":"Counts"},"error_log":{"items":{},"type":"array","title":"Error Log"},"last_page":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Page"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"}},"type":"object","required":["id","org_id","source","status","attempts","created_time","last_edited_time"],"title":"ImportJobResponse","description":"Status object for an ``ImportJob`` (spec §21.9). NEVER carries the token.\n\n``counts`` (boards/columns/cards/blocks) drives the wizard's progress bar and\n``error_log`` explains a ``partial``/``failed`` run; ``board_id`` is null until\nthe worker creates the board and is the deep-link target on success.\n``last_page`` is the runner's incremental resume marker\n(``cursor_state.last_page``) surfaced for the live \"importing card N…\" line —\nthe raw ``cursor_state`` (which also holds the target ``database_id`` /\n``column_property``) is intentionally NOT echoed; only this progress field is."},"IntegrationResponse":{"properties":{"object":{"type":"string","title":"Object","default":"integration"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"name":{"type":"string","title":"Name"},"last_4":{"type":"string","title":"Last 4"},"role":{"type":"string","title":"Role","default":"member"},"owner_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner User Id"},"created_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created Time"}},"type":"object","required":["id","org_id","name","last_4"],"title":"IntegrationResponse"},"InvitationPreviewResponse":{"properties":{"object":{"type":"string","title":"Object","default":"invitation_preview"},"invitation_id":{"type":"string","format":"uuid","title":"Invitation Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"org_name":{"type":"string","title":"Org Name"},"org_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Slug"},"email":{"type":"string","title":"Email"},"role":{"type":"string","title":"Role"},"board_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Board Name"},"board_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Board Role"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["invitation_id","org_id","org_name","email","role","expires_at"],"title":"InvitationPreviewResponse","description":"What an emailed invitation token resolves to, before anyone signs in.\n\nBacks ``GET /v1/public/invitations/{token}``, which the ``/invitations/\n{token}`` deep link in the invite email lands on: it tells the SPA which\nworkspace is asking, which address the invitation is addressed to (so the\nsign-in/sign-up form can be pre-filled and the \"wrong account\" case can be\nexplained), and the invitation id to accept by. It carries no member\ndirectory, no org id beyond what the invitee is being offered, and nothing\nabout whether the address already has an account — presenting a valid token\nmust not become an account-enumeration oracle."},"InvitationResponse":{"properties":{"object":{"type":"string","title":"Object","default":"invitation"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"org_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Name"},"email":{"type":"string","title":"Email"},"role":{"type":"string","title":"Role"},"status":{"type":"string","title":"Status"},"invited_by_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Invited By User Id"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"board_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Board Role"},"board_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Board Name"},"last4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last4"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"created_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created Time"},"accepted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Accepted At"},"declined_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Declined At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"}},"type":"object","required":["id","org_id","email","role","status"],"title":"InvitationResponse","description":"One invitation, as the inviting workspace and the invitee both see it.\n\nDeliberately **never** carries the token — the raw token exists exactly once,\nin the email that was sent, and is not recoverable afterwards (only its\nargon2id hash is stored). ``last4`` is included so an admin can correlate a\nrow with a support request without the token being disclosed, mirroring\n``ShareLinkResponse``.\n\n``org_name`` is populated on the invitee-facing reads\n(``GET /v1/me/invitations``, accept/decline) because an invitee looking at a\nworkspace they have not joined has no other way to learn what they are being\nasked to join; the admin-facing list leaves it ``None`` (the caller already\nknows which workspace they are in)."},"JobContractRequest":{"properties":{"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"lease_ttl_seconds":{"anyOf":[{"type":"integer","maximum":86400.0,"minimum":1.0},{"type":"null"}],"title":"Lease Ttl Seconds"},"max_attempts":{"anyOf":[{"type":"integer","maximum":100.0,"minimum":1.0},{"type":"null"}],"title":"Max Attempts"},"concurrency_limit":{"anyOf":[{"type":"integer","maximum":10000.0,"minimum":1.0},{"type":"null"}],"title":"Concurrency Limit"},"backoff_seconds":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Backoff Seconds"},"on_success_column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"On Success Column Id"},"on_failure_column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"On Failure Column Id"},"input_schema":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Input Schema"},"output_schema":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Output Schema"}},"additionalProperties":false,"type":"object","title":"JobContractRequest","description":"Body for ``PUT /v1/boards/{board_id}/columns/{column_id}/contract`` — the\ncolumn job-contract admin write (NOV-260, spec §7.5.3 admin / cards-as-jobs\n§4.6 / §9). Every field is Optional; the caller sends only the ones it wants\nto set.\n\n``extra=\"forbid\"`` rejects any unknown key: a typo'd contract field (say\n``lease_ttl`` instead of ``lease_ttl_seconds``) is a configuration error, not\na silently-ignored no-op, so it surfaces as a ``422`` at the write boundary\nrather than quietly failing to take effect.\n\nPUT is a full replace: the service stores exactly the fields the caller\nprovided (see :func:`app.services.leases.set_column_contract_for_principal`).\nType/range checks live here; the cross-field write validation (well-formed\nJSON Schemas, on-board column references) lives in the service."},"JobContractResponse":{"properties":{"object":{"type":"string","title":"Object","default":"job_contract"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"column_id":{"type":"string","format":"uuid","title":"Column Id"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"lease_ttl_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Lease Ttl Seconds"},"max_attempts":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Attempts"},"concurrency_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Concurrency Limit"},"backoff_seconds":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Backoff Seconds"},"on_success_column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"On Success Column Id"},"on_failure_column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"On Failure Column Id"},"input_schema":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Input Schema"},"output_schema":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Output Schema"}},"type":"object","required":["board_id","column_id"],"title":"JobContractResponse","description":"The column's stored job contract on the wire (NOV-260, spec §7.5.3 admin).\n\nReturned by ``GET``/``PUT /v1/boards/{board_id}/columns/{column_id}/contract``.\nEchoes the fields as STORED on ``board_columns.job_contract`` — a field the\ncolumn never set reads back as ``null`` (the read side, not this response,\napplies platform defaults). A column with no contract at all serializes as\nthe empty shape (every field ``null``), so a fresh column and a\nfully-cleared one look identical (both \"not a queue\")."},"LeaseResponse":{"properties":{"object":{"type":"string","title":"Object","default":"lease"},"id":{"type":"string","format":"uuid","title":"Id"},"card_id":{"type":"string","format":"uuid","title":"Card Id"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"},"lease_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lease Token"},"attempt":{"type":"integer","title":"Attempt"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"claimed_at":{"type":"string","format":"date-time","title":"Claimed At"},"last_heartbeat_at":{"type":"string","format":"date-time","title":"Last Heartbeat At"},"released_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Released At"},"release_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Release Reason"},"not_before":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Not Before"},"deadlettered":{"type":"boolean","title":"Deadlettered","default":false}},"type":"object","required":["id","card_id","board_id","attempt","expires_at","claimed_at","last_heartbeat_at"],"title":"LeaseResponse","description":"The lease row on the wire — no nested card. Returned by ``:renew`` and\n``:fail``."},"ListDatabasesRequest":{"properties":{"token":{"type":"string","minLength":1,"title":"Token"}},"type":"object","required":["token"],"title":"ListDatabasesRequest","description":"Body for ``POST /v1/orgs/{org_id}/import-jobs:databases`` (spec §21.9/§21.10).\n\nToken-in-body (not a GET query string) so the transient Notion secret is\nnever written to an access log. The token is used once to list the databases\nthe integration can see and is then dropped — never stored."},"ListDatabasesResponse":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/NotionDatabaseSummary"},"type":"array","title":"Results"}},"type":"object","required":["results"],"title":"ListDatabasesResponse","description":"Envelope for the transient-token database listing (spec §21.9)."},"LoginRequest":{"properties":{"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"LoginRequest"},"MakeRecurringRequest":{"properties":{"recurrence_rule":{"additionalProperties":true,"type":"object","title":"Recurrence Rule"},"until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Until"},"count":{"anyOf":[{"type":"integer","maximum":10000.0,"minimum":1.0},{"type":"null"}],"title":"Count"},"next_materialization_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Materialization At"}},"additionalProperties":false,"type":"object","required":["recurrence_rule"],"title":"MakeRecurringRequest","description":"POST /v1/cards/{id}:make-recurring body.\n\nTurns an existing card into a template anchored at the existing card's\ndue_date.start (or `next_materialization_at` if provided). The original\ncard is left in place — the template emits future occurrences."},"MembershipResponse":{"properties":{"object":{"type":"string","title":"Object","default":"membership"},"id":{"type":"string","format":"uuid","title":"Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"role":{"type":"string","title":"Role"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"kind":{"type":"string","title":"Kind","default":"human"},"joined_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Joined At"},"last_signed_in_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Signed In At"}},"type":"object","required":["id","user_id","org_id","role"],"title":"MembershipResponse"},"MoveBlockRequest":{"properties":{"parent":{"$ref":"#/components/schemas/BlockParent"},"after":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"After"}},"additionalProperties":false,"type":"object","required":["parent"],"title":"MoveBlockRequest","description":"`POST /v1/blocks/{block_id}:move` body (NOV-498).\n\nMirrors ``POST /v1/cards/{id}:move``. ``parent`` names the destination —\nanother block in the same card, or the card itself (top level). ``after``\nnames the sibling to land behind.\n\n**``after=None`` means FIRST**, which is the OPPOSITE of ``after`` on\n``AppendBlocksRequest`` above (there it means \"at the end\", Notion's\nbehaviour). The asymmetry is deliberate and documented in spec §8.2:\n\"make this the first child\" has no other spelling, while \"make it last\" is\njust ``after=<current last sibling>``."},"MoveCardRequest":{"properties":{"column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"},"position":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Position"}},"additionalProperties":false,"type":"object","title":"MoveCardRequest"},"NotificationPreferencesResponse":{"properties":{"email":{"additionalProperties":{"type":"boolean"},"propertyNames":{"enum":["card_assigned","automation_notify"]},"type":"object","title":"Email"}},"type":"object","title":"NotificationPreferencesResponse","description":"Effective per-channel notification preferences (NOV-364, spec §22.3).\n\n``email`` maps EVERY v1 notification type to its EFFECTIVE boolean with\ndefaults resolved server-side (absence of a stored override => the type's\ndefault, ON in v1) — so the settings UI never re-derives the default logic.\nIn-app is intentionally omitted: it has no opt-out in v1 (always on)."},"NotificationResponse":{"properties":{"object":{"type":"string","title":"Object","default":"notification"},"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"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"card_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Card Id"},"comment_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Comment Id"},"actor_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Actor Id"},"read_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Read At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"read":{"type":"boolean","title":"Read","readOnly":true}},"additionalProperties":false,"type":"object","required":["id","type","title","created_at","read"],"title":"NotificationResponse"},"NotionDatabaseSummary":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"}},"type":"object","required":["id","title"],"title":"NotionDatabaseSummary","description":"One pickable Notion database: its id + human title (spec §21.9)."},"NpsRollup":{"properties":{"score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Score"},"promoters":{"type":"integer","title":"Promoters","default":0},"passives":{"type":"integer","title":"Passives","default":0},"detractors":{"type":"integer","title":"Detractors","default":0},"responses":{"type":"integer","title":"Responses","default":0},"average_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Average Rating"}},"type":"object","title":"NpsRollup","description":"``(promoters − detractors) / total × 100`` over the range (§25.7).\n\nThe bands are the standard NPS definition — promoters 9-10, passives 7-8,\ndetractors 0-6 — and are deliberately NOT read from\n``feedback_nps_detractor_max``. That setting is the §25.8 *alerting*\nsensitivity; turning the pager down must not retroactively rewrite the\nscore. ``score`` is ``None`` with no responses, never ``0``: \"nobody\nanswered\" and \"answered, net zero\" are different facts."},"OrgGrantRow":{"properties":{"object":{"type":"string","title":"Object","default":"org_grant"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"grant_type":{"type":"string","title":"Grant Type"},"status":{"type":"string","title":"Status"},"reason":{"type":"string","title":"Reason"},"granted_by_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Granted By User Id"},"granted_at":{"type":"string","format":"date-time","title":"Granted At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"revoked_by_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Revoked By User Id"},"free_seat_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Free Seat Count"},"percent_off":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Percent Off"},"stripe_object_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Object Id"},"idempotency_key":{"type":"string","title":"Idempotency Key"},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata"}},"type":"object","required":["id","org_id","grant_type","status","reason","granted_at","idempotency_key"],"title":"OrgGrantRow","description":"One ``org_grants`` ledger row (response shape)."},"OrgVolumeRow":{"properties":{"org_id":{"type":"string","format":"uuid","title":"Org Id"},"org_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Name"},"org_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Slug"},"count":{"type":"integer","title":"Count","default":0}},"type":"object","required":["org_id"],"title":"OrgVolumeRow"},"OrganizationResponse":{"properties":{"object":{"type":"string","title":"Object","default":"organization"},"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"primary_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Domain"},"billing_plan":{"type":"string","title":"Billing Plan","default":"free"},"billing_status":{"type":"string","title":"Billing Status","default":"active"},"billing_seats":{"type":"integer","title":"Billing Seats","default":0},"default_board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Default Board Id"}},"type":"object","required":["id","name","slug"],"title":"OrganizationResponse"},"PageResponse":{"properties":{"object":{"type":"string","title":"Object","default":"page"},"id":{"type":"string","format":"uuid","title":"Id"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"},"title":{"type":"string","title":"Title"},"properties":{"additionalProperties":true,"type":"object","title":"Properties"},"position":{"type":"number","title":"Position"},"assignees":{"items":{"type":"string"},"type":"array","title":"Assignees"},"due_date":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Due Date"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"labels":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Labels"},"is_archived":{"type":"boolean","title":"Is Archived"},"agent_activity":{"anyOf":[{"$ref":"#/components/schemas/AgentActivity"},{"type":"null"}]},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"},"body_last_edited_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Body Last Edited Time"},"etag":{"type":"integer","title":"Etag"},"attachment_count":{"type":"integer","title":"Attachment Count","default":0},"unique_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unique Id"}},"additionalProperties":false,"type":"object","required":["id","board_id","title","properties","position","assignees","labels","is_archived","created_time","last_edited_time","etag"],"title":"PageResponse","description":"Notion-compat wrapper for a Card (NOV-32, NOV-78).\n\n``properties`` is a Notion-shape **superset**: the synthesized built-ins\n(``Name``, ``Status``, ``Assignees``, ``Due date``, ``Labels``) are built\nin the router via ``app.services.cards.build_notion_properties`` and\nmerged with user-set keys from ``card.properties`` JSONB (user wins on\ncollisions). The column lookup needs a DB session, so this can't be a\npure ``@computed_field`` on the schema — the router constructs the dict\nand passes it via ``model_copy``.\n\nNotion-trained clients read ``properties.Name.title[0].plain_text`` and\n``properties.Status.status.name``; the native top-level fields below\nstay populated for the existing Novum frontend (NOV-78 is read-side\nadditive, frontend migration is a separate ticket)."},"PaginatedResponse_AdminAuditRow_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/AdminAuditRow"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[AdminAuditRow]"},"PaginatedResponse_AdminBillingOrgRow_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/AdminBillingOrgRow"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[AdminBillingOrgRow]"},"PaginatedResponse_AdminFeedbackRow_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/AdminFeedbackRow"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[AdminFeedbackRow]"},"PaginatedResponse_AdminOrgRow_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/AdminOrgRow"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[AdminOrgRow]"},"PaginatedResponse_AdminUserRow_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/AdminUserRow"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[AdminUserRow]"},"PaginatedResponse_AgentPresence_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/AgentPresence"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[AgentPresence]"},"PaginatedResponse_AiRunUsageResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/AiRunUsageResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[AiRunUsageResponse]"},"PaginatedResponse_AutomationResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/AutomationResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[AutomationResponse]"},"PaginatedResponse_AutomationRunResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/AutomationRunResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[AutomationRunResponse]"},"PaginatedResponse_AutopilotRunProvenanceResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/AutopilotRunProvenanceResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[AutopilotRunProvenanceResponse]"},"PaginatedResponse_BoardColumnResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/BoardColumnResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[BoardColumnResponse]"},"PaginatedResponse_BoardLabelResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/BoardLabelResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[BoardLabelResponse]"},"PaginatedResponse_BoardResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/BoardResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[BoardResponse]"},"PaginatedResponse_BoardTemplateResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/BoardTemplateResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[BoardTemplateResponse]"},"PaginatedResponse_BxInsightCard_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/BxInsightCard"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[BxInsightCard]"},"PaginatedResponse_BxPropertyRow_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/BxPropertyRow"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[BxPropertyRow]"},"PaginatedResponse_BxRouteCoverage_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/BxRouteCoverage"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[BxRouteCoverage]"},"PaginatedResponse_BxSessionRow_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/BxSessionRow"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[BxSessionRow]"},"PaginatedResponse_BxSnapshotSummary_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/BxSnapshotSummary"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[BxSnapshotSummary]"},"PaginatedResponse_CardAuditEntry_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/CardAuditEntry"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[CardAuditEntry]"},"PaginatedResponse_CardResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/CardResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[CardResponse]"},"PaginatedResponse_CardTemplateResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/CardTemplateResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[CardTemplateResponse]"},"PaginatedResponse_DomainClaimResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/DomainClaimResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[DomainClaimResponse]"},"PaginatedResponse_ImportJobResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/ImportJobResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[ImportJobResponse]"},"PaginatedResponse_IntegrationResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/IntegrationResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[IntegrationResponse]"},"PaginatedResponse_InvitationResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/InvitationResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[InvitationResponse]"},"PaginatedResponse_LeaseResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/LeaseResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[LeaseResponse]"},"PaginatedResponse_MembershipResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/MembershipResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[MembershipResponse]"},"PaginatedResponse_OrgGrantRow_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/OrgGrantRow"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[OrgGrantRow]"},"PaginatedResponse_OrganizationResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/OrganizationResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[OrganizationResponse]"},"PaginatedResponse_PlatformAdminRow_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/PlatformAdminRow"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[PlatformAdminRow]"},"PaginatedResponse_PlaybookResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/PlaybookResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[PlaybookResponse]"},"PaginatedResponse_SearchHitResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/SearchHitResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[SearchHitResponse]"},"PaginatedResponse_ShareLinkResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/ShareLinkResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[ShareLinkResponse]"},"PaginatedResponse_UserSessionResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/UserSessionResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[UserSessionResponse]"},"PaginatedResponse_WebhookDeliveryResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/WebhookDeliveryResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[WebhookDeliveryResponse]"},"PaginatedResponse_WebhookResponse_":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"results":{"items":{"$ref":"#/components/schemas/WebhookResponse"},"type":"array","title":"Results"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"page_size":{"type":"integer","title":"Page Size","default":25}},"type":"object","required":["results"],"title":"PaginatedResponse[WebhookResponse]"},"PasswordPolicy":{"properties":{"object":{"type":"string","title":"Object","default":"password_policy"},"min_length":{"type":"integer","title":"Min Length"},"require_letter_and_digit":{"type":"boolean","title":"Require Letter And Digit"}},"type":"object","required":["min_length","require_letter_and_digit"],"title":"PasswordPolicy","description":"The rules ``app.services.auth.validate_password_strength`` enforces.\n\nBuilt by :func:`app.services.auth.password_policy` from the same settings\nthe validator reads, so the published policy cannot describe a rule the API\ndoes not actually apply."},"PasswordResetConfirmRequest":{"properties":{"token":{"type":"string","title":"Token"},"new_password":{"type":"string","minLength":1,"title":"New Password"}},"type":"object","required":["token","new_password"],"title":"PasswordResetConfirmRequest"},"PasswordResetRequest":{"properties":{"email":{"type":"string","title":"Email"}},"type":"object","required":["email"],"title":"PasswordResetRequest"},"PauseAgentRequest":{"properties":{"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","title":"PauseAgentRequest","description":"Body for POST /v1/orgs/{org_id}/agents/{agent_user_id}:pause.\n\n``reason`` is optional and free-form; the handler trims it to fit the\n``agent_budgets.paused_reason`` ``varchar(200)`` column. The body itself is\noptional (``:pause`` with no body is allowed)."},"PlatformAdminRow":{"properties":{"object":{"type":"string","title":"Object","default":"platform_admin"},"id":{"type":"string","format":"uuid","title":"Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"role":{"type":"string","title":"Role"},"granted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Granted At"},"granted_by_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Granted By User Id"}},"type":"object","required":["id","user_id","role"],"title":"PlatformAdminRow"},"PlaybookResponse":{"properties":{"object":{"type":"string","title":"Object","default":"playbook"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"name":{"type":"string","title":"Name"},"steps":{"items":{},"type":"array","title":"Steps"},"output_target":{"type":"string","title":"Output Target"},"on_success_move_to":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"On Success Move To"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"}},"type":"object","required":["id","org_id","name","steps","output_target","on_success_move_to","created_time","last_edited_time"],"title":"PlaybookResponse"},"PortalResponse":{"properties":{"portal_url":{"type":"string","title":"Portal Url"}},"type":"object","required":["portal_url"],"title":"PortalResponse","description":"Response for ``POST /v1/orgs/{org_id}/billing:portal`` (spec §18.6).\n\nThe owner is redirected to ``portal_url`` (a Stripe-hosted Customer Portal\nSession) to manage their subscription — cancel, update payment method, view\ninvoices. Requires an existing ``stripe_customer_id`` (``409 no_subscription``\nif absent)."},"PropertyItemResponse":{"properties":{"object":{"type":"string","title":"Object","default":"property_item"},"id":{"type":"string","title":"Id"},"type":{"type":"string","title":"Type"},"value":{"title":"Value"}},"additionalProperties":false,"type":"object","required":["id","type","value"],"title":"PropertyItemResponse","description":"Single property fetched from a card. Notion's `property_item` shape."},"PublicAgentActivityResponse":{"properties":{"object":{"type":"string","title":"Object","default":"public_agent_activity"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"summary":{"$ref":"#/components/schemas/AgentActivitySummary"},"entries":{"items":{"$ref":"#/components/schemas/AgentActivityEntry"},"type":"array","title":"Entries"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["board_id","summary","entries"],"title":"PublicAgentActivityResponse","description":"Client-shareable, read-only agent-activity report for ONE board (NOV-271).\n\nServed on the seatless share-link surface (``/v1/public/...``) so an agency\ncan show a CLIENT holding a viewer/commenter share-link token exactly what\nagents did on THEIR board — the \"run bots on a client board and show that\nclient what happened\" trust flow (spec §4.6 + §4.7). Deliberately narrower\nthan :class:`AgentActivityResponse`: it is ALWAYS board-scoped (``board_id``\nis the bound board, never org-wide) and all-agents (no ``agent_user_id`` — a\nclient sees the board's activity, not internal per-agent breakdowns). No\n``page_size`` — the feed is still cursor-paginated via ``next_cursor``."},"PublicCommentRequest":{"properties":{"body":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Body"}},"additionalProperties":false,"type":"object","required":["body"],"title":"PublicCommentRequest","description":"A comment posted through a ``commenter`` share link (NOV-199).\n\nOnly the block ``body`` is accepted — the card is fixed by the path and the\nauthor is the (anonymous) link, never a User. Emptiness is enforced in the\ncomment service."},"PublicConfigResponse":{"properties":{"object":{"type":"string","title":"Object","default":"public_config"},"password":{"$ref":"#/components/schemas/PasswordPolicy"}},"type":"object","required":["password"],"title":"PublicConfigResponse","description":"Envelope for the unauthenticated runtime-config read."},"PurgeBoardPropertyRequest":{"properties":{"name":{"type":"string","title":"Name"}},"additionalProperties":false,"type":"object","required":["name"],"title":"PurgeBoardPropertyRequest","description":"NOV-459 (§7.1): body of ``POST /v1/boards/{id}/properties:purge``.\n\nNames the ARCHIVED property definition to delete permanently. Separate from\nthe properties patch on purpose: ``{\"properties\": {\"ID\": null}}`` archives a\n``unique_id`` property (recoverable — re-adding the name reinstates the same\ndefinition, id, prefix and counter), and this is the one action that is not."},"RenewLeaseRequest":{"properties":{"lease_token":{"type":"string","maxLength":64,"minLength":1,"title":"Lease Token"},"lease_ttl_seconds":{"anyOf":[{"type":"integer","maximum":86400.0,"minimum":1.0},{"type":"null"}],"title":"Lease Ttl Seconds"}},"additionalProperties":false,"type":"object","required":["lease_token"],"title":"RenewLeaseRequest","description":"Body for ``POST /v1/leases/{lease_id}:renew`` (and ``:heartbeat``)."},"ResendVerificationRequest":{"properties":{"email":{"type":"string","title":"Email"}},"type":"object","required":["email"],"title":"ResendVerificationRequest"},"RevokeAgentTokensResponse":{"properties":{"object":{"type":"string","title":"Object","default":"agent_tokens_revoked"},"agent_user_id":{"type":"string","format":"uuid","title":"Agent User Id"},"revoked_count":{"type":"integer","title":"Revoked Count"}},"type":"object","required":["agent_user_id","revoked_count"],"title":"RevokeAgentTokensResponse","description":"Response for the hard, irreversible :revoke_tokens variant (spec §4.7).\n\n``revoked_count`` is how many ``Integration`` credentials were deleted for\nthe agent in this org. Unlike :pause, this sets no paused/disabled state — it\nonly wipes credentials."},"RevokeOrgGrantRequest":{"properties":{"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","title":"RevokeOrgGrantRequest","description":"Optional revoke body — an operator note recorded in the audit trail."},"RouteVolumeRow":{"properties":{"route":{"type":"string","title":"Route"},"count":{"type":"integer","title":"Count","default":0}},"type":"object","required":["route"],"title":"RouteVolumeRow"},"SearchHitResponse":{"properties":{"object":{"type":"string","title":"Object","default":"card"},"id":{"type":"string","format":"uuid","title":"Id"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"column_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Column Id"},"title":{"type":"string","title":"Title"},"properties":{"additionalProperties":true,"type":"object","title":"Properties"},"position":{"type":"number","title":"Position"},"assignees":{"items":{"type":"string"},"type":"array","title":"Assignees"},"due_date":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Due Date"},"labels":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Labels"},"is_archived":{"type":"boolean","title":"Is Archived"},"agent_activity":{"anyOf":[{"$ref":"#/components/schemas/AgentActivity"},{"type":"null"}]},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"},"body_last_edited_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Body Last Edited Time"},"etag":{"type":"integer","title":"Etag"},"attachment_count":{"type":"integer","title":"Attachment Count","default":0},"unique_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unique Id"},"_search":{"$ref":"#/components/schemas/SearchResultMeta"}},"additionalProperties":false,"type":"object","required":["id","board_id","title","properties","position","assignees","labels","is_archived","created_time","last_edited_time","etag","_search"],"title":"SearchHitResponse","description":"One ``POST /v1/search`` result: a card PLUS its relevance block (NOV-457).\n\nA search hit is a full ``CardResponse`` with one extra key — it is NOT a\ndifferent object, so this subclasses the card rather than wrapping it. That\nkeeps the wire flat (the hit *is* the card) and means every card field stays\nin one place; adding a field to ``CardResponse`` reaches search for free.\n\n``_search`` cannot be a Python field name — pydantic reserves the leading\nunderscore for private attributes — so the field is ``search`` carrying an\nexplicit alias. FastAPI serializes response models with ``by_alias=True``,\nand ``populate_by_name`` (inherited from ``APIModel``) lets the service's\nhand-built ``{\"_search\": ...}`` dict validate, so the key crosses the wire\nunchanged in both directions."},"SearchRequest":{"properties":{"query":{"type":"string","maxLength":500,"minLength":1,"title":"Query"},"types":{"anyOf":[{"items":{"type":"string","const":"card"},"type":"array"},{"type":"null"}],"title":"Types"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"page_size":{"anyOf":[{"type":"integer","maximum":100.0,"minimum":1.0},{"type":"null"}],"title":"Page Size"},"start_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Cursor"}},"additionalProperties":false,"type":"object","required":["query"],"title":"SearchRequest"},"SearchResultMeta":{"properties":{"score":{"type":"number","title":"Score"},"highlight":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Highlight"}},"additionalProperties":false,"type":"object","required":["score"],"title":"SearchResultMeta","description":"Search-only envelope attached to result objects under the `_search` key.\n\nKept off the canonical Card shape so result consumers can ignore it and\ncallers wanting ranking signal can pluck it out."},"SeatBreakdown":{"properties":{"humans":{"type":"integer","title":"Humans"},"agents":{"type":"integer","title":"Agents"},"billable_agents":{"type":"integer","title":"Billable Agents"},"guests":{"type":"integer","title":"Guests","default":0},"guest_allowance":{"type":"integer","title":"Guest Allowance","default":0},"free_guests":{"type":"integer","title":"Free Guests","default":0},"billable_guests":{"type":"integer","title":"Billable Guests","default":0}},"type":"object","required":["humans","agents","billable_agents"],"title":"SeatBreakdown","description":"Seat split for the People/Billing UI (spec §18.9 / §18.14, NOV-189/NOV-533).\n\nComputed server-side from ``billing.seat_breakdown`` so neither the \"first\nagent free\" rule (§18.1) nor the guest allowance (§18.14) is ever re-derived\nin the frontend — clients render this split **verbatim**.\n\n* ``humans`` — non-guest human members. Guests are NOT included (§18.14).\n* ``guests`` — ``role='guest'`` members (§4.4 external collaborators).\n* ``guest_allowance`` = ``max(humans, free_caps.max_human_seats)`` — the\n  free guest room, which floats with paid headcount.\n* ``free_guests`` / ``billable_guests`` — guests inside / beyond it. A\n  billable guest bills on the **human** seat price; there is no guest SKU.\n* ``billable_agents`` = ``max(0, agents - 1)``.\n\nThe top-level ``billable_seats`` on :class:`BillingResponse` equals\n``humans + billable_guests + billable_agents``. §18.14: \"the org sees the\ncount before it is charged for it\" — this is that surface."},"SetAgentActivityRequest":{"properties":{"agent_name":{"type":"string","maxLength":200,"minLength":1,"title":"Agent Name"},"doing":{"type":"string","maxLength":500,"minLength":1,"title":"Doing"}},"additionalProperties":false,"type":"object","required":["agent_name","doing"],"title":"SetAgentActivityRequest","description":"Body for POST /v1/cards/{card_id}/agent_activity."},"SetPasswordRequest":{"properties":{"new_password":{"type":"string","minLength":1,"title":"New Password"}},"type":"object","required":["new_password"],"title":"SetPasswordRequest","description":"OAuth-only users who want to enable email/password login alongside\ntheir existing identity provider. Idempotent — once they have a password,\nthey must use ``POST /v1/me/password`` to change it."},"ShareLinkCreatedResponse":{"properties":{"object":{"type":"string","title":"Object","default":"board_share_link"},"id":{"type":"string","format":"uuid","title":"Id"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"role":{"type":"string","title":"Role"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label"},"token_last4":{"type":"string","title":"Token Last4"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"},"token":{"type":"string","title":"Token"},"url":{"type":"string","title":"Url"}},"additionalProperties":false,"type":"object","required":["id","board_id","role","token_last4","created_time","last_edited_time","token","url"],"title":"ShareLinkCreatedResponse","description":"Returned ONCE from create — carries the raw token + ready-to-share URL.\n\n``token`` and ``url`` are present only on this create response and are never\nretrievable again (only the argon2id hash is stored)."},"ShareLinkResponse":{"properties":{"object":{"type":"string","title":"Object","default":"board_share_link"},"id":{"type":"string","format":"uuid","title":"Id"},"board_id":{"type":"string","format":"uuid","title":"Board Id"},"role":{"type":"string","title":"Role"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label"},"token_last4":{"type":"string","title":"Token Last4"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"}},"additionalProperties":false,"type":"object","required":["id","board_id","role","token_last4","created_time","last_edited_time"],"title":"ShareLinkResponse","description":"A share link's management-side view — never includes the raw token.\n\n``token_last4`` identifies the link in the UI; the full token is shown only\nonce, in :class:`ShareLinkCreatedResponse`."},"SignupRequest":{"properties":{"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["email","password"],"title":"SignupRequest"},"SurveyDecisionResponse":{"properties":{"eligible":{"type":"boolean","title":"Eligible"},"last_shown_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Shown At"}},"additionalProperties":false,"type":"object","required":["eligible"],"title":"SurveyDecisionResponse","description":"Whether one triggered survey may be shown right now (NOV-447, §25.9).\n\n``last_shown_at`` rides along with the verdict so the client can show a\n\"you last answered this in May\" affordance, and so a frontend test can\nassert the cap round-tripped, without a second read of the preferences blob."},"SurveyEligibilityResponse":{"properties":{"object":{"type":"string","title":"Object","default":"survey_eligibility"},"nps":{"$ref":"#/components/schemas/SurveyDecisionResponse"},"upgrade_exit":{"$ref":"#/components/schemas/SurveyDecisionResponse"}},"additionalProperties":false,"type":"object","required":["nps","upgrade_exit"],"title":"SurveyEligibilityResponse","description":"``GET /v1/me/surveys`` (NOV-447, spec §25.9).\n\nThe server owns the whole NPS decision because the client cannot make it:\nthe SPA is never told how old its workspace is and there is no cards-moved\ncounter on the wire. Deliberately a *capability* answer (\"may this survey be\nshown\") rather than a data dump — the org's age and this person's move count\nare inputs to a product decision, not fields a browser needs.\n\n``upgrade_exit.eligible`` covers only the 30-day cap. The other half of that\ntrigger — abandoning the in-app upgrade flow after >= 5 s — is a behaviour\nonly the client can observe, so the client ANDs its own observation with\nthis flag."},"SurveyPreferencesResponse":{"properties":{"nps_last_shown_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Nps Last Shown At"},"upgrade_exit_last_shown_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Upgrade Exit Last Shown At"}},"type":"object","title":"SurveyPreferencesResponse","description":"When each triggered survey was last SHOWN to this user (NOV-447, §25.9).\n\nShown, not submitted: the 90-day / 30-day caps in §25.9 are caps on\ninterrupting someone, so a survey they dismissed without answering has to\ncount exactly as much as one they filled in. Absent (``None``) means \"never\nshown\" and the survey is eligible as far as the cap is concerned — the\nother NPS conditions (org age, cards moved) are evaluated by\n``GET /v1/me/surveys``, not here."},"UnreadCountResponse":{"properties":{"object":{"type":"string","title":"Object","default":"notification_count"},"unread":{"type":"integer","title":"Unread"}},"type":"object","required":["unread"],"title":"UnreadCountResponse","description":"Envelope for ``GET /v1/notifications/count`` (spec §22.2)."},"UpdateAutomationRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Name"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"trigger":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Trigger"},"conditions":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Conditions"},"actions":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Actions"},"run_as_agent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Run As Agent Id"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","title":"UpdateAutomationRequest","description":"Body for ``PATCH /v1/orgs/{org_id}/automations/{id}`` (§20.8).\n\nEvery field is optional — a client sends only what it changes. ``is_active``\nsupports the enable/disable toggle. Fields the client omits are left untouched\n(the router forwards only ``model_dump(exclude_unset=True)`` to the service, so\nan explicit ``null`` is distinguishable from an absent key)."},"UpdateBlockRequest":{"properties":{"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"},"archived":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Archived"}},"additionalProperties":true,"type":"object","title":"UpdateBlockRequest","description":"`PATCH /v1/blocks/{id}` body.\n\nThe shape mirrors Notion: a top-level key matching ``type`` carries the\nnew payload. ``archived`` toggles soft-delete. ``type`` is optional and\nonly used when changing block kind.\n\n``extra=\"allow\"`` is load-bearing — the type-key payload arrives as an\nextra — so a stray ``parent`` key cannot be caught here by\n``extra=\"forbid\"``. It is rejected in ``update_block_for_principal``\ninstead (NOV-498), which keeps the failure a 400 in the Notion-shape error\nenvelope rather than pydantic's 422, and catches MCP callers on the same\npath as HTTP ones."},"UpdateBoardAccessRequest":{"properties":{"role":{"type":"string","title":"Role"}},"additionalProperties":false,"type":"object","required":["role"],"title":"UpdateBoardAccessRequest"},"UpdateBoardRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Description"},"icon":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Icon"},"cover":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Cover"},"status_groups":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Status Groups"},"properties":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Properties"},"visibility":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Visibility"},"workspace_access":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workspace Access"},"permissions":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Permissions"},"property_validation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Property Validation"}},"additionalProperties":false,"type":"object","title":"UpdateBoardRequest"},"UpdateCardRequest":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"properties":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Properties"},"assignees":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Assignees"},"due_date":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Due Date"},"labels":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Labels"},"remove_labels":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Remove Labels"},"is_archived":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Archived"}},"additionalProperties":false,"type":"object","title":"UpdateCardRequest"},"UpdateCardTemplateRequest":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"properties":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Properties"},"body_blocks":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Body Blocks"},"assignees":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Assignees"},"labels":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Labels"},"recurrence_rule":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Recurrence Rule"},"next_materialization_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Materialization At"},"until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Until"},"count_remaining":{"anyOf":[{"type":"integer","maximum":10000.0,"minimum":0.0},{"type":"null"}],"title":"Count Remaining"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"additionalProperties":false,"type":"object","title":"UpdateCardTemplateRequest"},"UpdateColumnRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"position":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Position"},"status_group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status Group"},"autopilot":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Autopilot"},"playbook_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Playbook Id"},"autopilot_label_gate":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Autopilot Label Gate"}},"additionalProperties":false,"type":"object","title":"UpdateColumnRequest"},"UpdateCommentRequest":{"properties":{"body":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Body"}},"additionalProperties":false,"type":"object","required":["body"],"title":"UpdateCommentRequest"},"UpdateFeedbackRequest":{"properties":{"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"admin_notes":{"anyOf":[{"type":"string","maxLength":5000},{"type":"null"}],"title":"Admin Notes"}},"additionalProperties":false,"type":"object","title":"UpdateFeedbackRequest","description":"``PATCH /v1/admin/feedback/{id}`` — status and/or admin notes.\n\nBoth fields are optional and the two are independent, but a body that sets\nNEITHER is a 400 from the router rather than a silent 200: an empty PATCH\nis a client bug, and answering it with the unchanged row would hide it.\n``admin_notes`` accepts the empty string as \"clear the notes\" — distinct\nfrom omitting the key, which leaves them alone."},"UpdateMembershipRequest":{"properties":{"role":{"type":"string","title":"Role"}},"type":"object","required":["role"],"title":"UpdateMembershipRequest"},"UpdateNotificationPreferencesRequest":{"properties":{"email":{"anyOf":[{"additionalProperties":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"propertyNames":{"enum":["card_assigned","automation_notify"]},"type":"object"},{"type":"null"}],"title":"Email"}},"type":"object","title":"UpdateNotificationPreferencesRequest","description":"Partial per-type notification write (NOV-364).\n\n``email`` is a partial MERGE map: each key sets that type's email flag and\ntypes absent from the map are left untouched; an explicit ``None`` value\nCLEARS that type's stored override so it follows the default again (the\nsame tri-state as the ``board_card_view`` merge idiom). An unknown type is\nrejected by the ``NotificationType`` Literal (422), the same way an unknown\n``theme`` value is; ``StrictBool`` pins non-bool values (\"yes\", 1, …) to a\n422 as well — no lax coercion on a consent field. Omitting ``email`` (or\nthe whole ``notifications`` block) is a no-op."},"UpdateOrganizationRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"primary_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Domain"}},"type":"object","title":"UpdateOrganizationRequest"},"UpdatePlaybookRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"steps":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Steps"},"output_target":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Output Target"},"on_success_move_to":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"On Success Move To"}},"type":"object","title":"UpdatePlaybookRequest","description":"Body for ``PATCH /v1/orgs/{org_id}/playbooks/{id}`` (§23.4).\n\nEvery field is optional — a client sends only what it changes. Fields the client\nomits are left untouched (the router forwards only ``model_dump(exclude_unset=\nTrue)``, so an explicit ``null`` clearing ``on_success_move_to`` is\ndistinguishable from an absent key)."},"UpdatePreferencesRequest":{"properties":{"theme":{"anyOf":[{"type":"string","enum":["dark","light","system"]},{"type":"null"}],"title":"Theme"},"default_card_view":{"anyOf":[{"type":"string","enum":["side_panel","center_peek","focused_overlay","full_page"]},{"type":"null"}],"title":"Default Card View"},"board_card_view":{"anyOf":[{"additionalProperties":{"anyOf":[{"type":"string","enum":["side_panel","center_peek","focused_overlay","full_page"]},{"type":"null"}]},"type":"object"},{"type":"null"}],"title":"Board Card View"},"notifications":{"anyOf":[{"$ref":"#/components/schemas/UpdateNotificationPreferencesRequest"},{"type":"null"}]},"dismissed_hints":{"anyOf":[{"additionalProperties":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"propertyNames":{"const":"connect_agent"},"type":"object"},{"type":"null"}],"title":"Dismissed Hints"},"surveys":{"anyOf":[{"additionalProperties":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"propertyNames":{"enum":["nps","upgrade_exit"]},"type":"object"},{"type":"null"}],"title":"Surveys"}},"type":"object","title":"UpdatePreferencesRequest","description":"PATCH /v1/me/preferences. Only the keys the caller wants to change.\n\nSending ``default_card_view: null`` explicitly clears it so the user\nfalls back to the client-side first-time default (NOV-81)."},"UpdateProfileRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Name"},"avatar_url":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Avatar Url"}},"type":"object","title":"UpdateProfileRequest","description":"PATCH /v1/me/profile. Both fields are optional — caller sends only what\nthey want to change. ``None`` and \"omitted\" are different on the wire; we\ntreat \"omitted\" as no-op and an empty string as \"clear the field\"."},"UpdateWebhookRequest":{"properties":{"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"event_types":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Event Types"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","title":"UpdateWebhookRequest"},"UserPreferencesResponse":{"properties":{"object":{"type":"string","title":"Object","default":"user_preferences"},"theme":{"type":"string","enum":["dark","light","system"],"title":"Theme","default":"system"},"default_card_view":{"anyOf":[{"type":"string","enum":["side_panel","center_peek","focused_overlay","full_page"]},{"type":"null"}],"title":"Default Card View"},"board_card_view":{"additionalProperties":{"type":"string","enum":["side_panel","center_peek","focused_overlay","full_page"]},"type":"object","title":"Board Card View"},"notifications":{"$ref":"#/components/schemas/NotificationPreferencesResponse"},"dismissed_hints":{"additionalProperties":{"type":"boolean"},"propertyNames":{"const":"connect_agent"},"type":"object","title":"Dismissed Hints"},"surveys":{"$ref":"#/components/schemas/SurveyPreferencesResponse"}},"type":"object","title":"UserPreferencesResponse"},"UserProfileResponse":{"properties":{"object":{"type":"string","title":"Object","default":"user_profile"},"id":{"type":"string","format":"uuid","title":"Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"email_verified":{"type":"boolean","title":"Email Verified","default":false},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"signup_source":{"type":"string","title":"Signup Source"},"has_password":{"type":"boolean","title":"Has Password"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_signed_in_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Signed In At"}},"type":"object","required":["id","signup_source","has_password","created_at"],"title":"UserProfileResponse","description":"GET /v1/me/profile shape. Self-only — never leaks across users."},"UserResponse":{"properties":{"object":{"type":"string","title":"Object","default":"user"},"id":{"type":"string","format":"uuid","title":"Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"email_verified":{"type":"boolean","title":"Email Verified","default":false},"kind":{"type":"string","title":"Kind","default":"human"},"agent_runtime_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Runtime Url"},"is_platform_admin":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Platform Admin"},"platform_admin_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Platform Admin Role"},"active_workspace":{"anyOf":[{"$ref":"#/components/schemas/ActiveWorkspaceResponse"},{"type":"null"}]}},"type":"object","required":["id"],"title":"UserResponse"},"UserSessionResponse":{"properties":{"object":{"type":"string","title":"Object","default":"user_session"},"id":{"type":"string","format":"uuid","title":"Id"},"user_agent_hint":{"type":"string","title":"User Agent Hint"},"user_agent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Agent"},"ip_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ip Address"},"last_active_at":{"type":"string","format":"date-time","title":"Last Active At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"is_current":{"type":"boolean","title":"Is Current","default":false}},"type":"object","required":["id","user_agent_hint","last_active_at","created_at","expires_at"],"title":"UserSessionResponse"},"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"},"VerifyEmailRequest":{"properties":{"token":{"type":"string","title":"Token"}},"type":"object","required":["token"],"title":"VerifyEmailRequest"},"WebhookDeliveryResponse":{"properties":{"object":{"type":"string","title":"Object","default":"webhook_delivery"},"id":{"type":"string","format":"uuid","title":"Id"},"webhook_id":{"type":"string","format":"uuid","title":"Webhook Id"},"event_id":{"type":"string","format":"uuid","title":"Event Id"},"event_type":{"type":"string","title":"Event Type"},"status":{"type":"string","title":"Status"},"attempts":{"type":"integer","title":"Attempts"},"last_response_status":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Last Response Status"},"last_response_body_excerpt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Response Body Excerpt"},"next_retry_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Retry At"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"}},"type":"object","required":["id","webhook_id","event_id","event_type","status","attempts","last_response_status","last_response_body_excerpt","next_retry_at","created_time","last_edited_time"],"title":"WebhookDeliveryResponse"},"WebhookResponse":{"properties":{"object":{"type":"string","title":"Object","default":"webhook"},"id":{"type":"string","format":"uuid","title":"Id"},"org_id":{"type":"string","format":"uuid","title":"Org Id"},"url":{"type":"string","title":"Url"},"event_types":{"items":{"type":"string"},"type":"array","title":"Event Types"},"board_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Board Id"},"is_active":{"type":"boolean","title":"Is Active"},"secret_last4":{"type":"string","title":"Secret Last4"},"created_time":{"type":"string","format":"date-time","title":"Created Time"},"last_edited_time":{"type":"string","format":"date-time","title":"Last Edited Time"}},"type":"object","required":["id","org_id","url","event_types","board_id","is_active","secret_last4","created_time","last_edited_time"],"title":"WebhookResponse"}},"securitySchemes":{"IntegrationToken":{"type":"http","scheme":"bearer","description":"Integration token. Mint one in the app under **Settings → Integrations** and paste the `nov_…` value here — it is sent as `Authorization: Bearer nov_…`, the same credential the API enforces. Browser session cookies are NOT used by try-it-out (the cookie flow additionally requires an `X-CSRF-Token` header this page cannot mint)."}}}}