# Probably Cousins -- a guide for agents Probably Cousins is a family history register. Every family gets its own private site; a family's owner can issue API keys so an agent (you) can read facts, propose corrections, run a documentation check, or download the family's tree -- always with the evidence attached, never a bare percentage passed off as certainty. ## The one rule that matters A fact without a record URL is a lead, not a fact. Every fact this API returns says what evidence it rests on -- see GET /api/v1/contract for the full evidence ladder (SEALED / DOCUMENTED / SUPPORTED / COMPILED-MULTI / COMPILED-SINGLE / CONFLICTED / LORE) and the five public tiers (Confirmed cousins / Probably cousins / Maybe cousins / Unlikely / No connection found). "No connection found" is not the same as "not related" -- it means no documented connection was found within 24 links, never a claim about biology. Living people: a key sees living relatives in full ONLY inside its own family's space, per that family's living_visibility setting -- the same switch a signed-in relative's browser respects. A key never sees another family's living people, ever. The one exception on the public site is a family's own reference person, named on /find only if that family's owner picked them and agreed to it. POST /api/v1/suggestions never patches a family's tree directly -- it files a proposed fact into that family's moderation queue, and a human decides it. Research jobs run differently: a finding with a citable record URL and a clear rung of evidence is applied to the tree automatically; anything less certain is left for the family's owner to decide, same as a suggestion. ## Getting a key A family's owner issues keys from their /admin page ("Agent access"), choosing scopes: read (facts, search, contract), suggest (propose facts), export (download the GEDCOM), load (replace the stored tree -- see "Pull, work, push" below). Send it as `Authorization: Bearer pc_live_...`. There is no self-service signup for a key -- it always comes from a human who owns a family's data. ## Pull, work, push {#pull-work-push} There is exactly ONE source of truth for a family's tree: the copy the site stores (family_trees + Storage tree.ged). A research agent that improves a tree does not edit it in place -- it pulls the current GEDCOM, works on a local copy, and pushes the result back. A push that is not based on the current version is refused, never silently overwritten. No code deploy is ever involved in updating a family's tree. ``` # 1. Pull -- note the ETag, this is the version your push must be based on curl -s -D- -H "Authorization: Bearer pc_live_..." \ https://probablycousins.com/api/v1/tree.ged -o tree.ged # ETag: "3" # 2. Work -- improve tree.ged however you like, still valid GEDCOM # 3. Push -- If-Match must equal the ETag you just pulled curl -s -X PUT -H "Authorization: Bearer pc_live_..." \ -H 'If-Match: "3"' -H "X-Tree-Note: wave 4 -- 1900 census matches" \ --data-binary @tree.ged \ https://probablycousins.com/api/v1/tree # {"version":4,"people":1712,"families":430,"living":58,"warnings":[]} # 3b. A large tree: gzip it and say so. The wire limit is 4.5 MB (the # platform's cap on a request body); the GEDCOM itself may be up to # 64 MB once inflated. GEDCOM compresses about 4.4:1, so this carries # a tree to roughly 18 MB of text. gzip -9 -c tree.ged > tree.ged.gz curl -s -X PUT -H "Authorization: Bearer pc_live_..." \ -H 'If-Match: "3"' -H "X-Tree-Note: wave 5" \ -H "Content-Encoding: gzip" --data-binary @tree.ged.gz \ https://probablycousins.com/api/v1/tree ``` A 409 means someone (a person's own upload, or another agent) changed the tree since you pulled -- the body carries `current_version`; pull again (GET /api/v1/tree.ged) and redo your work against the new copy. A 428 means you forgot If-Match entirely. The push is also refused (400) if the GEDCOM fails verification, has zero people, or has more than 2% fewer people than what's stored -- send `X-Allow-Shrink: yes` if a shrink is genuinely intended (e.g. you split off a branch on purpose). `GET /api/v1/tree` (scope read) gives the current version and counts without downloading the whole file, for checking staleness before you bother to pull. ## Endpoints (see /openapi.json for the full machine-readable spec) - GET /api/v1/contract the evidence ladder, tiers, living rule - GET /api/v1/family the key's own family - GET /api/v1/people?q=&limit=&cursor= search/list - GET /api/v1/people/{id} one person, facts with evidence - GET /api/v1/people/{id}/facts/{fact} one fact with evidence - GET /api/v1/tree.ged pull: download the GEDCOM, with its version (scope: export) - GET /api/v1/tree status: version, counts, staleness (scope: read) - PUT /api/v1/tree push: replace the stored tree, If-Match required (scope: load) - POST /api/v1/audit GEDCOM in, documentation report out (any key) - POST /api/v1/suggestions propose a fact with evidence (scope: suggest) - GET /api/v1/suggestions/{id} status of a suggestion - GET /api/v1/find?anchor=&figure= path, score, tier between two people in the family - GET /api/v1/find?anchor_text= free-text anchor search across every opted-in family + the research catalog (not scoped to the key's family) -- returns candidates if the text is ambiguous, otherwise scored results - GET /api/v1/find?anchor= follow-up for an ambiguous anchor_text: pass one candidate id, no figure -- scored results in the same cross-family graph (add anchor_text_hint= to keep the exact match confidence) - GET /api/v1/next-steps?person=&surface=&anchor=&max= priced "What next?" menu of concrete research moves, generated from this family's own tree's evidence gaps (scope: read) MCP: POST /api/mcp (Streamable HTTP, same bearer key). Discovery doc: GET /.well-known/mcp.json. Tools: get_contract, search_people, get_person, get_fact, find_connection, next_steps, check_tree, suggest_fact, export_gedcom, get_tree_status, push_tree -- each tool's own description repeats the evidence rule above; export_gedcom/get_tree_status/push_tree carry the same version used by the pull-work-push cycle above. ## What next? {#next-steps} Most people cannot write a research prompt. GET /api/v1/next-steps (and the next_steps MCP tool) gives you the same short, priced menu of concrete moves the site itself shows -- generated from the tree's own evidence gaps, never a bare "describe what to look into" box. Each step names a kind (find_record_for_link, confirm_vital, resolve_conflict, find_parents, verify_story, or the free match_other_families), a subject, a one-sentence reason, and a provisional price -- hold one the same way you would hold any other job, by posting its id back to the family's own site (POST /api/jobs/hold is a signed-in family route, not part of this key API); the price is always re-validated server-side before anything is charged. ## Errors Every error is { "error": { "code", "message", "docs" } }. 401 means a missing or bad key; 403 means a valid key without the scope a route needs; 404 never distinguishes "doesn't exist" from "not yours to see" -- a key never learns what exists outside its own family. Each key is limited to 600 requests per rolling minute; a 429 rate_limited means back off and retry shortly. This page is the machine-readable guide; the human-readable version lives in the repo at docs/API.md (see /openapi.json for the full request/response shapes).