{"openapi":"3.1.0","info":{"title":"Cuba Research Project API","description":"The public read API of the **Cuba Research Project** archive: Cuban\ngenealogical and demographic records — civil registrations,\nparish books, cemetery and burial registers, censuses and electoral rolls,\nschool lists and military service files — with every person named in them\nindexed and searchable.\n\nEverything documented under **search** and **meta** is public: no key, no\naccount, `GET` only. The remaining tags cover the members' area (family trees,\ntranscription, messaging) and need a session.\n\n* Guide for automated readers: <https://cubaresearchproject.com/llms.txt>\n* This document: <https://cubaresearchproject.com/api/openapi.json>\n\nNames are matched without regard to accent or case, so `Valdes` and `Valdes`\nwith an acute return the same people. Search results come 24 to a page.\n\nPlease identify your client with a descriptive `User-Agent`, keep request rates\ncivil, and credit Cuba Research Project when you publish anything built on it.\n","contact":{"name":"Cuba Research Project","url":"https://cubaresearchproject.com/contact/"},"license":{"name":"Free for research and personal use","url":"https://cubaresearchproject.com/terms-of-service/"},"version":"1.0.0"},"servers":[{"url":"https://cubaresearchproject.com","description":"Production"}],"paths":{"/api/auth/login":{"post":{"tags":["auth"],"summary":"Auth:Cookie-Db.Login","operationId":"auth_cookie_db_login_api_auth_login_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_auth_cookie_db_login_api_auth_login_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorModel"},"examples":{"LOGIN_BAD_CREDENTIALS":{"summary":"Bad credentials or the user is inactive.","value":{"detail":"LOGIN_BAD_CREDENTIALS"}},"LOGIN_USER_NOT_VERIFIED":{"summary":"The user is not verified.","value":{"detail":"LOGIN_USER_NOT_VERIFIED"}}}}}},"204":{"description":"No Content"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/logout":{"post":{"tags":["auth"],"summary":"Auth:Cookie-Db.Logout","operationId":"auth_cookie_db_logout_api_auth_logout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing token or inactive user."},"204":{"description":"No Content"}},"security":[{"APIKeyCookie":[]}]}},"/api/auth/register":{"post":{"tags":["auth"],"summary":"Register:Register","operationId":"register_register_api_auth_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRead"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorModel"},"examples":{"REGISTER_USER_ALREADY_EXISTS":{"summary":"A user with this email already exists.","value":{"detail":"REGISTER_USER_ALREADY_EXISTS"}},"REGISTER_INVALID_PASSWORD":{"summary":"Password validation failed.","value":{"detail":{"code":"REGISTER_INVALID_PASSWORD","reason":"Password should beat least 3 characters"}}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/request-verify-token":{"post":{"tags":["auth"],"summary":"Verify:Request-Token","operationId":"verify_request_token_api_auth_request_verify_token_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_verify_request_token_api_auth_request_verify_token_post"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/verify":{"post":{"tags":["auth"],"summary":"Verify:Verify","operationId":"verify_verify_api_auth_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_verify_verify_api_auth_verify_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRead"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorModel"},"examples":{"VERIFY_USER_BAD_TOKEN":{"summary":"Bad token, not existing user ornot the e-mail currently set for the user.","value":{"detail":"VERIFY_USER_BAD_TOKEN"}},"VERIFY_USER_ALREADY_VERIFIED":{"summary":"The user is already verified.","value":{"detail":"VERIFY_USER_ALREADY_VERIFIED"}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/forgot-password":{"post":{"tags":["auth"],"summary":"Reset:Forgot Password","operationId":"reset_forgot_password_api_auth_forgot_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_reset_forgot_password_api_auth_forgot_password_post"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/reset-password":{"post":{"tags":["auth"],"summary":"Reset:Reset Password","operationId":"reset_reset_password_api_auth_reset_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_reset_reset_password_api_auth_reset_password_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorModel"},"examples":{"RESET_PASSWORD_BAD_TOKEN":{"summary":"Bad or expired token.","value":{"detail":"RESET_PASSWORD_BAD_TOKEN"}},"RESET_PASSWORD_INVALID_PASSWORD":{"summary":"Password validation failed.","value":{"detail":{"code":"RESET_PASSWORD_INVALID_PASSWORD","reason":"Password should be at least 3 characters"}}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/verify-login":{"post":{"tags":["auth"],"summary":"Verify And Login","description":"Confirm the address in the emailed token, then set the session cookie.\n\nAlready-verified is not an error the member can act on — they clicked a link\ntwice — so it answers 409 and the page sends them to sign in rather than\nsilently opening a session off a link that may be sitting in an old inbox.","operationId":"verify_and_login_api_auth_verify_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/oauth/unlink":{"post":{"tags":["auth"],"summary":"Unlink Oauth","description":"Cut the account loose from whichever provider opens it, and post a reset link.\n\nThis is the only way off a provider, so it has to leave a working account\nbehind: the hash on it is the random one from the OAuth signup, which\nnobody knows, so the reset email *is* the new way in. It is sent to the\naddress already on the account — the same one the provider vouched for.\n\nOne route rather than one per provider, because an account has at most one\nlink (associate_by_email is off) and the member is not choosing between\nthem — they are leaving the only arrangement they have.\n\nThe current session stays open. Signing out before following the emailed\nlink would otherwise lock them out of their own account.","operationId":"unlink_oauth_api_auth_oauth_unlink_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/auth/facebook/deauthorize":{"post":{"tags":["auth"],"summary":"Deauthorize","description":"Meta pings this when someone removes the app from their Facebook account.\n\nAnswers 200 in every case it can. Meta retries a failure and eventually\ndisables the callback, and \"this Facebook user was never a member here\" is\nnot a failure — it is the commonest outcome of all, since anyone who\nunlinked from their account page reaches Facebook's screen afterwards.","operationId":"deauthorize_api_auth_facebook_deauthorize_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_deauthorize_api_auth_facebook_deauthorize_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/facebook/data-deletion":{"post":{"tags":["auth"],"summary":"Data Deletion","description":"Meta's \"delete my data\" request. Deletes the account behind the link.\n\nThe response shape is Meta's, not ours: a URL the person can open to see\nwhat happened, and a confirmation code that names this request. Both are\nshown to them inside Facebook.\n\nDeleting the whole account is the honest reading of the request here. An\naccount reached through Facebook Login exists *because* of Facebook, and\nkeeping the trees and uploads while dropping only the link would answer\n\"delete my data\" by deleting the least of it.","operationId":"data_deletion_api_auth_facebook_data_deletion_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_data_deletion_api_auth_facebook_data_deletion_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/facebook/data-deletion/{code}":{"get":{"tags":["auth"],"summary":"Deletion Status","description":"What the status page reads. Deliberately says almost nothing.\n\nAnyone with the code can call this, so it carries no email, no name and no\naccount id — only that a request under this code was received, and when it\nwas finished. That is all Meta asks the page to show, and all a code alone\nshould be able to prove.","operationId":"deletion_status_api_auth_facebook_data_deletion__code__get","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","title":"Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/users/me":{"get":{"tags":["users"],"summary":"Users:Current User","operationId":"users_current_user_api_users_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRead"}}}},"401":{"description":"Missing token or inactive user."}},"security":[{"APIKeyCookie":[]}]},"patch":{"tags":["users"],"summary":"Users:Patch Current User","operationId":"users_patch_current_user_api_users_me_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRead"}}}},"401":{"description":"Missing token or inactive user."},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorModel"},"examples":{"UPDATE_USER_EMAIL_ALREADY_EXISTS":{"summary":"A user with this email already exists.","value":{"detail":"UPDATE_USER_EMAIL_ALREADY_EXISTS"}},"UPDATE_USER_INVALID_PASSWORD":{"summary":"Password validation failed.","value":{"detail":{"code":"UPDATE_USER_INVALID_PASSWORD","reason":"Password should beat least 3 characters"}}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/users/{id}":{"get":{"tags":["users"],"summary":"Users:User","operationId":"users_user_api_users__id__get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRead"}}}},"401":{"description":"Missing token or inactive user."},"403":{"description":"Not a superuser."},"404":{"description":"The user does not exist."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["users"],"summary":"Users:Patch User","operationId":"users_patch_user_api_users__id__patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRead"}}}},"401":{"description":"Missing token or inactive user."},"403":{"description":"Not a superuser."},"404":{"description":"The user does not exist."},"400":{"content":{"application/json":{"examples":{"UPDATE_USER_EMAIL_ALREADY_EXISTS":{"summary":"A user with this email already exists.","value":{"detail":"UPDATE_USER_EMAIL_ALREADY_EXISTS"}},"UPDATE_USER_INVALID_PASSWORD":{"summary":"Password validation failed.","value":{"detail":{"code":"UPDATE_USER_INVALID_PASSWORD","reason":"Password should beat least 3 characters"}}}},"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["users"],"summary":"Users:Delete User","operationId":"users_delete_user_api_users__id__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing token or inactive user."},"403":{"description":"Not a superuser."},"404":{"description":"The user does not exist."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/google/authorize":{"get":{"tags":["auth"],"summary":"Oauth:Google.Cookie-Db-Oauth.Authorize","operationId":"oauth_google_cookie_db_oauth_authorize_api_auth_google_authorize_get","parameters":[{"name":"scopes","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"title":"Scopes"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuth2AuthorizeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/google/callback":{"get":{"tags":["auth"],"summary":"Oauth:Google.Cookie-Db-Oauth.Callback","description":"The response varies based on the authentication backend used.","operationId":"oauth_google_cookie_db_oauth_callback_api_auth_google_callback_get","parameters":[{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code"}},{"name":"code_verifier","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Verifier"}},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"}},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"content":{"application/json":{"examples":{"INVALID_STATE_TOKEN":{"summary":"Invalid state token."},"LOGIN_BAD_CREDENTIALS":{"summary":"User is inactive.","value":{"detail":"LOGIN_BAD_CREDENTIALS"}}},"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/meta":{"get":{"tags":["search"],"summary":"Meta","operationId":"meta_api_meta_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/random":{"get":{"tags":["search"],"summary":"Random Record","description":"A random searchable person — powers the 'Surprise me' discovery button.","operationId":"random_record_api_random_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/suggest":{"get":{"tags":["search"],"summary":"Suggest","description":"Typeahead: distinct names matching the query, most frequent first.\nPowers the home-page search autocomplete.\n\nAccepts either a free-text `q` (matched against the whole name) or the\nstructured given / surname / surname2 trio the hero box now sends; when\nseveral are given they are ANDed, exactly like /api/search.","operationId":"suggest_api_suggest_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}},{"name":"given","in":"query","required":false,"schema":{"type":"string","default":"","title":"Given"}},{"name":"surname","in":"query","required":false,"schema":{"type":"string","default":"","title":"Surname"}},{"name":"surname2","in":"query","required":false,"schema":{"type":"string","default":"","title":"Surname2"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/collections":{"get":{"tags":["search"],"summary":"Collections","description":"Full dataset listing for the Collections page; each links to its on-site\npage (wp_url) so collections point to the collections on this site.\n\nServed from the in-process cache. A cache older than the TTL is still\nserved — the refresh happens behind the response — so the only request that\never pays the two seconds is one that arrives before the boot warm-up has\nfinished.","operationId":"collections_api_collections_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/collections/{dataset_id}/values":{"get":{"tags":["search"],"summary":"Collection Values","description":"Every distinct place (hometowns and ports), ship name or birthplace\n(\"native of\") in one collection, most common first. Feeds the typeahead on its search form.","operationId":"collection_values_api_collections__dataset_id__values_get","parameters":[{"name":"dataset_id","in":"path","required":true,"schema":{"type":"integer","title":"Dataset Id"}},{"name":"field","in":"query","required":true,"schema":{"type":"string","pattern":"^(place|ship|native)$","title":"Field"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/search":{"get":{"tags":["search"],"summary":"Search","operationId":"search_api_search_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"given","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Given"}},{"name":"surname","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surname"}},{"name":"surname2","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surname2"}},{"name":"place","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Place"}},{"name":"ship","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ship"}},{"name":"native","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native"}},{"name":"dataset","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Dataset"}},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Type"}},{"name":"yfrom","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Yfrom"}},{"name":"yto","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Yto"}},{"name":"bfrom","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Bfrom"}},{"name":"bto","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Bto"}},{"name":"dfrom","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Dfrom"}},{"name":"dto","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Dto"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","default":"relevance","title":"Sort"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"source","in":"query","required":false,"schema":{"type":"string","default":"","title":"Source"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/record/{record_id}":{"get":{"tags":["search"],"summary":"Record","operationId":"record_api_record__record_id__get","parameters":[{"name":"record_id","in":"path","required":true,"schema":{"type":"integer","title":"Record Id"}},{"name":"person_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Person Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/record/{record_id}/companions":{"get":{"tags":["search"],"summary":"Record Companions","description":"The other people on the same voyage.\n\nThe ship-passenger collection groups its rows by an internal ship code, and\nfinding relatives who travelled together is how the source was meant to be\nread. Returns the companions on the same code in the same collection, the\nprincipal first per record, capped so a 900-passenger steamer does not\nbecome the page.","operationId":"record_companions_api_record__record_id__companions_get","parameters":[{"name":"record_id","in":"path","required":true,"schema":{"type":"integer","title":"Record Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/person/{person_id}/main-record":{"get":{"tags":["search"],"summary":"Person Main Record","operationId":"person_main_record_api_person__person_id__main_record_get","parameters":[{"name":"person_id","in":"path","required":true,"schema":{"type":"integer","title":"Person Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/stats/imports":{"get":{"tags":["search"],"summary":"Import Stats","description":"Records imported since we started (IMPORT_SINCE), from two sources:\narchive collections (records.created_at) and family trees (import_events,\nnet of deletions). Returns a daily series for the filled area chart.","operationId":"import_stats_api_stats_imports_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/favorites":{"get":{"tags":["user"],"summary":"List Favorites","operationId":"list_favorites_api_favorites_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]},"post":{"tags":["user"],"summary":"Add Favorite","operationId":"add_favorite_api_favorites_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FavoriteIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/favorites/ids":{"get":{"tags":["user"],"summary":"Favorite Ids","operationId":"favorite_ids_api_favorites_ids_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/favorites/{record_id}":{"delete":{"tags":["user"],"summary":"Remove Favorite","operationId":"remove_favorite_api_favorites__record_id__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"record_id","in":"path","required":true,"schema":{"type":"integer","title":"Record Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/submissions":{"post":{"tags":["user"],"summary":"Create Submission","operationId":"create_submission_api_submissions_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/submissions/mine":{"get":{"tags":["user"],"summary":"My Submissions","operationId":"my_submissions_api_submissions_mine_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/profiles/{user_id}":{"get":{"tags":["user"],"summary":"Public Profile","operationId":"public_profile_api_profiles__user_id__get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/stats":{"get":{"tags":["admin"],"summary":"Stats","operationId":"stats_api_admin_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/admin/overview/reset":{"post":{"tags":["admin"],"summary":"Reset Overview","description":"Start the overview's counts from now. Nothing is deleted.","operationId":"reset_overview_api_admin_overview_reset_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/admin/overview":{"get":{"tags":["admin"],"summary":"Overview","description":"Everything the admin overview shows, for one period.\n\nThree kinds of figure, kept apart because they answer different questions:\n`summary` is what happened in the period (with the period before it, for\ndirection); `totals` is what exists right now; `attention` is what is\nwaiting on an administrator. Archive records and tree people stay separate\nthroughout: tree people are user-supplied genealogy, not archival evidence.","operationId":"overview_api_admin_overview_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"window","in":"query","required":false,"schema":{"type":"string","default":"30d","title":"Window"}},{"name":"start","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start"}},{"name":"end","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/activity":{"get":{"tags":["admin"],"summary":"Activity Feed","description":"Everything that happened on the site, newest first.\n\nSign-ins are left out unless asked for by name: there are many more of\nthem than of anything else, and an unfiltered feed of logins buries the\ntree someone deleted this morning.","operationId":"activity_feed_api_admin_activity_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Kind"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Q"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/trees":{"get":{"tags":["admin"],"summary":"Trees","description":"Every family tree that exists, with its owner and its size.","operationId":"trees_api_admin_trees_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Q"}},{"name":"visibility","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(public|private)$"},{"type":"null"}],"title":"Visibility"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","pattern":"^(updated|created|people|title)$","default":"updated","title":"Sort"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/deleted":{"get":{"tags":["admin"],"summary":"Deleted","description":"What has been deleted, what it was, and who deleted it.","operationId":"deleted_api_admin_deleted_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(tree_deleted|account_deleted|document_deleted|photo_deleted)$"},{"type":"null"}],"title":"Kind"}},{"name":"cause","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(owner|admin|account|facebook|self)$"},{"type":"null"}],"title":"Cause"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Q"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/transcribe-settings":{"get":{"tags":["admin"],"summary":"Get Transcribe Settings","description":"Current cleanup prompt and the models used by future transcribe jobs.","operationId":"get_transcribe_settings_api_admin_transcribe_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]},"put":{"tags":["admin"],"summary":"Update Transcribe Settings","description":"Persist settings for future cleanup and demographic-extraction runs.","operationId":"update_transcribe_settings_api_admin_transcribe_settings_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscribeSettingsPatch"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/admin/ocr-credits":{"get":{"tags":["admin"],"summary":"Ocr Credits","description":"HandwritingOCR's page credits, the top-up policy, and recent spending.\n\nThe member-facing allowance (transcribe_allowance) and this balance are\nseparate meters: a member can have pages left while the vendor account has\nnone, in which case every upload fails at their end. This is where an\nadministrator sees that coming.","operationId":"ocr_credits_api_admin_ocr_credits_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/admin/ocr-credit-settings":{"put":{"tags":["admin"],"summary":"Update Ocr Credit Settings","description":"Save the credit policy. Applies to the next document, not to jobs already running.","operationId":"update_ocr_credit_settings_api_admin_ocr_credit_settings_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OcrCreditSettingsPatch"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/admin/ocr-credits/topup":{"post":{"tags":["admin"],"summary":"Buy Ocr Credits","description":"Buy HandwritingOCR page credits now, on the card held by their account.\n\nNot subject to the daily cap: that cap exists to bound what the pipeline\nspends unattended, and an administrator pressing this button is neither\nunattended nor unaware. The purchase is still recorded in the ledger.","operationId":"buy_ocr_credits_api_admin_ocr_credits_topup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TopUp"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/admin/ocr-credits/purchases":{"get":{"tags":["admin"],"summary":"Ocr Credit Purchases","description":"The last 50 credit purchases: the audit trail for money spent at the vendor.","operationId":"ocr_credit_purchases_api_admin_ocr_credits_purchases_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/admin/transcribe-jobs":{"get":{"tags":["admin"],"summary":"Transcribe Jobs","description":"Recent transcription jobs with their recorded steps.\n\nOne query per page of jobs plus one for their steps, rather than a join —\na job has a handful of steps and folding them client-side keeps the row\ncount honest for pagination.","operationId":"transcribe_jobs_api_admin_transcribe_jobs_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Status"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/transcribe-jobs/{job_id}/retry":{"post":{"tags":["admin"],"summary":"Retry Transcribe Job","description":"Put a failed job back on the queue.\n\nAttempts reset to zero: an admin retrying by hand has usually just fixed\nwhatever made it fail, and should get the full retry budget again.","operationId":"retry_transcribe_job_api_admin_transcribe_jobs__job_id__retry_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"integer","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/{user_id}/documents":{"get":{"tags":["admin"],"summary":"User Documents","operationId":"user_documents_api_admin_users__user_id__documents_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/documents/{doc_id}":{"get":{"tags":["admin"],"summary":"Document Detail","description":"The document, every page in all three versions, and its jobs.\n\nOpening someone's family papers is written into their account history, so\nthe member's record shows who looked. Once an hour per admin and document:\nthe page refetches on focus, and each refetch is not a new look.","operationId":"document_detail_api_admin_documents__doc_id__get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/documents/{doc_id}/file":{"get":{"tags":["admin"],"summary":"Document File","description":"The original scan, as the member uploaded it.","operationId":"document_file_api_admin_documents__doc_id__file_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users":{"get":{"tags":["admin"],"summary":"Users","description":"The member list, and the four questions it gets asked about a row.\n\nHow did they get in, what are they paying for, have they confirmed their\naddress, and are they staff. All four were answerable only by opening each\naccount one at a time, which for \"who never confirmed their email\" meant\nopening all of them.\n\nThe joins are LEFT and every one of them has a default, because none of the\nthree tables is guaranteed to hold a row: an account has no oauth_account\nunless it came in through a provider, no transcribe_allowance until it\ntouches transcription, and no billing_accounts until it reaches checkout.\nAn INNER join here would quietly shorten the list.","operationId":"users_api_admin_users_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Q"}},{"name":"provider","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(password|google|facebook)$"},{"type":"null"}],"title":"Provider"}},{"name":"plan","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(free|hobbyist|researcher|archive)$"},{"type":"null"}],"title":"Plan"}},{"name":"verified","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yes|no)$"},{"type":"null"}],"title":"Verified"}},{"name":"role","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(admin|member)$"},{"type":"null"}],"title":"Role"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(active|disabled)$"},{"type":"null"}],"title":"Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/ids":{"get":{"tags":["admin"],"summary":"User Ids","description":"Every id the list's current filters match, across all pages — what\n\"select all\" takes when the answer runs longer than one page.","operationId":"user_ids_api_admin_users_ids_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Q"}},{"name":"provider","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(password|google|facebook)$"},{"type":"null"}],"title":"Provider"}},{"name":"plan","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(free|hobbyist|researcher|archive)$"},{"type":"null"}],"title":"Plan"}},{"name":"verified","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(yes|no)$"},{"type":"null"}],"title":"Verified"}},{"name":"role","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(admin|member)$"},{"type":"null"}],"title":"Role"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(active|disabled)$"},{"type":"null"}],"title":"Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/{user_id}":{"get":{"tags":["admin"],"summary":"User Detail","operationId":"user_detail_api_admin_users__user_id__get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["admin"],"summary":"Update User","operationId":"update_user_api_admin_users__user_id__patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUserPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Delete User","description":"Delete the account and everything it owns. Not reversible.\n\nThe purge itself is UserManager.on_before_delete — the same one a member\ndeleting themselves goes through, so the two can never disagree about what\n\"everything\" means. A live Stripe subscription is cancelled first: deleting\nthe account without it would leave Stripe charging a card for a member who\nno longer exists here to use or cancel it.","operationId":"delete_user_api_admin_users__user_id__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteUser"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/{user_id}/allowance":{"get":{"tags":["admin"],"summary":"Read Allowance","description":"This account's allowance, plus the ledger behind it — the audit trail.","operationId":"read_allowance_api_admin_users__user_id__allowance_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["admin"],"summary":"Update Allowance","operationId":"update_allowance_api_admin_users__user_id__allowance_patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AllowancePatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/{user_id}/sign-out":{"post":{"tags":["admin"],"summary":"Sign Out Everywhere","description":"End every open session. For a shared or lost device, or a password that\nmay have leaked — they can sign straight back in with the right one.","operationId":"sign_out_everywhere_api_admin_users__user_id__sign_out_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/{user_id}/password-reset":{"post":{"tags":["admin"],"summary":"Send Password Reset","description":"Mail the member the same reset link \"Forgot password\" would.\n\nNobody here ever sets or sees a password. An account opened through Google\nor Facebook has none, and is refused rather than sent the directions email\nthe public form sends it — an admin asking for this has the wrong idea about\nthe account, and should be told so.","operationId":"send_password_reset_api_admin_users__user_id__password_reset_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/{user_id}/verification-email":{"post":{"tags":["admin"],"summary":"Resend Verification","operationId":"resend_verification_api_admin_users__user_id__verification_email_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/bulk":{"post":{"tags":["admin"],"summary":"Bulk Users","description":"One action on many accounts, by running the single-account route once\nper id. Its refusals (already confirmed, disabled, signs in with Google,\nyourself) come back per account as the reason it was skipped, instead of\nstopping the rest; each account commits on its own, audit line included.\nDeleting is deliberately not offered: that one stays a typed-back\nconfirmation on the member's own page.","operationId":"bulk_users_api_admin_users_bulk_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUsers"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/admin/submissions":{"get":{"tags":["admin"],"summary":"Submissions","operationId":"submissions_api_admin_submissions_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/submissions/{sid}":{"patch":{"tags":["admin"],"summary":"Update Submission","operationId":"update_submission_api_admin_submissions__sid__patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"sid","in":"path","required":true,"schema":{"type":"integer","title":"Sid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/message-reports":{"get":{"tags":["admin"],"summary":"Message Reports","operationId":"message_reports_api_admin_message_reports_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/message-reports/{rid}":{"patch":{"tags":["admin"],"summary":"Update Message Report","operationId":"update_message_report_api_admin_message_reports__rid__patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"rid","in":"path","required":true,"schema":{"type":"integer","title":"Rid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/review-queue":{"get":{"tags":["admin"],"summary":"Review Queue","operationId":"review_queue_api_admin_review_queue_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/records/{rid}/resolve-review":{"post":{"tags":["admin"],"summary":"Resolve Review","operationId":"resolve_review_api_admin_records__rid__resolve_review_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"rid","in":"path","required":true,"schema":{"type":"integer","title":"Rid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/me":{"get":{"tags":["admin"],"summary":"Whoami","operationId":"whoami_api_admin_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/admin/hints":{"get":{"tags":["admin"],"summary":"Hints Status","description":"Size of the index and the state of the scan queue.","operationId":"hints_status_api_admin_hints_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/admin/hints/reindex":{"post":{"tags":["admin"],"summary":"Hints Reindex","description":"Rebuild the hint candidate index, then queue every tree for a rescan.\n\nRun after importing a collection. The archive half is a ~20 second pass over\n665k people, so this holds the request open — it is an admin action taken\ndeliberately, not something a page load triggers.","operationId":"hints_reindex_api_admin_hints_reindex_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"archive","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Archive"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/mailer/stats":{"get":{"tags":["mailer"],"summary":"Stats","operationId":"stats_api_admin_mailer_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/admin/mailer/lists":{"get":{"tags":["mailer"],"summary":"Lists","operationId":"lists_api_admin_mailer_lists_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/admin/mailer/subscribers":{"get":{"tags":["mailer"],"summary":"Subscribers","operationId":"subscribers_api_admin_mailer_subscribers_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"list","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"List"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/mailer/campaigns":{"get":{"tags":["mailer"],"summary":"Campaigns","operationId":"campaigns_api_admin_mailer_campaigns_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/mailer/campaigns/{campaign_id}":{"get":{"tags":["mailer"],"summary":"Campaign Detail","operationId":"campaign_detail_api_admin_mailer_campaigns__campaign_id__get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"integer","title":"Campaign Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/mailer/campaigns/{campaign_id}/recipients":{"get":{"tags":["mailer"],"summary":"Campaign Recipients","operationId":"campaign_recipients_api_admin_mailer_campaigns__campaign_id__recipients_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"integer","title":"Campaign Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/mailer/campaigns/{campaign_id}/events":{"get":{"tags":["mailer"],"summary":"Campaign Events","operationId":"campaign_events_api_admin_mailer_campaigns__campaign_id__events_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"campaign_id","in":"path","required":true,"schema":{"type":"integer","title":"Campaign Id"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/mailer/campaigns/send":{"post":{"tags":["mailer"],"summary":"Send Campaign","operationId":"send_campaign_api_admin_mailer_campaigns_send_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/mailer/subscribe":{"post":{"tags":["mailer"],"summary":"Subscribe","operationId":"subscribe_api_mailer_subscribe_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscribeIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/mailer/confirm":{"post":{"tags":["mailer"],"summary":"Confirm Subscription","description":"Click target of the confirmation email: flip every pending list row for\nthis subscriber to 'subscribed'. The token stays valid so a second click\nreads as \"already confirmed\" instead of a broken link.","operationId":"confirm_subscription_api_mailer_confirm_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/mailer/webhooks/resend":{"post":{"tags":["mailer"],"summary":"Resend Webhook","operationId":"resend_webhook_api_mailer_webhooks_resend_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/trees":{"get":{"tags":["trees"],"summary":"My Trees","operationId":"my_trees_api_trees_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]},"post":{"tags":["trees"],"summary":"Create Tree","operationId":"create_tree_api_trees_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TreeCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/trees/import":{"post":{"tags":["trees"],"summary":"Import Tree","description":"DEPRECATED: create a tree from GEDCOM already parsed in the browser.\n\nSuperseded by POST /import/upload, which uploads the raw file and parses it\nserver-side in the background. Kept only so a stale SPA cached in someone's\nbrowser keeps working; delete once the logs show no hits. Large payloads are\nrefused because parsing them client-side is what crashed the tab.","operationId":"import_tree_api_trees_import_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TreeImport"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/trees/import/upload":{"post":{"tags":["trees"],"summary":"Upload Gedcom","description":"Queue a GEDCOM for background import. Returns as soon as the file is on\ndisk — the actual parse + insert happens in tree_import.py, and the owner\ngets a WebSocket notification when it finishes.\n\nThe file is streamed to disk in chunks so a 25 MB upload never sits in\nmemory, and lands under UPLOAD_DIR (outside the deploy rsync path) so an\nin-flight job survives a restart. A .zip is accepted too; its one GEDCOM\nis extracted and the archive discarded.","operationId":"upload_gedcom_api_trees_import_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_gedcom_api_trees_import_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/trees/imports":{"get":{"tags":["trees"],"summary":"My Imports","description":"Import jobs still running, plus anything that finished in the last day —\nso a user who closed the tab mid-import still sees how it went.","operationId":"my_imports_api_trees_imports_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/trees/imports/{job_id}":{"get":{"tags":["trees"],"summary":"My Import","operationId":"my_import_api_trees_imports__job_id__get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"integer","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/public":{"get":{"tags":["trees"],"summary":"Public Trees","description":"The community directory. Every aggregate on a card — people, birth\nyears, surnames — is measured over the people a visitor can open, the same\nprivacy rule as the counts, so no living relative leaks through a summary.\n\nA signed-out visitor sees the directory but not who keeps each tree: the\nowner comes back as a same-length stand-in the page draws blurred, with no\nid or picture, and searching by an owner's name finds nothing for them.","operationId":"public_trees_api_trees_public_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}":{"get":{"tags":["trees"],"summary":"Get Tree","operationId":"get_tree_api_trees__tree_id__get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["trees"],"summary":"Save Tree","operationId":"save_tree_api_trees__tree_id__put","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TreeSave"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["trees"],"summary":"Delete Tree","operationId":"delete_tree_api_trees__tree_id__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/persons/{person_id}/citations":{"get":{"tags":["trees"],"summary":"Person Citations","description":"List a person's fact citations. Owner always; others only if the tree is public.","operationId":"person_citations_api_trees__tree_id__persons__person_id__citations_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["trees"],"summary":"Add Citation","operationId":"add_citation_api_trees__tree_id__persons__person_id__citations_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TreeCitationBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/citations/{cid}":{"delete":{"tags":["trees"],"summary":"Delete Citation","operationId":"delete_citation_api_trees__tree_id__citations__cid__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"cid","in":"path","required":true,"schema":{"type":"integer","title":"Cid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/uploads":{"post":{"tags":["trees"],"summary":"Upload Document","description":"Store a supporting document (image or PDF) for a citation. Owner-only.\nReturns a served URL to hand back on the citation as file_url.","operationId":"upload_document_api_trees__tree_id__uploads_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_document_api_trees__tree_id__uploads_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/persons/{person_id}/copy-preview":{"get":{"tags":["trees"],"summary":"Copy Preview","operationId":"copy_preview_api_trees__tree_id__persons__person_id__copy_preview_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}},{"name":"into","in":"query","required":true,"schema":{"type":"integer","title":"Into"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/copy":{"post":{"tags":["trees"],"summary":"Copy Person","operationId":"copy_person_api_trees__tree_id__copy_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopyBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/insights":{"get":{"tags":["trees"],"summary":"Get Insights","description":"The cached cards, plus enough state for the page to show its freshness.\n\nNever blocks on the model: a tree with no cards yet is scheduled and comes\nback as `running`, and the page polls.","operationId":"get_insights_api_trees__tree_id__insights_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/insights/refresh":{"post":{"tags":["trees"],"summary":"Refresh Insights","description":"Owner-only \"check now\". Not forced: it rewrites only the cards whose facts\nmoved since the last run, so pressing it on an unchanged tree costs nothing.","operationId":"refresh_insights_api_trees__tree_id__insights_refresh_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/hints":{"get":{"tags":["hints"],"summary":"List Hints","description":"One page of hints, plus everything the page's rails need to render.\n\nReturns the filtered page AND the unfiltered tallies — per person, per band,\nper kind — so choosing a filter never costs a second round trip and the\ncounts beside each choice show what picking it would actually give you.","operationId":"list_hints_api_trees__tree_id__hints_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"person_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Person Id"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","default":"pending","title":"Status"}},{"name":"band","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Band"}},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind"}},{"name":"include_weak","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Weak"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/hints/refresh":{"post":{"tags":["hints"],"summary":"Refresh Hints","description":"Rescan on demand. Runs in the background: a large tree takes seconds and\nthe caller should not hold a request open for it.","operationId":"refresh_hints_api_trees__tree_id__hints_refresh_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Force"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/hints/{hint_id}/accept":{"post":{"tags":["hints"],"summary":"Accept","description":"Confirm a hint.\n\nTwo different acts behind one button, because they are two different\nclaims. A same-person hint attaches the record as a source, copies over\nwhichever fields the reader ticked in the comparison, and then looks at the\nfamily the record names. A relative hint CREATES that person in the stated\nrelationship — there is nothing to compare, because they are not in the\ntree yet.","operationId":"accept_api_trees__tree_id__hints__hint_id__accept_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"hint_id","in":"path","required":true,"schema":{"type":"integer","title":"Hint Id"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AcceptBody"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/hints/{hint_id}/reject":{"post":{"tags":["hints"],"summary":"Reject","description":"Dismiss a hint, permanently.\n\nThe row stays and its status is what every later scan checks, so this pair\nis never suggested again — not after a retune, not after a re-import.","operationId":"reject_api_trees__tree_id__hints__hint_id__reject_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"hint_id","in":"path","required":true,"schema":{"type":"integer","title":"Hint Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/hints/{hint_id}/undo":{"post":{"tags":["hints"],"summary":"Undo","description":"Put a decided hint back to pending.\n\nRejections are permanent against the MATCHER, not against the person who\nmade them — misclicking \"not a match\" must be recoverable. Undoing an accept\nleaves the attached source in place: removing it belongs to the Sources\ncolumn, where the user can see what else it backs.","operationId":"undo_api_trees__tree_id__hints__hint_id__undo_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"hint_id","in":"path","required":true,"schema":{"type":"integer","title":"Hint Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/persons/{person_id}/links":{"get":{"tags":["hints"],"summary":"Person Links","description":"The other trees this person has been linked to. Same visibility rule as\na person's citations: the owner always, anyone else only on a public tree\nand only for a person that tree is not hiding.","operationId":"person_links_api_trees__tree_id__persons__person_id__links_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/links/{link_id}":{"delete":{"tags":["hints"],"summary":"Delete Link","description":"Undo the link itself. Undoing the HINT leaves this in place — the same\nrule an accepted record follows — so this is where it comes off.","operationId":"delete_link_api_trees__tree_id__links__link_id__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"link_id","in":"path","required":true,"schema":{"type":"integer","title":"Link Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trees/{tree_id}/hints/{hint_id}/compare":{"get":{"tags":["hints"],"summary":"Compare Hint","description":"Side by side: what the tree says, what the record says, field by field.\n\nRead-only. The accept that follows takes the keys the reader ticked.","operationId":"compare_hint_api_trees__tree_id__hints__hint_id__compare_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"hint_id","in":"path","required":true,"schema":{"type":"integer","title":"Hint Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/hints/recent":{"get":{"tags":["hints"],"summary":"Recent Hints","description":"The newest open hints across every tree this person owns.\n\nAlso returns the per-tree totals, because \"view all\" has to point at a\ntree's page and there is no cross-tree one — with several trees the caller\nrenders a link each rather than guessing which was meant.","operationId":"recent_hints_api_hints_recent_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":5,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/messages/unread":{"get":{"tags":["messages"],"summary":"Unread","operationId":"unread_api_messages_unread_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/messages/conversations":{"get":{"tags":["messages"],"summary":"Conversations","operationId":"conversations_api_messages_conversations_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"folder","in":"query","required":false,"schema":{"type":"string","default":"inbox","title":"Folder"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/messages/conversations/{cid}":{"get":{"tags":["messages"],"summary":"Conversation","operationId":"conversation_api_messages_conversations__cid__get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"cid","in":"path","required":true,"schema":{"type":"integer","title":"Cid"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Before"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["messages"],"summary":"Delete For Me","description":"Delete-for-me: hide the thread and its history until a new message arrives.","operationId":"delete_for_me_api_messages_conversations__cid__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"cid","in":"path","required":true,"schema":{"type":"integer","title":"Cid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/messages":{"post":{"tags":["messages"],"summary":"Compose","operationId":"compose_api_messages_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComposeIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/messages/conversations/{cid}/messages":{"post":{"tags":["messages"],"summary":"Reply","operationId":"reply_api_messages_conversations__cid__messages_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"cid","in":"path","required":true,"schema":{"type":"integer","title":"Cid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/messages/conversations/{cid}/read":{"post":{"tags":["messages"],"summary":"Mark Read","operationId":"mark_read_api_messages_conversations__cid__read_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"cid","in":"path","required":true,"schema":{"type":"integer","title":"Cid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/messages/conversations/{cid}/accept":{"post":{"tags":["messages"],"summary":"Accept Request","operationId":"accept_request_api_messages_conversations__cid__accept_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"cid","in":"path","required":true,"schema":{"type":"integer","title":"Cid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/messages/conversations/{cid}/folder":{"post":{"tags":["messages"],"summary":"Set Folder","operationId":"set_folder_api_messages_conversations__cid__folder_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"cid","in":"path","required":true,"schema":{"type":"integer","title":"Cid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/messages/blocks":{"get":{"tags":["messages"],"summary":"List Blocks","operationId":"list_blocks_api_messages_blocks_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]},"post":{"tags":["messages"],"summary":"Block User","operationId":"block_user_api_messages_blocks_post","requestBody":{"content":{"application/json":{"schema":{"type":"object","title":"Body"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/messages/blocks/{user_id}":{"delete":{"tags":["messages"],"summary":"Unblock User","operationId":"unblock_user_api_messages_blocks__user_id__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/messages/reports":{"post":{"tags":["messages"],"summary":"Report","operationId":"report_api_messages_reports_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/messages/users/search":{"get":{"tags":["messages"],"summary":"Search Users","operationId":"search_users_api_messages_users_search_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/comments":{"get":{"tags":["comments"],"summary":"List Comments","description":"Every comment on a page, threaded. Public: reading needs no account.","operationId":"list_comments_api_comments_get","parameters":[{"name":"page_key","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":300,"title":"Page Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["comments"],"summary":"Create Comment","description":"Leave a comment, or a reply to one. Signing in is what it takes.","operationId":"create_comment_api_comments_post","security":[{"APIKeyCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/comments/counts":{"get":{"tags":["comments"],"summary":"Comment Counts","description":"How many comments each page has, for listings. `keys` is comma-separated.","operationId":"comment_counts_api_comments_counts_get","parameters":[{"name":"keys","in":"query","required":false,"schema":{"type":"string","maxLength":8000,"default":"","title":"Keys"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/comments/{comment_id}":{"patch":{"tags":["comments"],"summary":"Edit Comment","description":"Change your own comment. It is marked as edited from then on.","operationId":"edit_comment_api_comments__comment_id__patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"integer","title":"Comment Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentEdit"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["comments"],"summary":"Delete Comment","description":"Remove your own comment (an admin may remove any).\n\nA comment with replies under it leaves a tombstone: deleting it outright\nwould take the answers with it, and those belong to the people who wrote\nthem. A comment nobody replied to is removed completely.","operationId":"delete_comment_api_comments__comment_id__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"integer","title":"Comment Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/documents":{"get":{"tags":["transcribe"],"summary":"List Documents","description":"Every document this user has uploaded, newest first, with page progress.","operationId":"list_documents_api_transcribe_documents_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]},"post":{"tags":["transcribe"],"summary":"Upload","description":"Accept a scan, store it outside the web root, and start the pipeline.","operationId":"upload_api_transcribe_documents_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_api_transcribe_documents_post"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/transcribe/allowance":{"get":{"tags":["transcribe"],"summary":"Get Allowance","description":"What this account has left. Read by the transcribe page before uploading.","operationId":"get_allowance_api_transcribe_allowance_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/transcribe/documents/{doc_id}":{"get":{"tags":["transcribe"],"summary":"Get Document","operationId":"get_document_api_transcribe_documents__doc_id__get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["transcribe"],"summary":"Update Document Meta","description":"Edit what the document is called, and what it is.\n\nClearing a field stores an empty string rather than NULL, which is how the\npipeline knows the member meant it: the description pass only ever fills a\nfield that is NULL, so a summary deleted on purpose stays deleted.","operationId":"update_document_meta_api_transcribe_documents__doc_id__patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentMeta"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["transcribe"],"summary":"Delete Document","operationId":"delete_document_api_transcribe_documents__doc_id__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/documents/{doc_id}/describe":{"post":{"tags":["transcribe"],"summary":"Describe Document","description":"Ask for the summary, period and place to be written again.\n\nDeliberate, so it overwrites: a member who presses this wants the model's\nreading of the text as it stands now. The title is never touched — that one\nis only ever the filename or what they typed.","operationId":"describe_document_api_transcribe_documents__doc_id__describe_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/documents/{doc_id}/file":{"get":{"tags":["transcribe"],"summary":"Get Document File","description":"Stream the original scan. Authenticated: uploads are never served\nstatically, unlike everything under /wp-content/uploads.","operationId":"get_document_file_api_transcribe_documents__doc_id__file_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/documents/{doc_id}/thumbnail":{"get":{"tags":["transcribe"],"summary":"Get Document Thumbnail","description":"A small picture of the first page, made once and kept beside the scan.\n\nA list of scans is otherwise thirty identical marks; what tells one parish\nregister from the next at a glance is the paper it is written on. Built on\nthe first request rather than at upload, so it costs nothing for a document\nnobody ever lists, and 404s rather than failing loudly — the list falls\nback to the mark it drew before.","operationId":"get_document_thumbnail_api_transcribe_documents__doc_id__thumbnail_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/documents/{doc_id}/refresh":{"post":{"tags":["transcribe"],"summary":"Refresh Document","description":"Pull results from HandwritingOCR instead of waiting for the webhook.\n\nThe callback needs a public HTTPS origin (public_base_url), which the POC\nbox doesn't have yet, so this is how a document actually finishes today.\nIt also recovers any run abandoned by an API restart.","operationId":"refresh_document_api_transcribe_documents__doc_id__refresh_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/documents/{doc_id}/pages":{"get":{"tags":["transcribe"],"summary":"List Pages","operationId":"list_pages_api_transcribe_documents__doc_id__pages_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/pages/{page_id}":{"patch":{"tags":["transcribe"],"summary":"Update Page","description":"Save the user's edits and/or the reviewed flag for one page.","operationId":"update_page_api_transcribe_pages__page_id__patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"page_id","in":"path","required":true,"schema":{"type":"integer","title":"Page Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/webhooks/ocr":{"post":{"tags":["transcribe"],"summary":"Ocr Webhook","operationId":"ocr_webhook_api_transcribe_webhooks_ocr_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/transcribe/glossaries":{"get":{"tags":["glossary"],"summary":"List Glossaries","description":"Every glossary this member owns, alphabetically, with its size.","operationId":"list_glossaries_api_transcribe_glossaries_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]},"post":{"tags":["glossary"],"summary":"Create Glossary","operationId":"create_glossary_api_transcribe_glossaries_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlossaryIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/transcribe/glossaries/{gid}":{"get":{"tags":["glossary"],"summary":"Get Glossary","operationId":"get_glossary_api_transcribe_glossaries__gid__get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"gid","in":"path","required":true,"schema":{"type":"integer","title":"Gid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["glossary"],"summary":"Update Glossary","operationId":"update_glossary_api_transcribe_glossaries__gid__patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"gid","in":"path","required":true,"schema":{"type":"integer","title":"Gid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlossaryPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["glossary"],"summary":"Delete Glossary","description":"Deleting takes the terms with it, and detaches it from every document —\nboth by cascade, so a document never keeps a dangling attachment.","operationId":"delete_glossary_api_transcribe_glossaries__gid__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"gid","in":"path","required":true,"schema":{"type":"integer","title":"Gid"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/glossaries/{gid}/entries":{"get":{"tags":["glossary"],"summary":"List Entries","operationId":"list_entries_api_transcribe_glossaries__gid__entries_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"gid","in":"path","required":true,"schema":{"type":"integer","title":"Gid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["glossary"],"summary":"Create Entry","operationId":"create_entry_api_transcribe_glossaries__gid__entries_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"gid","in":"path","required":true,"schema":{"type":"integer","title":"Gid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntryIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/glossaries/{gid}/entries/bulk":{"post":{"tags":["glossary"],"summary":"Create Entries","description":"Import a list of terms in one transaction.\n\nThe whole file arrives at once rather than a request per row: a 400-term\nglossary imported one POST at a time is 400 round trips, and a browser tab\nclosed halfway through leaves half a glossary behind.","operationId":"create_entries_api_transcribe_glossaries__gid__entries_bulk_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"gid","in":"path","required":true,"schema":{"type":"integer","title":"Gid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntriesIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/glossary-entries/{eid}":{"patch":{"tags":["glossary"],"summary":"Update Entry","operationId":"update_entry_api_transcribe_glossary_entries__eid__patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"eid","in":"path","required":true,"schema":{"type":"integer","title":"Eid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntryPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["glossary"],"summary":"Delete Entry","operationId":"delete_entry_api_transcribe_glossary_entries__eid__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"eid","in":"path","required":true,"schema":{"type":"integer","title":"Eid"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/documents/{doc_id}/glossaries":{"get":{"tags":["glossary"],"summary":"List Document Glossaries","operationId":"list_document_glossaries_api_transcribe_documents__doc_id__glossaries_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["glossary"],"summary":"Set Document Glossaries","description":"Replace the attached set wholesale. Sending the desired list rather than\nadd/remove calls means a fast double-click cannot leave the document in a\nstate neither click asked for.","operationId":"set_document_glossaries_api_transcribe_documents__doc_id__glossaries_put","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlossaryIds"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/documents/{doc_id}/glossary-entries":{"get":{"tags":["glossary"],"summary":"List Document Entries","description":"Every term from the glossaries attached to this document, flat. This is\nthe one request the editor makes to build its highlights.","operationId":"list_document_entries_api_transcribe_documents__doc_id__glossary_entries_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/documents/{doc_id}/extract":{"post":{"tags":["transcribe"],"summary":"Start Extraction","description":"Kick off (or re-run) extraction. Returns immediately; poll the GET.","operationId":"start_extraction_api_transcribe_documents__doc_id__extract_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/documents/{doc_id}/extraction":{"get":{"tags":["transcribe"],"summary":"Get Extraction","description":"Everyone found in this document, plus the relationships and the\nsame-person suggestions. The panel polls this while status is 'running'.","operationId":"get_extraction_api_transcribe_documents__doc_id__extraction_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/extracted-persons/{person_id}":{"patch":{"tags":["transcribe"],"summary":"Patch Person","description":"Fix a misread name or date before it reaches the tree.","operationId":"patch_person_api_transcribe_extracted_persons__person_id__patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"person_id","in":"path","required":true,"schema":{"type":"integer","title":"Person Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PersonPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["transcribe"],"summary":"Delete Person","description":"Drop someone the model invented, or a duplicate the user doesn't want.","operationId":"delete_person_api_transcribe_extracted_persons__person_id__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"person_id","in":"path","required":true,"schema":{"type":"integer","title":"Person Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/documents/{doc_id}/tree-matches/{tree_id}":{"get":{"tags":["transcribe"],"summary":"Tree Matches","description":"For each extracted person, the people already in this tree who look like\nthem — so an import attaches to an existing person instead of duplicating.","operationId":"tree_matches_api_transcribe_documents__doc_id__tree_matches__tree_id__get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}},{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/documents/{doc_id}/in-tree/{tree_id}":{"get":{"tags":["transcribe"],"summary":"In Tree","description":"Which of this document's people an earlier import already put in the tree:\n{extracted id: tree person id}. The same answer to_tree acts on, so what the\npanel marks as \"in your tree\" is exactly what an import would update rather\nthan add.","operationId":"in_tree_api_transcribe_documents__doc_id__in_tree__tree_id__get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}},{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transcribe/documents/{doc_id}/to-tree":{"post":{"tags":["transcribe"],"summary":"To Tree","description":"Merge the selected people and relationships into a family tree.\n\nAdditive by design: existing people keep every value they already have, and\nan existing relationship is never rewritten. Re-importing the same document\nupdates the people it created rather than duplicating them, because their\ntree ids are derived from the extracted row ids.","operationId":"to_tree_api_transcribe_documents__doc_id__to_tree_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"doc_id","in":"path","required":true,"schema":{"type":"integer","title":"Doc Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToTreeBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/interest":{"get":{"tags":["billing"],"summary":"List Interest","description":"Admin view: the demand signal, newest first, plus a per-plan tally.","operationId":"list_interest_api_billing_interest_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]},"post":{"tags":["billing"],"summary":"Record Interest","description":"Record that someone wants a plan. Returns {ok: true} — nothing is charged.","operationId":"record_interest_api_billing_interest_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Interest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/billing/config":{"get":{"tags":["billing"],"summary":"Billing Config","description":"Whether checkout exists at all. Read by the pricing page on load.\n\nPublic: it says nothing an anonymous visitor could not infer from whether\nthe buttons work, and the pricing page is shown before sign-in.","operationId":"billing_config_api_billing_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/billing/subscription":{"get":{"tags":["billing"],"summary":"My Subscription","description":"This account's plan, in the terms the account page shows it.","operationId":"my_subscription_api_billing_subscription_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/billing/checkout":{"post":{"tags":["billing"],"summary":"Start Checkout","description":"Open a Stripe Checkout session and hand back its URL.\n\nSign-in is required, and not only for the obvious reason: a subscription has\nto attach to an account, and letting an anonymous visitor pay first would\ncreate a payment with nowhere to put it.","operationId":"start_checkout_api_billing_checkout_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/billing/portal":{"post":{"tags":["billing"],"summary":"Open Portal","description":"A link into Stripe's billing portal: change card, change plan, cancel.\n\nEverything Stripe already does well and this app should not reimplement.","operationId":"open_portal_api_billing_portal_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/billing/stripe/webhook":{"post":{"tags":["billing"],"summary":"Stripe Webhook","description":"Stripe's callback. The only path in the app that can grant a paid plan.\n\nOpens its own session rather than taking the dependency: the body has to be\nread as raw bytes for the signature check, and this must answer Stripe\nquickly whatever the rest of the request pipeline is doing.\n\nAlways answers 200 once the signature is good, even if the handler failed.\nA 500 makes Stripe retry the same event for three days; where a retry could\nhelp, the failure is logged loudly and the event id is left unrecorded so\nthe retry is actually reprocessed.","operationId":"stripe_webhook_api_billing_stripe_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/media/albums":{"get":{"tags":["media"],"summary":"List Albums","description":"Albums with their item count and up to three recent photographs each.\n\nThree rather than one: a shelf of single covers says nothing about what is\ninside an album, and a card that shows a little of its contents is read as\na pile of photographs rather than as a folder icon. Only images are\neligible — a PDF has no picture to show.","operationId":"list_albums_api_media_albums_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]},"post":{"tags":["media"],"summary":"Create Album","operationId":"create_album_api_media_albums_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/media/albums/{album_id}":{"patch":{"tags":["media"],"summary":"Rename Album","operationId":"rename_album_api_media_albums__album_id__patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"album_id","in":"path","required":true,"schema":{"type":"integer","title":"Album Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlbumBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["media"],"summary":"Delete Album","description":"Delete the album only. Its photos fall back to unfiled (ON DELETE SET NULL).","operationId":"delete_album_api_media_albums__album_id__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"album_id","in":"path","required":true,"schema":{"type":"integer","title":"Album Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/media":{"get":{"tags":["media"],"summary":"List Media","description":"The owner's library, optionally narrowed to one album (or the unfiled\nones). Each item carries the people it is attached to, so the grid can show\nthat without a request per tile.","operationId":"list_media_api_media_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"album_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Album Id"}},{"name":"unfiled","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Unfiled"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/media/upload":{"post":{"tags":["media"],"summary":"Upload Media","description":"Store one file in the owner's library.","operationId":"upload_media_api_media_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_media_api_media_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/media/{media_id}":{"patch":{"tags":["media"],"summary":"Update Media","operationId":"update_media_api_media__media_id__patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"media_id","in":"path","required":true,"schema":{"type":"integer","title":"Media Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ItemBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["media"],"summary":"Delete Media","description":"Drop the row, its links (ON DELETE CASCADE) and the file on disk.\n\nAlso takes the photo off any person still wearing it as a profile picture.\nThat used to be missed, and it showed: deleting a photo left every person\nit had been made the portrait of pointing at a file that no longer existed,\nso their tree card and person page rendered a broken image with no way for\nthe owner to work out which photo had done it.","operationId":"delete_media_api_media__media_id__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"media_id","in":"path","required":true,"schema":{"type":"integer","title":"Media Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/media/{media_id}/links":{"post":{"tags":["media"],"summary":"Link Media","description":"Attach one of your photos to a person in one of your trees, optionally\ntagging where in the frame they are. Both sides must be yours — a photo\ncan't be pinned onto someone else's tree.\n\nset_portrait in the reply tells the caller this person had no photo and no\npicture of their own before now, so this one should become their profile\npicture (the caller then POSTs the cropped face to /portrait).","operationId":"link_media_api_media__media_id__links_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"media_id","in":"path","required":true,"schema":{"type":"integer","title":"Media Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/media/{media_id}/portrait":{"post":{"tags":["media"],"summary":"Set Portrait","description":"Make this photo the person's profile picture. The caller may send a\ncropped face (the tag box, squared off) as `file`; without one the whole\nphoto is used. The avatar is a plain URL on the person's tree data, which\nis what the person page and the chart cards already read.","operationId":"set_portrait_api_media__media_id__portrait_post","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"media_id","in":"path","required":true,"schema":{"type":"integer","title":"Media Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_set_portrait_api_media__media_id__portrait_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/media/{media_id}/links/{tree_id}/{person_id}":{"delete":{"tags":["media"],"summary":"Unlink Media","operationId":"unlink_media_api_media__media_id__links__tree_id___person_id__delete","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"media_id","in":"path","required":true,"schema":{"type":"integer","title":"Media Id"}},{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/media/persons/{tree_id}/{person_id}":{"get":{"tags":["media"],"summary":"Person Media","description":"Photos attached to a person. Owner always; anyone else only when the\ntree is public — the same rule the person's citations follow.","operationId":"person_media_api_media_persons__tree_id___person_id__get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"tree_id","in":"path","required":true,"schema":{"type":"integer","title":"Tree Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/profile/avatar":{"post":{"tags":["profile"],"summary":"Upload Avatar","description":"Set the signed-in member's profile picture.\n\nReturns as soon as the bytes are on disk and the row is written. The\npicture is live at that moment — full size, which is why the job exists.","operationId":"upload_avatar_api_profile_avatar_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_avatar_api_profile_avatar_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]},"delete":{"tags":["profile"],"summary":"Delete Avatar","description":"Remove the picture. Falls back to the initial, as an account with no\npicture always has.","operationId":"delete_avatar_api_profile_avatar_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/profile/storage":{"get":{"tags":["profile"],"summary":"My Storage","description":"The signed-in member's own usage.","operationId":"my_storage_api_profile_storage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/support":{"post":{"tags":["support"],"summary":"Send Support Request","operationId":"send_support_request_api_support_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_send_support_request_api_support_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyCookie":[]}]}},"/api/data-deletion/request":{"post":{"tags":["deletion"],"summary":"Request Deletion","description":"Take the form and mail the address a confirmation link.\n\nAnswers the same way whether or not an account exists: the form is public,\nand telling a stranger which addresses are registered here would make it a\nmembership oracle. The person is told to expect the email either way, which\nis true either way — the mail goes to the address, not to the account.","operationId":"request_deletion_api_data_deletion_request_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/data-deletion/confirm":{"post":{"tags":["deletion"],"summary":"Confirm Deletion","description":"Click target of the confirmation email. This is what reaches the inbox.\n\nA second click reads as \"already confirmed\" rather than a broken link, and\nsends nothing twice: the row is only forwarded on the transition.","operationId":"confirm_deletion_api_data_deletion_confirm_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/deletions":{"get":{"tags":["deletion"],"summary":"List Requests","description":"Every request, newest first, each one answering the two questions that\ndecide what to do with it: is there an account under this address, and did\nthe address itself confirm the request?\n\nThe account is looked up live rather than stored, because it is a fact\nabout now — the point of the row is that it may still need acting on, and\na hit recorded last week would only say what was true then.","operationId":"list_requests_api_admin_deletions_get","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/deletions/{rid}":{"patch":{"tags":["deletion"],"summary":"Set Status","description":"Record that a confirmed request has been dealt with, or put it back.\n\n'pending' is never set from here — it means \"the address has not answered\",\nwhich is not something an administrator can decide.","operationId":"set_status_api_admin_deletions__rid__patch","security":[{"APIKeyCookie":[]}],"parameters":[{"name":"rid","in":"path","required":true,"schema":{"type":"integer","title":"Rid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api":{"get":{"tags":["meta"],"summary":"Service index","description":"The one URL an agent needs to find everything else.\n\nLists the public endpoints and the machine-readable descriptions of the\nsite, so a client that lands on /api can reach the OpenAPI document, the\ndocumentation and llms.txt without guessing at paths.","operationId":"api_index_api_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/health":{"get":{"tags":["meta"],"summary":"Health","operationId":"health_api_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/config":{"get":{"tags":["meta"],"summary":"Config","description":"Public feature flags the frontend needs at load (e.g. which auth\nproviders are available).","operationId":"config_api_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AcceptBody":{"properties":{"take":{"items":{"type":"string"},"type":"array","title":"Take","default":[]},"add_event":{"type":"boolean","title":"Add Event","default":false}},"type":"object","title":"AcceptBody","description":"What the reader chose in the comparison.\n\n`take` is the list of field keys to copy from the record. Absent means take\nnothing — accepting still attaches the source, which is the part that is\nnever optional. `add_event` puts the record's own event on the timeline."},"AdminUserPatch":{"properties":{"display_name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Display Name"},"email":{"anyOf":[{"type":"string","maxLength":320,"minLength":3},{"type":"null"}],"title":"Email"},"lang":{"anyOf":[{"type":"string","pattern":"^(en|es)$"},{"type":"null"}],"title":"Lang"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"is_superuser":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Superuser"},"is_verified":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Verified"}},"type":"object","title":"AdminUserPatch"},"AlbumBody":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","title":"AlbumBody"},"AllowancePatch":{"properties":{"plan":{"anyOf":[{"type":"string","pattern":"^(free|hobbyist|researcher|archive)$"},{"type":"null"}],"title":"Plan"},"bonus_pages":{"anyOf":[{"type":"integer","maximum":100000.0,"minimum":0.0},{"type":"null"}],"title":"Bonus Pages"},"unlimited":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Unlimited"},"reset_period":{"type":"boolean","title":"Reset Period","default":false}},"type":"object","title":"AllowancePatch","description":"The only way an account gets more pages while billing does not exist."},"Body_auth_cookie_db_login_api_auth_login_post":{"properties":{"grant_type":{"anyOf":[{"type":"string","pattern":"password"},{"type":"null"}],"title":"Grant Type"},"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"},"scope":{"type":"string","title":"Scope","default":""},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"client_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Secret"}},"type":"object","required":["username","password"],"title":"Body_auth_cookie_db_login_api_auth_login_post"},"Body_data_deletion_api_auth_facebook_data_deletion_post":{"properties":{"signed_request":{"type":"string","title":"Signed Request"}},"type":"object","required":["signed_request"],"title":"Body_data_deletion_api_auth_facebook_data_deletion_post"},"Body_deauthorize_api_auth_facebook_deauthorize_post":{"properties":{"signed_request":{"type":"string","title":"Signed Request"}},"type":"object","required":["signed_request"],"title":"Body_deauthorize_api_auth_facebook_deauthorize_post"},"Body_reset_forgot_password_api_auth_forgot_password_post":{"properties":{"email":{"type":"string","format":"email","title":"Email"}},"type":"object","required":["email"],"title":"Body_reset_forgot_password_api_auth_forgot_password_post"},"Body_reset_reset_password_api_auth_reset_password_post":{"properties":{"token":{"type":"string","title":"Token"},"password":{"type":"string","title":"Password"}},"type":"object","required":["token","password"],"title":"Body_reset_reset_password_api_auth_reset_password_post"},"Body_send_support_request_api_support_post":{"properties":{"message":{"type":"string","title":"Message"},"files":{"items":{"type":"string","format":"binary"},"type":"array","title":"Files","default":[]}},"type":"object","required":["message"],"title":"Body_send_support_request_api_support_post"},"Body_set_portrait_api_media__media_id__portrait_post":{"properties":{"tree_id":{"type":"integer","title":"Tree Id"},"person_id":{"type":"string","title":"Person Id"},"file":{"anyOf":[{"type":"string","format":"binary"},{"type":"null"}],"title":"File"}},"type":"object","required":["tree_id","person_id"],"title":"Body_set_portrait_api_media__media_id__portrait_post"},"Body_upload_api_transcribe_documents_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_api_transcribe_documents_post"},"Body_upload_avatar_api_profile_avatar_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_avatar_api_profile_avatar_post"},"Body_upload_document_api_trees__tree_id__uploads_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_document_api_trees__tree_id__uploads_post"},"Body_upload_gedcom_api_trees_import_upload_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"title":{"type":"string","title":"Title","default":""},"visibility":{"type":"string","title":"Visibility","default":""}},"type":"object","required":["file"],"title":"Body_upload_gedcom_api_trees_import_upload_post"},"Body_upload_media_api_media_upload_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"album_id":{"type":"string","title":"Album Id","default":""},"title":{"type":"string","title":"Title","default":""}},"type":"object","required":["file"],"title":"Body_upload_media_api_media_upload_post"},"Body_verify_request_token_api_auth_request_verify_token_post":{"properties":{"email":{"type":"string","format":"email","title":"Email"}},"type":"object","required":["email"],"title":"Body_verify_request_token_api_auth_request_verify_token_post"},"Body_verify_verify_api_auth_verify_post":{"properties":{"token":{"type":"string","title":"Token"}},"type":"object","required":["token"],"title":"Body_verify_verify_api_auth_verify_post"},"Box":{"properties":{"x":{"type":"number","title":"X"},"y":{"type":"number","title":"Y"},"w":{"type":"number","title":"W"},"h":{"type":"number","title":"H"}},"type":"object","required":["x","y","w","h"],"title":"Box","description":"A tag box on the photo, as fractions of the image (0..1)."},"BulkUsers":{"properties":{"action":{"type":"string","enum":["verification_email","password_reset","sign_out","disable","enable"],"title":"Action"},"ids":{"items":{"type":"integer"},"type":"array","maxItems":1000,"minItems":1,"title":"Ids"}},"type":"object","required":["action","ids"],"title":"BulkUsers"},"CampaignIn":{"properties":{"list_ids":{"items":{"type":"integer"},"type":"array","title":"List Ids"},"subject":{"type":"string","title":"Subject"},"html":{"type":"string","title":"Html"},"preheader":{"type":"string","title":"Preheader","default":""},"confirm":{"type":"string","title":"Confirm"}},"type":"object","required":["list_ids","subject","html","confirm"],"title":"CampaignIn"},"CheckoutRequest":{"properties":{"plan":{"type":"string","title":"Plan"},"period":{"type":"string","title":"Period","default":"monthly"},"lang":{"type":"string","title":"Lang","default":"en"},"blocks":{"type":"integer","title":"Blocks","default":1}},"type":"object","required":["plan"],"title":"CheckoutRequest"},"CommentEdit":{"properties":{"body":{"type":"string","maxLength":5000,"minLength":1,"title":"Body"}},"type":"object","required":["body"],"title":"CommentEdit"},"CommentIn":{"properties":{"page_key":{"type":"string","maxLength":300,"minLength":1,"title":"Page Key"},"parent_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parent Id"},"body":{"type":"string","maxLength":5000,"minLength":1,"title":"Body"}},"type":"object","required":["page_key","body"],"title":"CommentIn"},"ComposeIn":{"properties":{"to_user_id":{"type":"integer","title":"To User Id"},"body":{"type":"string","maxLength":4000,"minLength":1,"title":"Body"}},"type":"object","required":["to_user_id","body"],"title":"ComposeIn"},"ConfirmIn":{"properties":{"token":{"type":"string","title":"Token"}},"type":"object","required":["token"],"title":"ConfirmIn"},"CopyAttach":{"properties":{"to":{"type":"string","title":"To"},"relation":{"type":"string","title":"Relation"}},"type":"object","required":["to","relation"],"title":"CopyAttach"},"CopyBody":{"properties":{"source_tree":{"type":"integer","title":"Source Tree"},"source_person":{"type":"string","title":"Source Person"},"merge_into":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Merge Into"},"take":{"items":{"type":"string"},"type":"array","title":"Take","default":[]},"attach":{"anyOf":[{"$ref":"#/components/schemas/CopyAttach"},{"type":"null"}]},"relatives":{"items":{"$ref":"#/components/schemas/CopyRelative"},"type":"array","title":"Relatives","default":[]}},"type":"object","required":["source_tree","source_person"],"title":"CopyBody"},"CopyRelative":{"properties":{"source_id":{"type":"string","title":"Source Id"},"match":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Match"}},"type":"object","required":["source_id"],"title":"CopyRelative"},"DeleteUser":{"properties":{"confirm_email":{"type":"string","title":"Confirm Email"}},"type":"object","required":["confirm_email"],"title":"DeleteUser"},"DocumentMeta":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary"},"time_period":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Time Period"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"}},"type":"object","title":"DocumentMeta","description":"The catalogue card. Every field is optional: the editor saves one at a\ntime, and an omitted key leaves that field alone."},"EntriesIn":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/EntryIn"},"type":"array","title":"Entries"}},"type":"object","required":["entries"],"title":"EntriesIn","description":"A whole CSV in one request. Duplicates within the file, and terms the\nglossary already carries, are skipped rather than rejected — importing the\nsame list twice should be a no-op, not an error."},"EntryIn":{"properties":{"term":{"type":"string","maxLength":255,"minLength":1,"title":"Term"},"definition":{"type":"string","minLength":1,"title":"Definition"}},"type":"object","required":["term","definition"],"title":"EntryIn"},"EntryPatch":{"properties":{"term":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Term"},"definition":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Definition"}},"type":"object","title":"EntryPatch"},"ErrorModel":{"properties":{"detail":{"anyOf":[{"type":"string"},{"additionalProperties":{"type":"string"},"type":"object"}],"title":"Detail"}},"type":"object","required":["detail"],"title":"ErrorModel"},"FavoriteIn":{"properties":{"record_id":{"type":"integer","title":"Record Id"},"person_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Person Id"}},"type":"object","required":["record_id"],"title":"FavoriteIn"},"FolderIn":{"properties":{"folder":{"type":"string","pattern":"^(inbox|archived)$","title":"Folder"}},"type":"object","required":["folder"],"title":"FolderIn"},"GlossaryIds":{"properties":{"glossary_ids":{"items":{"type":"integer"},"type":"array","title":"Glossary Ids"}},"type":"object","required":["glossary_ids"],"title":"GlossaryIds"},"GlossaryIn":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","required":["name"],"title":"GlossaryIn"},"GlossaryPatch":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","title":"GlossaryPatch"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Interest":{"properties":{"plan":{"type":"string","title":"Plan"},"period":{"type":"string","title":"Period","default":"monthly"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"lang":{"type":"string","title":"Lang","default":"en"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"}},"type":"object","required":["plan"],"title":"Interest"},"ItemBody":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"caption":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Caption"},"taken_on":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Taken On"},"album_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Album Id"},"move":{"type":"boolean","title":"Move","default":false}},"type":"object","title":"ItemBody"},"LinkBody":{"properties":{"tree_id":{"type":"integer","title":"Tree Id"},"person_id":{"type":"string","title":"Person Id"},"box":{"anyOf":[{"$ref":"#/components/schemas/Box"},{"type":"null"}]}},"type":"object","required":["tree_id","person_id"],"title":"LinkBody"},"OAuth2AuthorizeResponse":{"properties":{"authorization_url":{"type":"string","title":"Authorization Url"}},"type":"object","required":["authorization_url"],"title":"OAuth2AuthorizeResponse"},"OcrCreditSettingsPatch":{"properties":{"min_credits":{"type":"integer","maximum":100000.0,"minimum":0.0,"title":"Min Credits"},"topup_credits":{"type":"integer","maximum":10000.0,"minimum":100.0,"title":"Topup Credits"},"auto_topup":{"type":"boolean","title":"Auto Topup","default":false},"daily_cap":{"type":"integer","maximum":100000.0,"minimum":0.0,"title":"Daily Cap"}},"type":"object","required":["min_credits","topup_credits","daily_cap"],"title":"OcrCreditSettingsPatch"},"PageUpdate":{"properties":{"edited_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Edited Text"},"is_reviewed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Reviewed"}},"type":"object","title":"PageUpdate"},"PersonPatch":{"properties":{"given_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Given Name"},"surname":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surname"},"surname2":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surname2"},"sex":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex"},"birth_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Birth Date"},"birth_place":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Birth Place"},"death_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Death Date"},"death_place":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Death Place"},"occupation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Occupation"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","title":"PersonPatch","description":"Any extracted field the user wants to correct before importing."},"ReplyIn":{"properties":{"body":{"type":"string","maxLength":4000,"minLength":1,"title":"Body"}},"type":"object","required":["body"],"title":"ReplyIn"},"ReportIn":{"properties":{"reported_user_id":{"type":"integer","title":"Reported User Id"},"conversation_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Conversation Id"},"message_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Message Id"},"reason":{"type":"string","pattern":"^(spam|harassment|scam|hate|other)$","title":"Reason"},"details":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Details"}},"type":"object","required":["reported_user_id","reason"],"title":"ReportIn"},"ReportPatch":{"properties":{"status":{"type":"string","pattern":"^(open|reviewed|resolved)$","title":"Status"},"admin_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Admin Note"}},"type":"object","required":["status"],"title":"ReportPatch"},"RequestIn":{"properties":{"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name","default":""},"note":{"type":"string","title":"Note","default":""},"lang":{"type":"string","title":"Lang","default":"en"}},"type":"object","required":["email"],"title":"RequestIn"},"StatusPatch":{"properties":{"status":{"type":"string","title":"Status"}},"type":"object","required":["status"],"title":"StatusPatch"},"SubmissionIn":{"properties":{"record_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Record Id"},"kind":{"type":"string","pattern":"^(correction|addition|question)$","title":"Kind","default":"correction"},"message":{"type":"string","maxLength":4000,"minLength":3,"title":"Message"}},"type":"object","required":["message"],"title":"SubmissionIn"},"SubmissionPatch":{"properties":{"status":{"type":"string","pattern":"^(open|reviewed|resolved)$","title":"Status"},"admin_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Admin Note"}},"type":"object","required":["status"],"title":"SubmissionPatch"},"SubscribeIn":{"properties":{"email":{"type":"string","title":"Email"},"lang":{"type":"string","title":"Lang","default":"en"}},"type":"object","required":["email"],"title":"SubscribeIn"},"ToTreeBody":{"properties":{"tree_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Tree Id"},"new_tree_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"New Tree Title"},"person_ids":{"items":{"type":"integer"},"type":"array","title":"Person Ids","default":[]},"relationship_ids":{"items":{"type":"integer"},"type":"array","title":"Relationship Ids","default":[]},"merges":{"additionalProperties":{"type":"integer"},"type":"object","title":"Merges","default":{}},"links":{"additionalProperties":{"type":"string"},"type":"object","title":"Links","default":{}}},"type":"object","title":"ToTreeBody"},"TopUp":{"properties":{"credits":{"type":"integer","maximum":10000.0,"minimum":100.0,"title":"Credits","default":100}},"type":"object","title":"TopUp"},"TranscribeSettingsPatch":{"properties":{"cleanup_model":{"type":"string","maxLength":120,"minLength":1,"title":"Cleanup Model"},"extract_model":{"type":"string","maxLength":120,"minLength":1,"title":"Extract Model"},"cleanup_prompt":{"type":"string","maxLength":20000,"minLength":100,"title":"Cleanup Prompt"}},"type":"object","required":["cleanup_model","extract_model","cleanup_prompt"],"title":"TranscribeSettingsPatch"},"TreeCitationBody":{"properties":{"fact":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fact"},"record_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Record Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"file_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"File Url"},"file_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"File Name"}},"type":"object","title":"TreeCitationBody"},"TreeCreate":{"properties":{"title":{"type":"string","title":"Title","default":"My family tree"},"visibility":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Visibility"}},"type":"object","title":"TreeCreate"},"TreeImport":{"properties":{"title":{"type":"string","title":"Title","default":"Imported tree"},"data":{"items":{},"type":"array","title":"Data","default":[]},"gedcom_raw":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gedcom Raw"}},"type":"object","title":"TreeImport"},"TreeSave":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"data":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Data"},"visibility":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Visibility"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At"}},"type":"object","title":"TreeSave"},"UserCreate":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","title":"Password"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","default":true},"is_superuser":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Superuser","default":false},"is_verified":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Verified","default":false},"display_name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Display Name"},"lang":{"type":"string","title":"Lang","default":"en"}},"type":"object","required":["email","password"],"title":"UserCreate"},"UserRead":{"properties":{"id":{"type":"integer","title":"Id"},"email":{"type":"string","format":"email","title":"Email"},"is_active":{"type":"boolean","title":"Is Active","default":true},"is_superuser":{"type":"boolean","title":"Is Superuser","default":false},"is_verified":{"type":"boolean","title":"Is Verified","default":false},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"lang":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lang"},"bio":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bio"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"oauth_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Oauth Provider"}},"type":"object","required":["id","email"],"title":"UserRead"},"UserUpdate":{"properties":{"password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Password"},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Email"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"is_superuser":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Superuser"},"is_verified":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Verified"},"display_name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Display Name"},"bio":{"anyOf":[{"type":"string","maxLength":600},{"type":"null"}],"title":"Bio"}},"type":"object","title":"UserUpdate"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VerifyBody":{"properties":{"token":{"type":"string","title":"Token"}},"type":"object","required":["token"],"title":"VerifyBody"}},"securitySchemes":{"APIKeyCookie":{"type":"apiKey","in":"cookie","name":"crpsession"}}},"tags":[{"name":"meta","description":"Service index, health, and the shape of the archive."},{"name":"search","description":"Public search over every indexed person, and the records behind the hits."},{"name":"auth","description":"Registration and sign-in."},{"name":"trees","description":"Family trees (members)."},{"name":"transcribe","description":"Document transcription (members)."}]}