{"openapi":"3.1.0","info":{"title":"Procrastitask API","description":"Backend for Procrastitask - reminders that **escalate until dealt with**.\n\n**Auth:** send `Authorization: Bearer <token>`. A session token (`st_`, from\n`POST /v1/auth/apple` or `/v1/auth/email/verify`) or a Personal Access Token\n(`pk_`, from `POST /v1/tokens`, used by agents). `tasks:write` scope is required\non any mutation.\n\n**Agents:** a PAT also authenticates the hosted MCP server at `/mcp`. See\n`docs/agent_setup.md` and `docs/api.md` in the repo.\n\nAll responses use the envelope `{ ok, data, error }`.\n","version":"0.1.0"},"paths":{"/v1/auth/apple":{"post":{"tags":["auth"],"summary":"Apple Sign In","description":"Sign in with Apple: verify the identity token, then find-or-create the\naccount by Apple id. If Apple provides an email that already has an account,\nlink this Apple id to it rather than creating a duplicate.","operationId":"apple_sign_in_v1_auth_apple_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppleAuthRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_EmailAuthResult_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/email/request":{"post":{"tags":["auth"],"summary":"Request Email Code","description":"Email a single-use sign-in code. Verifying it signs in to the email's\naccount, creating one if the email is new (no anonymous account to claim).","operationId":"request_email_code_v1_auth_email_request_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAuthRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_dict_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/email/verify":{"post":{"tags":["auth"],"summary":"Verify Email Code","description":"Verify a code and sign in: log into the email's account if it exists, else\ncreate a new account for that email. Returns a fresh session. The code (which\nonly reaches the email's real inbox) is the proof of ownership - no prior\nsession is involved.","operationId":"verify_email_code_v1_auth_email_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAuthVerify"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_EmailAuthResult_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/session":{"delete":{"tags":["auth"],"summary":"Logout","description":"Revoke the caller's current session token (log out this device).","operationId":"logout_v1_auth_session_delete","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_dict_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/sessions":{"get":{"tags":["auth"],"summary":"List Sessions","description":"List the caller's active sessions (ids + timestamps, never the token) so a\nuser can revoke a device they no longer hold the token for.","operationId":"list_sessions_v1_auth_sessions_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_list_SessionOut__"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/sessions/{session_id}":{"delete":{"tags":["auth"],"summary":"Revoke Session","description":"Revoke a specific session by id (must be the caller's own).","operationId":"revoke_session_v1_auth_sessions__session_id__delete","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_dict_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/mfa/enroll":{"post":{"tags":["auth"],"summary":"Mfa Enroll","description":"Begin TOTP enrollment: generate a secret, store it ENCRYPTED (MFA stays OFF until\nconfirmed), and return the secret + otpauth URI to key an authenticator app.","operationId":"mfa_enroll_v1_auth_mfa_enroll_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_MfaEnrollOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/mfa/enroll/confirm":{"post":{"tags":["auth"],"summary":"Mfa Enroll Confirm","description":"Confirm enrollment with a live code from the app, then flip MFA on and return the\none-time recovery codes (shown ONCE - stored only hashed).","operationId":"mfa_enroll_confirm_v1_auth_mfa_enroll_confirm_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaCode"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_MfaConfirmOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/mfa/verify":{"post":{"tags":["auth"],"summary":"Mfa Verify","description":"Complete a sign-in that returned `mfa_required`: spend the single-use mfa_token plus a\nTOTP or recovery code, and issue the real session token.","operationId":"mfa_verify_v1_auth_mfa_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaVerify"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_EmailAuthResult_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/mfa":{"get":{"tags":["auth"],"summary":"Mfa Status","operationId":"mfa_status_v1_auth_mfa_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_MfaStatusOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/mfa/disable":{"post":{"tags":["auth"],"summary":"Mfa Disable","description":"Turn MFA off. Requires a CURRENT second factor (a stolen session alone must not be\nable to strip 2FA off the account).","operationId":"mfa_disable_v1_auth_mfa_disable_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaCode"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_MfaStatusOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/devices":{"post":{"tags":["devices"],"summary":"Register Device","operationId":"register_device_v1_devices_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceRegister"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_DeviceOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/devices/la-token":{"post":{"tags":["devices"],"summary":"Register La Token","description":"Store the ActivityKit push-to-start token on the matching device (LA F6). Arrives\nasynchronously after device registration, so upsert the device if it's not there yet.","operationId":"register_la_token_v1_devices_la_token_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LaPushToken"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_DeviceOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/devices/test-push":{"post":{"tags":["devices"],"summary":"Send Test Push","description":"Send a real push to every valid device this user has registered.\n\nThe delivery-health screen's only END-TO-END check (docs/delivery_health_spec.md).\nEvery automatic check can pass while delivery is still broken: a token registered\nagainst the wrong APNs environment, a token revoked since registration, an expired\ncredential, or a Focus filter silently dropping us. None of that is visible from the\ndevice, and none of it is visible from `/health`. Only actually sending finds it.\n\nReuses the ordinary `ApnsClient` and its transient/permanent classification (#85)\nrather than a parallel path - a diagnostic that exercises different code from the\nreal thing proves nothing about the real thing.\n\nDeliberately reports PER DEVICE. \"It worked\" is useless to someone with an iPhone and\nan iPad where only one is broken.","operationId":"send_test_push_v1_devices_test_push_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_TestPushOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/devices/ping":{"post":{"tags":["devices"],"summary":"Ping Device","description":"Foreground heartbeat: refresh last_seen_at for the caller's device. The sweep reads a fresh\nlast_seen_at as 'the app is foreground, it will start the Live Activity itself' and SKIPS the\nserver push-start (correctness review CRITICAL #1). The client calls this on every sync + resume,\nso this must be bumped by ordinary use, not just at login. No-op if the device isn't registered.","operationId":"ping_device_v1_devices_ping_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DevicePing"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_DeviceOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/deliveries/ack":{"post":{"tags":["deliveries"],"summary":"Ack Delivery","description":"Mark the most recent unacknowledged delivery for (task, stage) as seen.\n\nKeyed on task_id + stage rather than a delivery id because that is what the push\npayload actually carries (`custom={\"task_id\": ..., \"stage\": ...}`); the client never\nsees the Delivery row's id, and apns_id is not exposed to the app by the OS.\n\nAcks the NEWEST unacked row, not the oldest. Every nudge for a task carries\n`apns-collapse-id = task.id`, so at most ONE notification per task is ever visible -\na tap is therefore always on the most recent send. Stage 2 re-nudges hourly with no\ncap (271 rows against a single open task in prod today) and a recurring task reuses\none task_id across occurrences, so taking the oldest would stamp a delivery sent days\nearlier and make `acked_at - sent_at` latency meaningless.\n\nAPPROXIMATE BY CONSTRUCTION: a user with several devices gets one Delivery row per\ndevice, and one human tap acks one of them. So this measures \"this person saw this\nnudge\", not \"this handset displayed it\" - which is the question the escalation ladder\nneeds answered. Pretending to per-device precision would be a fiction.\n\nIDEMPOTENT PER (task, stage): if ANY row for that pair is already acked, this is a\nno-op. One human tap is one ack, so a client retry, a double tap on a notification\nthat lingers in Notification Center, or the cold-launch path racing the live listener\ncannot ack a second fan-out row and double-count one person.\n\nScoped to the caller's OWN deliveries. `user_id` is on the row, so another account\ncannot acknowledge - or probe the existence of - someone else's nudge.","operationId":"ack_delivery_v1_deliveries_ack_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryAck"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_DeliveryAckOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tasks":{"get":{"tags":["tasks"],"summary":"List Tasks","operationId":"list_tasks_v1_tasks_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_list_TaskOut__"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["tasks"],"summary":"Create Task","operationId":"create_task_v1_tasks_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_TaskOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tasks/{task_id}":{"get":{"tags":["tasks"],"summary":"Get Task","operationId":"get_task_v1_tasks__task_id__get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_TaskDetailOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["tasks"],"summary":"Update Task","operationId":"update_task_v1_tasks__task_id__patch","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_TaskOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tasks/{task_id}/complete":{"post":{"tags":["tasks"],"summary":"Complete","operationId":"complete_v1_tasks__task_id__complete_post","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_TaskOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tasks/{task_id}/snooze":{"post":{"tags":["tasks"],"summary":"Snooze","operationId":"snooze_v1_tasks__task_id__snooze_post","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnoozeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_TaskOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tasks/{task_id}/skip":{"post":{"tags":["tasks"],"summary":"Skip","description":"Close a task WITHOUT doing it - an honest miss (recorded). Distinct from\nCancel (/drop), which voids the task and records nothing.","operationId":"skip_v1_tasks__task_id__skip_post","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_TaskOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tasks/{task_id}/drop":{"post":{"tags":["tasks"],"summary":"Drop","operationId":"drop_v1_tasks__task_id__drop_post","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_TaskOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tasks/{task_id}/accept":{"post":{"tags":["tasks"],"summary":"Accept","description":"The OWNER accepts an assigned task - only now does escalation start.","operationId":"accept_v1_tasks__task_id__accept_post","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_TaskOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tasks/{task_id}/decline":{"post":{"tags":["tasks"],"summary":"Decline","description":"The OWNER declines an assigned task - blameless exit, no escalation.","operationId":"decline_v1_tasks__task_id__decline_post","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_TaskOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tasks/{task_id}/la-token":{"post":{"tags":["tasks"],"summary":"Register La Activity Token","description":"Report a running Live Activity's own push token, so the server can UPDATE and END the\ncard while the app is closed.\n\nOwnership is enforced by _get_owner_only: only the person whose Lock Screen shows the card\ncan bind a token to it. Without that, a token for someone else's activity could be attached\nto a task and pushed the task's title - a cross-account content leak.\n\nReported per activity, however it began (foreground request or server push-to-start), and\nre-reported when ActivityKit rotates it - so this is a plain upsert.","operationId":"register_la_activity_token_v1_tasks__task_id__la_token_post","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LaActivityToken"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_TaskOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sync":{"get":{"tags":["sync"],"summary":"Sync","operationId":"sync_v1_sync_get","parameters":[{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Since"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_dict_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tokens":{"post":{"tags":["tokens"],"summary":"Mint Token","operationId":"mint_token_v1_tokens_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_TokenMinted_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["tokens"],"summary":"List Tokens","operationId":"list_tokens_v1_tokens_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_list_TokenOut__"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tokens/{token_id}":{"delete":{"tags":["tokens"],"summary":"Revoke Token","operationId":"revoke_token_v1_tokens__token_id__delete","parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Token Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_dict_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/spaces":{"post":{"tags":["spaces"],"summary":"Create Space","operationId":"create_space_v1_spaces_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_SpaceOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["spaces"],"summary":"List Spaces","operationId":"list_spaces_v1_spaces_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_list_SpaceOut__"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/spaces/{space_id}/members":{"get":{"tags":["spaces"],"summary":"List Members","operationId":"list_members_v1_spaces__space_id__members_get","parameters":[{"name":"space_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Space Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_list_MemberOut__"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/spaces/{space_id}/invites":{"post":{"tags":["spaces"],"summary":"Create Invite","operationId":"create_invite_v1_spaces__space_id__invites_post","parameters":[{"name":"space_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Space Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_InviteOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/spaces/{space_id}/invites/{invite_id}":{"delete":{"tags":["spaces"],"summary":"Revoke Invite","operationId":"revoke_invite_v1_spaces__space_id__invites__invite_id__delete","parameters":[{"name":"space_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Space Id"}},{"name":"invite_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Invite Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_dict_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/invites/{token}/accept":{"post":{"tags":["spaces"],"summary":"Accept Invite","operationId":"accept_invite_v1_invites__token__accept_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_SpaceOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/spaces/{space_id}/tasks":{"get":{"tags":["spaces"],"summary":"Space Tasks","description":"Shared status view. Accepted tasks are visible to all members (the 'did\nyou do it?' killer); a still-pending assignment request stays visible only to\nthe two parties (owner/assignee + assigner) until the owner consents, so an\nuninvolved member can't see a request before it's been accepted.","operationId":"space_tasks_v1_spaces__space_id__tasks_get","parameters":[{"name":"space_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Space Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_list_TaskOut__"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/spaces/{space_id}/tasks/{task_id}":{"get":{"tags":["spaces"],"summary":"Space Task Detail","description":"Detail for one task in a shared space, so the assigner (or any member, for\nan accepted task) can open it to see status + nudge history. Same visibility\nrule as the status list: accepted tasks are visible to all members; a pending\nrequest stays visible only to the owner + assigner.","operationId":"space_task_detail_v1_spaces__space_id__tasks__task_id__get","parameters":[{"name":"space_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Space Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Task Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_TaskDetailOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/spaces/{space_id}/membership":{"delete":{"tags":["spaces"],"summary":"Leave Space","operationId":"leave_space_v1_spaces__space_id__membership_delete","parameters":[{"name":"space_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Space Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_dict_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me":{"get":{"tags":["me"],"summary":"Get Me","operationId":"get_me_v1_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_UserSettingsOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["me"],"summary":"Update Me","operationId":"update_me_v1_me_patch","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSettingsUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_UserSettingsOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["me"],"summary":"Delete Me","description":"Permanently delete the caller's account and ALL their data (App Store\nGuideline 5.1.1(v); GDPR/CCPA erasure). A core DELETE lets the DB-level ON\nDELETE rules do the work: owned rows (tasks, devices, sessions, PATs, outcomes,\ndeliveries, memberships, invites) cascade away; tasks the user assigned to\nothers and spaces they created survive (assigner_id / created_by set null).","operationId":"delete_me_v1_me_delete","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_dict_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/pause":{"post":{"tags":["me"],"summary":"Pause","description":"Open or extend the global pause (docs/pause_spec.md, #152).\n\nDeliberately reachable by an agent holding settings:write - \"pause my reminders for\na week\" is exactly the request someone makes when they are too unwell to open an app.","operationId":"pause_v1_me_pause_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PauseRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_UserSettingsOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["me"],"summary":"Resume","description":"Resume now, ending the pause early. Idempotent when not paused.","operationId":"resume_v1_me_pause_delete","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_UserSettingsOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/pause/ack":{"post":{"tags":["me"],"summary":"Ack Pause","description":"Acknowledge the welcome-back card, so it shows exactly once per pause.","operationId":"ack_pause_v1_me_pause_ack_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_UserSettingsOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/stats":{"get":{"tags":["stats"],"summary":"Stats","operationId":"stats_v1_stats_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_StatsOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/outcomes":{"get":{"tags":["outcomes"],"summary":"List Outcomes","operationId":"list_outcomes_v1_outcomes_get","parameters":[{"name":"outcome","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(done|skipped)$"},{"type":"null"}],"title":"Outcome"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_list_OutcomeOut__"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/revenuecat":{"post":{"tags":["billing"],"summary":"Revenuecat Webhook","operationId":"revenuecat_webhook_v1_webhooks_revenuecat_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_dict_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"summary":"Health","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Envelope_dict_"}}}}}}}},"components":{"schemas":{"AppleAuthRequest":{"properties":{"identity_token":{"type":"string","maxLength":4096,"title":"Identity Token"},"full_name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Full Name"},"authorization_code":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Authorization Code"}},"type":"object","required":["identity_token"],"title":"AppleAuthRequest"},"DeliveryAck":{"properties":{"task_id":{"type":"string","format":"uuid","title":"Task Id"},"stage":{"type":"integer","maximum":2.0,"minimum":-1.0,"title":"Stage"}},"type":"object","required":["task_id","stage"],"title":"DeliveryAck","description":"A notification tap, reported by the client (#216).\n\nKeyed on task_id + stage because that is what the push payload carries; the client\nnever sees the Delivery row's id, and the OS does not expose apns-id to the app."},"DeliveryAckOut":{"properties":{"acked":{"type":"boolean","title":"Acked"}},"type":"object","required":["acked"],"title":"DeliveryAckOut"},"DeviceOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"platform":{"type":"string","title":"Platform"},"valid":{"type":"boolean","title":"Valid"}},"type":"object","required":["id","platform","valid"],"title":"DeviceOut"},"DevicePing":{"properties":{"apns_token":{"type":"string","maxLength":600,"minLength":32,"pattern":"^[0-9a-fA-F]+$","title":"Apns Token"}},"type":"object","required":["apns_token"],"title":"DevicePing","description":"Foreground heartbeat: bumps the device's last_seen_at so the sweep's push-start heuristic\nhas a real foreground signal (correctness review CRITICAL #1)."},"DeviceRegister":{"properties":{"platform":{"type":"string","title":"Platform","default":"ios"},"apns_token":{"anyOf":[{"type":"string","maxLength":600},{"type":"null"}],"title":"Apns Token"},"fcm_token":{"anyOf":[{"type":"string","maxLength":4096,"minLength":32,"pattern":"^[A-Za-z0-9_:.-]+$"},{"type":"null"}],"title":"Fcm Token"},"push_env":{"type":"string","title":"Push Env","default":"sandbox"}},"type":"object","title":"DeviceRegister","description":"Register a push token. Exactly one token, matching the platform (#161).\n\nBounded rather than pattern-matched on the iOS side: registration never validated the\nAPNs token's shape, and tightening that now could lock out a device that is currently\nworking. Android is a new surface with no installed base, so it gets the stricter rule."},"EmailAuthRequest":{"properties":{"email":{"type":"string","title":"Email"}},"type":"object","required":["email"],"title":"EmailAuthRequest"},"EmailAuthResult":{"properties":{"status":{"type":"string","title":"Status"},"mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mode"},"created":{"type":"boolean","title":"Created","default":false},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"},"session_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session Token"},"mfa_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mfa Token"},"methods":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Methods"}},"type":"object","required":["status"],"title":"EmailAuthResult","description":"Sign-in result (email or Apple). `status` is \"authenticated\" when a session is\nissued, or \"mfa_required\" when the account has 2FA on - then `mfa_token` + `methods`\nare set and the caller completes at POST /v1/auth/mfa/verify. No breaking change (#47)."},"EmailAuthVerify":{"properties":{"email":{"type":"string","title":"Email"},"code":{"type":"string","title":"Code"}},"type":"object","required":["email","code"],"title":"EmailAuthVerify"},"Envelope_DeliveryAckOut_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/DeliveryAckOut"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[DeliveryAckOut]"},"Envelope_DeviceOut_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/DeviceOut"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[DeviceOut]"},"Envelope_EmailAuthResult_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/EmailAuthResult"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[EmailAuthResult]"},"Envelope_InviteOut_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/InviteOut"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[InviteOut]"},"Envelope_MfaConfirmOut_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/MfaConfirmOut"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[MfaConfirmOut]"},"Envelope_MfaEnrollOut_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/MfaEnrollOut"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[MfaEnrollOut]"},"Envelope_MfaStatusOut_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/MfaStatusOut"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[MfaStatusOut]"},"Envelope_SpaceOut_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/SpaceOut"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[SpaceOut]"},"Envelope_StatsOut_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/StatsOut"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[StatsOut]"},"Envelope_TaskDetailOut_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/TaskDetailOut"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[TaskDetailOut]"},"Envelope_TaskOut_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/TaskOut"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[TaskOut]"},"Envelope_TestPushOut_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/TestPushOut"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[TestPushOut]"},"Envelope_TokenMinted_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/TokenMinted"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[TokenMinted]"},"Envelope_UserSettingsOut_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"$ref":"#/components/schemas/UserSettingsOut"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[UserSettingsOut]"},"Envelope_dict_":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[dict]"},"Envelope_list_MemberOut__":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"items":{"$ref":"#/components/schemas/MemberOut"},"type":"array"},{"type":"null"}],"title":"Data"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[list[MemberOut]]"},"Envelope_list_OutcomeOut__":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"items":{"$ref":"#/components/schemas/OutcomeOut"},"type":"array"},{"type":"null"}],"title":"Data"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[list[OutcomeOut]]"},"Envelope_list_SessionOut__":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"items":{"$ref":"#/components/schemas/SessionOut"},"type":"array"},{"type":"null"}],"title":"Data"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[list[SessionOut]]"},"Envelope_list_SpaceOut__":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"items":{"$ref":"#/components/schemas/SpaceOut"},"type":"array"},{"type":"null"}],"title":"Data"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[list[SpaceOut]]"},"Envelope_list_TaskOut__":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"items":{"$ref":"#/components/schemas/TaskOut"},"type":"array"},{"type":"null"}],"title":"Data"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[list[TaskOut]]"},"Envelope_list_TokenOut__":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"data":{"anyOf":[{"items":{"$ref":"#/components/schemas/TokenOut"},"type":"array"},{"type":"null"}],"title":"Data"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"Envelope[list[TokenOut]]"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"InviteCreate":{"properties":{"invitee_contact":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invitee Contact"}},"type":"object","title":"InviteCreate"},"InviteOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"space_id":{"type":"string","format":"uuid","title":"Space Id"},"token":{"type":"string","title":"Token"},"status":{"type":"string","title":"Status"}},"type":"object","required":["id","space_id","token","status"],"title":"InviteOut"},"LaActivityToken":{"properties":{"la_update_token":{"type":"string","maxLength":600,"minLength":32,"pattern":"^[0-9a-fA-F]+$","title":"La Update Token"}},"type":"object","required":["la_update_token"],"title":"LaActivityToken","description":"A RUNNING Live Activity's own ActivityKit push token, reported by the client once\nActivityKit vends it. Distinct from LaPushToken: that one starts a card, this one updates\nand ends the card it belongs to. Same hex-token bound + format validation, for the same\nreason (it is spliced into the outbound APNs URL path)."},"LaPushToken":{"properties":{"apns_token":{"type":"string","maxLength":600,"minLength":32,"pattern":"^[0-9a-fA-F]+$","title":"Apns Token"},"la_push_token":{"type":"string","maxLength":600,"minLength":32,"pattern":"^[0-9a-fA-F]+$","title":"La Push Token"}},"type":"object","required":["apns_token","la_push_token"],"title":"LaPushToken","description":"The ActivityKit push-to-start token, reported after device registration (LA F6). Both are\niOS hex device tokens; bound + format-validate at the boundary (security review M-2/L-1) so an\noversized or path-distorting value can't be stored or spliced into the outbound APNs URL path."},"MemberOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"role":{"type":"string","title":"Role"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"}},"type":"object","required":["user_id","role"],"title":"MemberOut"},"MfaCode":{"properties":{"code":{"type":"string","maxLength":32,"title":"Code"}},"type":"object","required":["code"],"title":"MfaCode","description":"A single TOTP or recovery code (enroll-confirm / disable)."},"MfaConfirmOut":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"recovery_codes":{"items":{"type":"string"},"type":"array","title":"Recovery Codes"}},"type":"object","required":["enabled","recovery_codes"],"title":"MfaConfirmOut"},"MfaEnrollOut":{"properties":{"secret":{"type":"string","title":"Secret"},"otpauth_uri":{"type":"string","title":"Otpauth Uri"}},"type":"object","required":["secret","otpauth_uri"],"title":"MfaEnrollOut","description":"Enrollment payload: the secret to key an authenticator app (shown once) + the\notpauth:// URI to render as a QR. mfa is NOT active until /enroll/confirm succeeds."},"MfaStatusOut":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"recovery_codes_remaining":{"type":"integer","title":"Recovery Codes Remaining","default":0}},"type":"object","required":["enabled"],"title":"MfaStatusOut"},"MfaVerify":{"properties":{"mfa_token":{"type":"string","maxLength":128,"title":"Mfa Token"},"code":{"type":"string","maxLength":32,"title":"Code"}},"type":"object","required":["mfa_token","code"],"title":"MfaVerify","description":"Complete a sign-in: the mfa_token from /email/verify + a TOTP or recovery code."},"OutcomeOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"task_id":{"type":"string","format":"uuid","title":"Task Id"},"title":{"type":"string","title":"Title"},"occurrence_due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Occurrence Due At"},"outcome":{"type":"string","title":"Outcome"},"recorded_at":{"type":"string","format":"date-time","title":"Recorded At"}},"type":"object","required":["id","task_id","title","occurrence_due_at","outcome","recorded_at"],"title":"OutcomeOut"},"PauseRequest":{"properties":{"until":{"type":"string","format":"date-time","title":"Until"},"keep_must_not_miss":{"type":"boolean","title":"Keep Must Not Miss","default":true}},"type":"object","required":["until"],"title":"PauseRequest","description":"Open (or extend) the global pause. `until` is an absolute instant, not a local\nhour - see docs/pause_spec.md on why a pause is deliberately timezone-free."},"SessionOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["id","created_at","expires_at"],"title":"SessionOut","description":"A session for the self-service session list - id + timestamps, never the token."},"SnoozeRequest":{"properties":{"until":{"type":"string","format":"date-time","title":"Until"}},"type":"object","required":["until"],"title":"SnoozeRequest"},"SpaceCreate":{"properties":{"name":{"type":"string","title":"Name"}},"type":"object","required":["name"],"title":"SpaceCreate"},"SpaceOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","created_at"],"title":"SpaceOut"},"StatsOut":{"properties":{"open":{"type":"integer","title":"Open"},"overdue":{"type":"integer","title":"Overdue"},"done":{"type":"integer","title":"Done"},"skipped":{"type":"integer","title":"Skipped"},"snoozed":{"type":"integer","title":"Snoozed"},"nudges_sent":{"type":"integer","title":"Nudges Sent"},"nudges_per_completion":{"type":"number","title":"Nudges Per Completion"},"avg_hours_to_complete":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Hours To Complete"},"completion_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Completion Rate"}},"type":"object","required":["open","overdue","done","skipped","snoozed","nudges_sent","nudges_per_completion","avg_hours_to_complete","completion_rate"],"title":"StatsOut"},"TaskCreate":{"properties":{"title":{"type":"string","maxLength":200,"minLength":1,"title":"Title"},"notes":{"anyOf":[{"type":"string","maxLength":10000},{"type":"null"}],"title":"Notes"},"kind":{"type":"string","title":"Kind","default":"todo"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"has_time":{"type":"boolean","title":"Has Time","default":false},"recurrence":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Recurrence"},"lead_time_days":{"anyOf":[{"type":"integer","maximum":30.0,"minimum":0.0},{"type":"null"}],"title":"Lead Time Days"},"mode":{"type":"string","pattern":"^(escalate|notify_once)$","title":"Mode","default":"escalate"},"break_quiet_hours":{"type":"boolean","title":"Break Quiet Hours","default":false},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"space_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Space Id"}},"type":"object","required":["title"],"title":"TaskCreate"},"TaskDetailOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"owner_id":{"type":"string","format":"uuid","title":"Owner Id"},"assigner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assigner Id"},"space_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Space Id"},"title":{"type":"string","title":"Title"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"kind":{"type":"string","title":"Kind"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"has_time":{"type":"boolean","title":"Has Time"},"mode":{"type":"string","title":"Mode"},"break_quiet_hours":{"type":"boolean","title":"Break Quiet Hours"},"status":{"type":"string","title":"Status"},"accepted_by_owner":{"type":"boolean","title":"Accepted By Owner"},"escalation_stage":{"type":"integer","title":"Escalation Stage"},"nudge_count":{"type":"integer","title":"Nudge Count"},"next_fire_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Fire At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"recurrence":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Recurrence"},"lead_time_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Lead Time Days"},"deferred_until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deferred Until"},"skipped_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Skipped At"},"last_nudge_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Nudge At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","owner_id","assigner_id","space_id","title","notes","kind","due_at","has_time","mode","break_quiet_hours","status","accepted_by_owner","escalation_stage","nudge_count","next_fire_at","completed_at","recurrence","lead_time_days","deferred_until","skipped_at","last_nudge_at","created_at","updated_at"],"title":"TaskDetailOut","description":"Fuller task shape for the MCP agent surface: adds the reminder-behavior\nfields (recurrence, lead time, quiet-hours override, defer target) so an\nagent can read back losslessly what it set. The mobile client stays on the\nleaner TaskOut."},"TaskOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"owner_id":{"type":"string","format":"uuid","title":"Owner Id"},"assigner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assigner Id"},"space_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Space Id"},"title":{"type":"string","title":"Title"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"kind":{"type":"string","title":"Kind"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"has_time":{"type":"boolean","title":"Has Time"},"mode":{"type":"string","title":"Mode"},"break_quiet_hours":{"type":"boolean","title":"Break Quiet Hours"},"status":{"type":"string","title":"Status"},"accepted_by_owner":{"type":"boolean","title":"Accepted By Owner"},"escalation_stage":{"type":"integer","title":"Escalation Stage"},"nudge_count":{"type":"integer","title":"Nudge Count"},"next_fire_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Fire At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"}},"type":"object","required":["id","owner_id","assigner_id","space_id","title","notes","kind","due_at","has_time","mode","break_quiet_hours","status","accepted_by_owner","escalation_stage","nudge_count","next_fire_at","completed_at"],"title":"TaskOut"},"TaskUpdate":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Title"},"notes":{"anyOf":[{"type":"string","maxLength":10000},{"type":"null"}],"title":"Notes"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"has_time":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Time"},"recurrence":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Recurrence"},"lead_time_days":{"anyOf":[{"type":"integer","maximum":30.0,"minimum":0.0},{"type":"null"}],"title":"Lead Time Days"},"mode":{"anyOf":[{"type":"string","pattern":"^(escalate|notify_once)$"},{"type":"null"}],"title":"Mode"},"break_quiet_hours":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Break Quiet Hours"}},"type":"object","title":"TaskUpdate"},"TestPushOut":{"properties":{"devices":{"type":"integer","title":"Devices"},"dry_run":{"type":"boolean","title":"Dry Run"},"results":{"items":{"$ref":"#/components/schemas/TestPushResult"},"type":"array","title":"Results"}},"type":"object","required":["devices","dry_run","results"],"title":"TestPushOut"},"TestPushResult":{"properties":{"platform":{"type":"string","title":"Platform"},"accepted":{"type":"boolean","title":"Accepted"},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail"},"token_invalidated":{"type":"boolean","title":"Token Invalidated","default":false},"dry_run":{"type":"boolean","title":"Dry Run","default":false}},"type":"object","required":["platform","accepted"],"title":"TestPushResult","description":"One device's result from the delivery test (docs/delivery_health_spec.md)."},"TokenCreate":{"properties":{"name":{"type":"string","title":"Name"},"scopes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scopes"}},"type":"object","required":["name"],"title":"TokenCreate"},"TokenMinted":{"properties":{"token":{"type":"string","title":"Token"},"id":{"type":"string","format":"uuid","title":"Id"},"prefix":{"type":"string","title":"Prefix"},"scopes":{"type":"string","title":"Scopes"}},"type":"object","required":["token","id","prefix","scopes"],"title":"TokenMinted","description":"Returned once at creation - carries the plaintext token."},"TokenOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"prefix":{"type":"string","title":"Prefix"},"scopes":{"type":"string","title":"Scopes"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["id","name","prefix","scopes","created_at","last_used_at","expires_at"],"title":"TokenOut"},"UserSettingsOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"timezone":{"type":"string","title":"Timezone"},"quiet_start":{"type":"integer","title":"Quiet Start"},"quiet_end":{"type":"integer","title":"Quiet End"},"is_pro":{"type":"boolean","title":"Is Pro","default":false},"tier":{"type":"string","title":"Tier","default":"free"},"pro_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pro Source"},"pro_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Pro Expires At"},"active_escalating":{"type":"integer","title":"Active Escalating","default":0},"free_escalating_cap":{"type":"integer","title":"Free Escalating Cap","default":0},"paused_until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Paused Until"},"paused_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Paused At"},"pause_keeps_mnm":{"type":"boolean","title":"Pause Keeps Mnm","default":true},"pause_ack_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Pause Ack At"},"is_paused":{"type":"boolean","title":"Is Paused","default":false}},"type":"object","required":["id","timezone","quiet_start","quiet_end"],"title":"UserSettingsOut"},"UserSettingsUpdate":{"properties":{"display_name":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Display Name"},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timezone"},"quiet_start":{"anyOf":[{"type":"integer","maximum":23.0,"minimum":0.0},{"type":"null"}],"title":"Quiet Start"},"quiet_end":{"anyOf":[{"type":"integer","maximum":23.0,"minimum":0.0},{"type":"null"}],"title":"Quiet End"}},"type":"object","title":"UserSettingsUpdate"},"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"}}},"tags":[{"name":"auth","description":"Anonymous sessions and session management."},{"name":"tokens","description":"Personal Access Tokens for agent / API access (ADR 0003)."},{"name":"tasks","description":"Personal tasks: create, list, complete, snooze, drop, accept/decline."},{"name":"spaces","description":"Shared accountability: spaces, invites, and assigned tasks."},{"name":"devices","description":"APNs device registration for push."},{"name":"sync","description":"Delta sync of tasks + breakthrough-alarm slices."},{"name":"me","description":"User settings: timezone and quiet hours."},{"name":"stats","description":"Personal completion statistics."}]}