{"openapi":"3.1.0","info":{"title":"Pressfront Trace API","version":"1.5.0","description":"Read your Trace results from your own tools or your AI: who came at a name, what they said, when, who carried it, and who moved together; and for a story going around, who said it first, who spread it, and whether they showed proof (POST /understand, POST /traces with text or claim, GET /traces/{id}/case). Keys come with Trace ($149 a month); make one in Settings. Try it first with the demo key pf_live_DemoKeyForPressfrontTraceSample1 (read-only: one invented trace and 3 real sample traces, GET /samples). Stance and tone are read by a language model and labeled as such. Docs: https://pressfront.co/developers","contact":{"email":"support@pressfront.co","url":"https://pressfront.co/developers"}},"servers":[{"url":"https://pressfront.co/api/v1"}],"security":[{"bearer":[]}],"tags":[{"name":"Samples"},{"name":"Account"},{"name":"Instant"},{"name":"Situations"},{"name":"Traces"},{"name":"Read a trace"},{"name":"Accounts"},{"name":"Alerts"}],"paths":{"/samples":{"get":{"operationId":"list_samples","summary":"Three real finished traces any key can read","description":"Real output in 30 seconds: a story going around (who said it first, how it spread), a very big name (every account, its side and its posts) and an instant check, each with links to its /case and /brief. Any key reads them, the public demo key pf_live_DemoKeyForPressfrontTraceSample1 included. Reads only: never a search or a new trace.","tags":["Samples"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Samples"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/me":{"get":{"operationId":"whoami","summary":"Who this key belongs to","description":"The plan, how many names are watched out of how many, and calls made today.","tags":["Account"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/instant/{handle}":{"get":{"operationId":"instant_check","summary":"Who is posting about a name right now: a second if traced today, about a minute if not","description":"How many accounts, on which side, a quote from each side, the biggest accounts in it with their side and their post, how much was read (by people and by followers, and the biggest not read), whether the critical share is usual (next to this name's earlier traces and the other names in our record), how it started, who carried it, what the critical posts claim, and the pace, plus the whole brief as Markdown. Traced in the last 24 hours = answers in about a second. Not traced = Pressfront searches it now (about a minute) and holds the answer up to `wait` seconds; if it is not done yet you get 202 status \"searching\" with secondsLeft, and calling again picks up the same search. Never an empty answer; a 202 is not an answer and costs nothing. `depth` says quick (the minute search) or full.","tags":["Instant"],"parameters":[{"name":"handle","in":"path","required":true,"description":"An X @handle or a Bluesky handle like name.bsky.social.","schema":{"type":"string"}},{"name":"maxAgeHours","in":"query","required":false,"description":"How old the newest trace may be, 1 to 72 hours (default 24).","schema":{"type":"integer"}},{"name":"wait","in":"query","required":false,"description":"When it has to search: seconds to hold the answer, 0 to 55 (default 50).","schema":{"type":"integer"}},{"name":"search","in":"query","required":false,"description":"false = only look for a recent trace; 404 instead of searching.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/understand":{"post":{"operationId":"understand_situation","summary":"Say back what Pressfront would look into","description":"Tell it what is going on in plain words (a paragraph, a pasted link). It answers with the one lookup that fits: the person being talked about (an @handle) or the story going around, as the few words people would post, plus 2 or 3 plain lines of what we would look into, the posts it read, other stories in what you sent, and how long you say it has gone on. Read by a language model; it never decides who is right or whether a story is true. Nothing is stored and nothing starts: pass the same text (or the claim it returned) to trace_situation. Screenshots and PDFs (files) work over REST only: POST /v1/understand with files. 422 unclear when it cannot tell what to look up. 300 a day.","tags":["Situations"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnderstandAnswer"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"413":{"$ref":"#/components/responses/Error"},"422":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"503":{"$ref":"#/components/responses/Error"}},"x-scope":"read","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"What is going on, in plain words, up to 6,000 characters. Over REST, text or files (or both)."},"files":{"type":"array","maxItems":5,"items":{"type":"object","required":["mediaType","data"],"properties":{"mediaType":{"type":"string","enum":["image/png","image/jpeg","image/webp","image/gif","application/pdf"]},"data":{"type":"string","description":"base64"}}},"description":"REST only (not over MCP): up to 5 screenshots or PDFs, each {mediaType: image/png | image/jpeg | image/webp | image/gif | application/pdf, data: base64 without the data: prefix}; 5.6M base64 characters each, 13M in all. Read, never stored."}},"required":[]}}}}}},"/traces":{"get":{"operationId":"list_traces","summary":"Your traces, newest first","description":"Every trace on your account, from the site or the API. Page with `before`.","tags":["Traces"],"parameters":[{"name":"limit","in":"query","required":false,"description":"How many to return, 1 to 50 (default 20).","schema":{"type":"integer"}},{"name":"status","in":"query","required":false,"description":"Only traces in this state.","schema":{"type":"string","enum":["queued","running","done","thin","failed"]}},{"name":"before","in":"query","required":false,"description":"The `next` value from the previous page (a time).","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"},"post":{"operationId":"start_trace","summary":"Start a trace of a name","description":"Traces an X @handle, an x.com link, or a Bluesky handle. Takes a few minutes; poll get_trace or use the trace.done webhook. The same name within an hour returns the trace you already have. 20 a day. For a story going around, or a situation in plain words, use trace_situation (the same POST /traces with text or claim). Also (trace_situation): Start a trace of a situation or a story going around. Send text (what is going on, in plain words, like you would tell a friend) or claim (the story the way people post it, like \"Lara went partying after the concert was cancelled\"). A story starts a rumor trace: who said it first, who spread it, and whether they showed a photo, video or link. A situation about one @handle starts a name trace, the same as start_trace. The answer includes `understood`, what we read your text as. Takes a few minutes; then get_case for the answer. How long they say it has gone on (\"for 10 months\") sets how far back it looks. The same story within an hour returns the trace you already have. Shares start_trace's 20 a day. Screenshots (files) work over REST only.","tags":["Traces"],"parameters":[],"responses":{"202":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TraceStarted"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"413":{"$ref":"#/components/responses/Error"},"422":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"503":{"$ref":"#/components/responses/Error"}},"x-scope":"full","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"An X @handle, an x.com link, or a Bluesky handle like name.bsky.social."},"text":{"type":"string","description":"What is going on, in plain words (up to 6,000 characters). Pasted post links help."},"claim":{"type":"string","description":"Or: the story itself, the few words people post about it. Starts a rumor trace directly."},"files":{"type":"array","maxItems":5,"items":{"type":"object","required":["mediaType","data"],"properties":{"mediaType":{"type":"string","enum":["image/png","image/jpeg","image/webp","image/gif","application/pdf"]},"data":{"type":"string","description":"base64"}}},"description":"REST only (not over MCP): up to 5 screenshots or PDFs, each {mediaType: image/png | image/jpeg | image/webp | image/gif | application/pdf, data: base64 without the data: prefix}; 5.6M base64 characters each, 13M in all. Read, never stored."}},"required":[],"minProperties":1}}}},"x-mcp-tools":["start_trace","trace_situation"]}},"/traces/{id}":{"get":{"operationId":"get_trace","summary":"One trace: status and headline numbers","description":"Where it is (queued, running, done), how long is left, what it has found so far while it runs, and when done its headline numbers and links to everything else. Pass wait=25 to hold the answer until it finishes (repeat until done).","tags":["Traces"],"parameters":[{"name":"id","in":"path","required":true,"description":"The trace id (26 characters, from POST /traces or GET /traces).","schema":{"type":"string"}},{"name":"wait","in":"query","required":false,"description":"Seconds to wait for it to finish, 1 to 25, before answering.","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/traces/{id}/brief":{"get":{"operationId":"get_trace_brief","summary":"The whole situation as plain facts, for an AI to explain","description":"Start here. Everything that happened, in order, as Markdown: the headline counts, a quote from each side, how it started, who carried it, what the critical posts claim, who moved together, safety flags, where it is now. Our numbers and their words; no model of ours writes it, so the AI reading it does the explaining. Works for name traces and story (rumor) traces.","tags":["Read a trace"],"parameters":[{"name":"id","in":"path","required":true,"description":"The trace id (26 characters, from POST /traces or GET /traces).","schema":{"type":"string"}},{"name":"format","in":"query","required":false,"description":"md (default) = Markdown text; json = {brief}.","schema":{"type":"string","enum":["md","json"]}},{"name":"partial","in":"query","required":false,"description":"true = while the trace still runs, read what it has found so far (first results inside about a minute).","schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK","content":{"text/markdown":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"object","properties":{"brief":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/traces/{id}/case":{"get":{"operationId":"get_case","summary":"The answer in four stops: who started it, how it spread, who they are, whether it happened before","description":"The answer card on top of every finished trace, as data, for a story (rumor) trace or a name trace. One headline built by fixed rules from our counts (no model writes it; headlineParts marks its key words), then four stops in order: Started (the earliest public post we could find, whether it had a photo or link, whether it is now gone), Spread (how many accounts; for a story how many repeated it, asked if it was real, or said it was not), Who (words they use in their own names and bios), Before? (how many were in earlier waves). A stop we could not fill says so and has known false. Plus huh (one standout fact, when there is one), saidBack (what the person asked about), and data (the numbers under it). Who said it first, who spread it, whether they showed proof; never who is right.","tags":["Read a trace"],"parameters":[{"name":"id","in":"path","required":true,"description":"The trace id (26 characters, from POST /traces or GET /traces).","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Case"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"422":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/traces/{id}/story":{"get":{"operationId":"get_trace_story","summary":"What happened, as data","description":"The What happened tab: headline, counts by side and tone, the biggest voice on each side, how it started, the jump, who carried it, claims, concentration, pace, and every measured fact.","tags":["Read a trace"],"parameters":[{"name":"id","in":"path","required":true,"description":"The trace id (26 characters, from POST /traces or GET /traces).","schema":{"type":"string"}},{"name":"partial","in":"query","required":false,"description":"true = while the trace still runs, read what it has found so far (first results inside about a minute).","schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/traces/{id}/accounts":{"get":{"operationId":"list_trace_accounts","summary":"Every account in the trace","description":"Each account with its side, followers, account age, when it first showed up, how it interacted, who it came through, and its first post. Filter, sort and page.","tags":["Read a trace"],"parameters":[{"name":"id","in":"path","required":true,"description":"The trace id (26 characters, from POST /traces or GET /traces).","schema":{"type":"string"}},{"name":"side","in":"query","required":false,"description":"Only this side. critical = all three critical tones together. Read by a language model.","schema":{"type":"string","enum":["strongly_critical","mocking","disagreeing","critical","supportive","neutral","not_read"]}},{"name":"sort","in":"query","required":false,"description":"followers (default), first = first to show up, touches = most interactions, newest_account = youngest accounts first.","schema":{"type":"string","enum":["followers","first","touches","newest_account"]}},{"name":"min_followers","in":"query","required":false,"description":"Only accounts with at least this many followers.","schema":{"type":"integer"}},{"name":"new","in":"query","required":false,"description":"true = only accounts under 30 days old.","schema":{"type":"boolean"}},{"name":"q","in":"query","required":false,"description":"Search handle, name and bio.","schema":{"type":"string"}},{"name":"full","in":"query","required":false,"description":"true = every post of each account, bio and avatar too.","schema":{"type":"boolean"}},{"name":"partial","in":"query","required":false,"description":"true = while the trace still runs, read what it has found so far (first results inside about a minute).","schema":{"type":"boolean"}},{"name":"limit","in":"query","required":false,"description":"How many to return, 1 to 200 (default 50).","schema":{"type":"integer"}},{"name":"cursor","in":"query","required":false,"description":"The `next` value from the previous page.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/traces/{id}/accounts/{handle}":{"get":{"operationId":"get_trace_account","summary":"One account in full","description":"Everything on one account in this trace: profile, side, every post of theirs we hold with each post's own reading.","tags":["Read a trace"],"parameters":[{"name":"id","in":"path","required":true,"description":"The trace id (26 characters, from POST /traces or GET /traces).","schema":{"type":"string"}},{"name":"handle","in":"path","required":true,"description":"Their handle, with or without @.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/traces/{id}/posts":{"get":{"operationId":"list_trace_posts","summary":"Every post we hold, in order","description":"Each post with its account, time, link, text, and its own side and tone. Filter by side, time, words, account or threat flag.","tags":["Read a trace"],"parameters":[{"name":"id","in":"path","required":true,"description":"The trace id (26 characters, from POST /traces or GET /traces).","schema":{"type":"string"}},{"name":"side","in":"query","required":false,"description":"Only this side. critical = all three critical tones together. Read by a language model.","schema":{"type":"string","enum":["strongly_critical","mocking","disagreeing","critical","supportive","neutral","not_read"]}},{"name":"sort","in":"query","required":false,"description":"oldest (default), newest, or followers = biggest accounts first.","schema":{"type":"string","enum":["oldest","newest","followers"]}},{"name":"since","in":"query","required":false,"description":"Only posts at or after this time (ISO 8601).","schema":{"type":"string"}},{"name":"until","in":"query","required":false,"description":"Only posts at or before this time (ISO 8601).","schema":{"type":"string"}},{"name":"q","in":"query","required":false,"description":"Only posts containing these words.","schema":{"type":"string"}},{"name":"account","in":"query","required":false,"description":"Only this account's posts.","schema":{"type":"string"}},{"name":"threat","in":"query","required":false,"description":"true = only posts a model flagged as a possible threat.","schema":{"type":"boolean"}},{"name":"partial","in":"query","required":false,"description":"true = while the trace still runs, read what it has found so far (first results inside about a minute).","schema":{"type":"boolean"}},{"name":"limit","in":"query","required":false,"description":"How many to return, 1 to 200 (default 50).","schema":{"type":"integer"}},{"name":"cursor","in":"query","required":false,"description":"The `next` value from the previous page.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/traces/{id}/timeline":{"get":{"operationId":"get_trace_timeline","summary":"Arrivals over time","description":"Posts (or accounts) per time bin with the critical share, the busiest stretch, stretches we did not read, and activity by hour and weekday.","tags":["Read a trace"],"parameters":[{"name":"id","in":"path","required":true,"description":"The trace id (26 characters, from POST /traces or GET /traces).","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/traces/{id}/groups":{"get":{"operationId":"get_trace_groups","summary":"Who moved together","description":"Accounts that posted within minutes of each other, posted the same words, or look alike (created together, same hours), and accounts that posted at the subject before. Measured matches, not proof of coordination.","tags":["Read a trace"],"parameters":[{"name":"id","in":"path","required":true,"description":"The trace id (26 characters, from POST /traces or GET /traces).","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/traces/{id}/who":{"get":{"operationId":"get_trace_who","summary":"Who they are","description":"Where X says the accounts are based, what they call themselves in their bios, renamed accounts, the typical account, big and verified accounts, journalists by their own bio, accounts the subject follows, and what ChatGPT and other AIs answer about the subject. Never who anyone is in real life.","tags":["Read a trace"],"parameters":[{"name":"id","in":"path","required":true,"description":"The trace id (26 characters, from POST /traces or GET /traces).","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/traces/{id}/sites":{"get":{"operationId":"get_trace_sites","summary":"Other sites","description":"Posts about it on other sites (Reddit, forums, archives and more), each with its link, plus which sites we checked and the domains the posts linked to.","tags":["Read a trace"],"parameters":[{"name":"id","in":"path","required":true,"description":"The trace id (26 characters, from POST /traces or GET /traces).","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/traces/{id}/evidence":{"get":{"operationId":"get_trace_evidence","summary":"Saved posts, threats, personal info","description":"The saved copies of posts (text as it stood when the trace ran), posts a model flagged as a possible threat, posts carrying the subject's personal info, and the block list. For the sealed pack (zip, hashes, time-stamp) see get_evidence_pack.","tags":["Read a trace"],"parameters":[{"name":"id","in":"path","required":true,"description":"The trace id (26 characters, from POST /traces or GET /traces).","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/traces/{id}/evidence/pack":{"get":{"operationId":"get_evidence_pack","summary":"The evidence pack: a sealed zip of every saved post, with hashes and a time-stamp","description":"The pack a full name trace builds a few minutes after it finishes: one capture record per post (the text as it stood, ids, times, the raw source record and, on Bluesky, the signed repo proof), screenshots, Internet Archive copies where taken, a SHA-256 manifest and an RFC 3161 time-stamp on it. `status` is none (not built yet), building, ready or failed, with a plain `reason`; when ready, `url` is a one-hour download link for the zip and `counts` says what is inside. Name traces only; a story trace answers 422. The pack never changes once sealed; later looks at pending archive copies are listed as addenda.","tags":["Read a trace"],"parameters":[{"name":"id","in":"path","required":true,"description":"The trace id (26 characters, from POST /traces or GET /traces).","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"422":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/accounts/{handle}":{"get":{"operationId":"lookup_account","summary":"Has this account shown up in my traces before?","description":"Every trace of yours this account appeared in, newest first, how it took part each time, and its profile then (name, followers, bio): renames and changes show as history. Covers traces from Oct 26, 2026 on. Through a marketplace key: the traces started through that marketplace, as the name each was about and the dates, never their ids or links.","tags":["Accounts"],"parameters":[{"name":"handle","in":"path","required":true,"description":"An X @handle or a Bluesky handle.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"How many to return, 1 to 100 (default 20).","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/alerts":{"get":{"operationId":"list_alerts","summary":"Alerts we sent you, newest first","description":"Every alert that fired on a name you watch: the count, the usual, a link. Page with `before`.","tags":["Alerts"],"parameters":[{"name":"since","in":"query","required":false,"description":"Only alerts after this time (ISO 8601).","schema":{"type":"string"}},{"name":"before","in":"query","required":false,"description":"The `next` value from the previous page.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"How many to return, 1 to 100 (default 20).","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"}},"/names":{"get":{"operationId":"list_watched_names","summary":"The names we watch for you","tags":["Alerts"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"read"},"post":{"operationId":"watch_name","summary":"Watch one more name","description":"We alert you (email, plus your Slack, Discord or webhook link) when posts at this name jump. Your plan watches 10 at a time.","tags":["Alerts"],"parameters":[],"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"full","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"An X @handle, an x.com link, or a Bluesky handle."}},"required":["name"]}}}}}},"/names/{id}":{"delete":{"operationId":"unwatch_name","summary":"Stop watching a name","tags":["Alerts"],"parameters":[{"name":"id","in":"path","required":true,"description":"The name's id from list_watched_names.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"}},"x-scope":"full"}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"Your key, pf_live_ followed by 32 characters."}},"schemas":{"Understood":{"type":"object","description":"What we read the text (and files) as. Read by a language model; the lookup itself is rebuilt by rules.","properties":{"kind":{"type":"string","enum":["target","claim"],"description":"target = one @handle is the one being talked about; claim = a story going around."},"query":{"type":"string","description":"What the trace would run on: the @handle, or the story in the few words people post."},"goal":{"type":"string","enum":["target","origin"]},"handle":{"type":["string","null"]},"claim":{"type":["string","null"]},"people":{"type":"array","items":{"type":"string"}},"sides":{"type":"array","items":{"type":"string"},"description":"Camps as the person named them, never judged."},"lines":{"type":"array","items":{"type":"string"},"description":"2 or 3 plain lines: what we would look into."},"missing":{"type":["string","null"],"description":"One thing that would make the search sharper."},"from":{"type":"string","enum":["fast","model"]},"seen":{"type":"array","items":{"type":"object"},"description":"One row per file: post or page, who, when, first words."},"seeds":{"type":"array","items":{"type":"object"},"description":"The posts they showed us; the search starts from them."},"also":{"type":"array","items":{"type":"object","properties":{"claim":{"type":"string"},"line":{"type":"string"}}},"description":"Other stories in what they sent."},"period":{"type":"object","properties":{"days":{"type":["integer","null"]},"words":{"type":["string","null"]}},"description":"How long they say it has gone on."}}},"Samples":{"type":"object","required":["samples"],"properties":{"samples":{"type":"array","items":{"type":"object","required":["id","kind","title","links"],"properties":{"id":{"type":"string","description":"The trace id: use it with every /traces/{id} read."},"kind":{"type":"string","enum":["origin","target"],"description":"origin = a story going around (read /case and /brief); target = a name (every tab)."},"title":{"type":"string"},"what":{"type":"string"},"finishedAt":{"type":"string"},"subject":{"type":"string","description":"The @handle of a name sample."},"platform":{"type":"string"},"links":{"type":"object","description":"Full URLs: trace, case, brief, and for a name story, accounts, posts and instant."}}}},"note":{"type":"string"}}},"UnderstandAnswer":{"type":"object","required":["understood"],"properties":{"understood":{"$ref":"#/components/schemas/Understood"}}},"TraceStarted":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"kind":{"type":"string","enum":["target","origin"],"description":"target = a name trace; origin = a story (rumor) trace."},"query":{"type":"string"},"since":{"type":["string","null"],"description":"How far back it looks (a date); null = as far back as it goes."},"link":{"type":"string"},"etaSec":{"type":["integer","null"]},"reused":{"type":"boolean"},"understood":{"$ref":"#/components/schemas/Understood"},"next":{"type":"string"}}},"CaseStop":{"type":"object","required":["n","label","text","known"],"properties":{"n":{"type":"integer","enum":[1,2,3,4]},"label":{"type":"string","enum":["Started","Spread","Who","Before?"]},"text":{"type":"string","description":"A few words and one number or one link."},"sub":{"type":["string","null"]},"url":{"type":["string","null"]},"note":{"type":["string","null"],"description":"A quiet line: the earliest post they showed us, or how far back they asked."},"known":{"type":"boolean","description":"false = we could not fill it, and the text says so."}}},"Case":{"type":"object","required":["headline","headlineParts","stops","note"],"properties":{"kind":{"type":"string","enum":["origin","target"]},"locked":{"type":"boolean"},"headline":{"type":"string","description":"One sentence built by fixed rules from our counts. No model writes it."},"headlineParts":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"key":{"type":"boolean"}}},"description":"The headline in pieces; key = its key words."},"stops":{"type":"array","minItems":4,"maxItems":4,"items":{"$ref":"#/components/schemas/CaseStop"}},"huh":{"type":["object","null"],"properties":{"big":{"type":"string"},"line":{"type":"string"},"quote":{"type":["string","null"]}},"description":"One standout fact, when there is one."},"saidBack":{"type":["object","null"],"properties":{"lines":{"type":"array","items":{"type":"string"}},"also":{"type":"array","items":{"type":"object"}},"period":{"type":["object","null"]}},"description":"What the person asked about, when the trace started from their words."},"data":{"type":"object","description":"The numbers under the stops: a story trace's case block (started, spread, who, same, before, seeds), or a name trace's counts, first post, groups and repeat accounts."},"note":{"type":"string"}}}},"responses":{"Error":{"description":"Every error is {error, message}: a short code and one sentence a person can act on.","content":{"application/json":{"schema":{"type":"object","required":["error","message"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}}}}}