{"openapi":"3.0.0","paths":{"/v1/accounts":{"get":{"description":"Search Pursuit’s universe of US government entities. Use `total_count` to size an addressable market, then follow `next_cursor` to retrieve every matching account. Accounts are global reference data; `pursuit_id` is the stable identifier returned by account enrichment.","operationId":"AccountsController_listAccounts","parameters":[{"name":"limit","required":false,"in":"query","description":"Maximum number of accounts to return per page.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Pagination cursor from the previous response’s `next_cursor`. Re-send the identical `filter` alongside it — the cursor marks a position in that filter’s result set, not a snapshot of it.","schema":{"type":"string"}},{"name":"filter","required":false,"in":"query","description":"URL-encoded JSON account filter. Omit to match every account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountFilterDto"}}}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListAccountsResponseDto"}}}},"400":{"description":"Returned when the filter or pagination parameters are invalid. The `details` array identifies invalid filter paths."},"401":{"description":"Missing or invalid `x-api-key`, or an inactive subscription."},"429":{"description":"Rate limit exceeded. Limits are applied per company: 50 requests / 10 seconds."}},"security":[{"api_key":[]}],"summary":"Search accounts","tags":["Accounts"]}},"/v1/accounts/types":{"get":{"description":"Return the supported account type groups and the Census summary-level codes used to filter them.","operationId":"AccountsController_listAccountTypes","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountTypesResponseDto"}}}},"401":{"description":"Missing or invalid `x-api-key`, or an inactive subscription."},"429":{"description":"Rate limit exceeded. Limits are applied per company: 50 requests / 10 seconds."}},"security":[{"api_key":[]}],"summary":"List account type groups","tags":["Accounts"]}},"/v1/accounts/enrich":{"post":{"description":"Look up a Pursuit account by name + state and return enrichment data when a match is found.","operationId":"AccountsController_enrichAccount","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrichAccountRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrichAccountResponseDto"}}}},"400":{"description":"Returned when required fields are missing or when the state value is invalid."},"401":{"description":"Missing or invalid `x-api-key`, or an inactive subscription."},"402":{"description":"The company or user does not have sufficient credits."},"429":{"description":"Rate limit exceeded. Limits are applied per company: 50 requests / 10 seconds. Inspect the `Retry-After` and `X-RateLimit-*` response headers."}},"security":[{"api_key":[]}],"summary":"Enrich an account","tags":["Accounts"]}},"/v1/contacts/enrich":{"post":{"description":"Return up to `limit` contacts at a Pursuit account, ordered by confidence score (highest first). Provide `pursuit_id` (from `POST /v1/accounts/enrich`) or `display_name` + `state` to identify the account. Optionally filter by `titles` and/or `departments` to retrieve a persona (e.g., all IT leaders).","operationId":"ContactsController_enrichContact","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrichContactRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrichContactResponseDto"}}}},"400":{"description":"Returned when required fields are missing, the state value is invalid, or neither `pursuit_id` nor `display_name`+`state` are supplied."},"401":{"description":"Missing or invalid `x-api-key`, or an inactive subscription."},"402":{"description":"The company or user does not have sufficient credits."},"429":{"description":"Rate limit exceeded. Limits are applied per company: 50 requests / 10 seconds. Inspect the `Retry-After` and `X-RateLimit-*` response headers."}},"security":[{"api_key":[]}],"summary":"Enrich contacts at an account","tags":["Contacts"]}},"/v1/contacts/validate":{"post":{"description":"Validate and enrich a single CRM contact.\n\nSupply an `account` (`pursuit_id`, or `display_name` + `state`) and a `contact`.\n\n- reason `match`: contact found in Pursuit.\n- reason `score`: not found; the submitted input is scored.","operationId":"ContactsController_validateContact","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateContactRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateContactResponseDto"}}}},"400":{"description":"Missing or malformed required fields."},"401":{"description":"Missing or invalid `x-api-key`, or an inactive subscription."},"402":{"description":"The company or user does not have sufficient credits."},"429":{"description":"Rate limit exceeded (50 requests / 10 seconds per company)."}},"security":[{"api_key":[]}],"summary":"Validate + enrich a single CRM contact","tags":["Contacts"]}},"/v1/research/workbooks":{"get":{"description":"List every research workbook in the API key’s account — including workbooks owned by other users and those marked private, since the key is scoped to the whole account. Cursor-paginated: follow `next_cursor` until `has_more` is false.","operationId":"ResearchController_listWorkbooks","parameters":[{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return per page.","schema":{"minimum":1,"maximum":200,"default":50,"example":50,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Pagination cursor from the previous response’s `next_cursor`; pass it to fetch the next page. Omit for the first page.","schema":{"example":"167","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListWorkbooksResponseDto"}}}},"401":{"description":"Missing or invalid `x-api-key`, or an inactive subscription."},"429":{"description":"Rate limit exceeded. Limits are applied per company: 50 requests / 10 seconds. Back off and retry after the 10-second window."}},"security":[{"api_key":[]}],"summary":"List research workbooks","tags":["Research"]}},"/v1/research/workbooks/{workbookId}/worksheets":{"get":{"description":"List the research worksheets in a workbook, each with its column schema and row count. Cursor-paginated: follow `next_cursor` until `has_more` is false.","operationId":"ResearchController_listWorksheets","parameters":[{"name":"workbookId","required":true,"in":"path","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return per page.","schema":{"minimum":1,"maximum":200,"default":50,"example":50,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Pagination cursor from the previous response’s `next_cursor`; pass it to fetch the next page. Omit for the first page.","schema":{"example":"167","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListWorksheetsResponseDto"}}}},"401":{"description":"Missing or invalid `x-api-key`, or an inactive subscription."},"429":{"description":"Rate limit exceeded. Limits are applied per company: 50 requests / 10 seconds. Back off and retry after the 10-second window."}},"security":[{"api_key":[]}],"summary":"List worksheets in a workbook","tags":["Research"]}},"/v1/research/worksheets/{worksheetId}":{"get":{"description":"Return a worksheet’s metadata and column schema.","operationId":"ResearchController_getWorksheet","parameters":[{"name":"worksheetId","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorksheetDetailDto"}}}},"401":{"description":"Missing or invalid `x-api-key`, or an inactive subscription."},"429":{"description":"Rate limit exceeded. Limits are applied per company: 50 requests / 10 seconds. Back off and retry after the 10-second window."}},"security":[{"api_key":[]}],"summary":"Get a worksheet","tags":["Research"]}},"/v1/research/worksheets/{worksheetId}/cells":{"get":{"description":"Return the worksheet’s completed research cells (one per record × column). Each cell carries `record_type` (`account` or `contact`), `record_id`, `column_id`/`column_key`, and the value; group by `(record_type, record_id)` to reconstruct rows, and upsert on `(record_type, record_id, column_id)`. Only completed cells are returned — in-progress, failed, and empty cells are omitted.\n\n**Full backfill:** call without `updated_after`, then follow `next_cursor` until `has_more` is false. `limit` bounds the number of **cells** per page.\n\n**Incremental sync (changes since last pull):** start a pass by setting `updated_after` to the `next_updated_after` returned by your previous pass (the response always returns one, including on backfill). On every subsequent page, re-send that same `updated_after` together with `cursor`; the watermark is bound into the cursor and a mismatch is rejected. When `has_more` is false the pass is done; begin the next pass with `updated_after` set to this response’s `next_updated_after` and no `cursor`.\n\n**Notes:** (1) Cells for columns no longer in the worksheet `columns` schema (e.g. a deleted prompt) carry a `column_<id>` `column_key`. (2) Upsert-only: a deleted cell is never reported as removed, so the stream is not a full mirror — periodically re-run a backfill to reconcile deletions. (3) Cells whose worksheet filter later changed are still returned — previously-generated research stays available.","operationId":"ResearchController_listCells","parameters":[{"name":"worksheetId","required":true,"in":"path","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of cells to return per page.","schema":{"minimum":1,"maximum":500,"default":100,"example":100,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Pagination cursor from the previous response’s `next_cursor`; pass it to fetch the next page. For incremental paging, re-send the same `updated_after` you started the pass with (it is bound into the cursor and a mismatch is rejected).","schema":{"example":"Y2VsbDo2OWEyMGJmYzhjYzkwNTc1ZWYwMWIwZWZ8MHwxNzcx","type":"string"}},{"name":"updated_after","required":false,"in":"query","description":"ISO-8601 timestamp. When set, returns only cells changed at or after this time (incremental sync). Set it to the `next_updated_after` returned by your previous pass (server-issued — do not compute it yourself) and re-send the same value on every page of the pass. Omit entirely for a full backfill. When `has_more` is false, start the next pass with this response’s `next_updated_after` and no `cursor`.","schema":{"example":"2026-06-01T00:00:00.000Z","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CellsResponseDto"}}}},"401":{"description":"Missing or invalid `x-api-key`, or an inactive subscription."},"429":{"description":"Rate limit exceeded. Limits are applied per company: 50 requests / 10 seconds. Back off and retry after the 10-second window."}},"security":[{"api_key":[]}],"summary":"List worksheet cells","tags":["Research"]}},"/v1/signals/runs":{"post":{"callbacks":{"signals.run.completed":{"{$request.body#/callback_url}":{"post":{"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignalRunCallbackPayloadDto"},"examples":{"completed":{"summary":"The run completed with signals.","value":{"event_type":"signals.run.completed","timestamp":"2026-08-31T20:16:51.123Z","run_id":"b9f15935-6221-4f6d-ab58-1021ce1df8ee","source_id":"external-source-123","profile_id":42,"status":"completed","result_count":1,"signals":[{"id":12345,"entity":{"id":"507f1f77bcf86cd799439011","name":"City of Sacramento","state":"CA"},"summary":"The city selected a new permitting software platform.","document":{"title":"Council agenda","url":"https://app.pursuit.us/d/eyJvIjoiLi4uIn0.signature","type":"Agenda","date":"2026-08-20","date_type":"meeting_date"}}]}},"no_data":{"summary":"The run completed without signals.","value":{"event_type":"signals.run.completed","timestamp":"2026-08-31T20:16:51.123Z","run_id":"b9f15935-6221-4f6d-ab58-1021ce1df8ee","source_id":"external-source-123","profile_id":42,"status":"no_data","result_count":0,"signals":[]}},"partial":{"summary":"The run returned signals and recoverable errors.","value":{"event_type":"signals.run.completed","timestamp":"2026-08-31T20:16:51.123Z","run_id":"b9f15935-6221-4f6d-ab58-1021ce1df8ee","source_id":"external-source-123","profile_id":42,"status":"partial","result_count":1,"signals":[{"id":12345,"entity":{"id":"507f1f77bcf86cd799439011","name":"City of Sacramento","state":"CA"},"summary":"The city selected a new permitting software platform.","document":{"title":"Council agenda","url":"https://app.pursuit.us/d/eyJvIjoiLi4uIn0.signature","type":"Agenda","date":"2026-08-20","date_type":"meeting_date"}}],"errors":[{"code":"place_lookup_failed","message":"Lookup failed."}]}},"failed":{"summary":"The run failed before it produced signals.","value":{"event_type":"signals.run.completed","timestamp":"2026-08-31T20:16:51.123Z","run_id":"b9f15935-6221-4f6d-ab58-1021ce1df8ee","source_id":"external-source-123","status":"failed","result_count":0,"signals":[],"errors":[{"message":"The signal run failed."}]}}}}}},"responses":{"204":{"description":"The callback was accepted."}},"parameters":[{"name":"X-Webhook-Delivery-Id","in":"header","required":true,"description":"The signal run identifier for this delivery.","schema":{"type":"string","format":"uuid"}},{"name":"X-Webhook-Timestamp","in":"header","required":true,"description":"The Unix timestamp used in the signature input: `<timestamp>.<raw JSON body>`.","schema":{"type":"string","example":"1788207411"}},{"name":"X-Webhook-Signature","in":"header","required":true,"description":"The lowercase hexadecimal HMAC SHA-256 signature, prefixed with `sha256=`. Use the returned signing secret.","schema":{"type":"string","example":"sha256=0123456789abcdef"}}]}}}},"description":"Creates or updates a source-scoped signal profile and sends the completed result to callback_url. Holds `limit` credits and refunds one credit per signal under the limit once the callback is built.","operationId":"SignalsController_startRun","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSignalRunRequestDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSignalRunResponseDto"}}}},"400":{"description":"The request is invalid or uses an unsupported proof-of-concept mode."},"401":{"description":"An API key is missing or invalid."},"402":{"description":"The company or user does not have sufficient credits to hold `limit` credits."},"429":{"description":"The company rate limit was exceeded."}},"security":[{"api_key":[]}],"summary":"Start an asynchronous signal sourcing run","tags":["Signals"]}},"/v1/account/research":{"post":{"callbacks":{"account.research.completed":{"{$request.body#/callback_url}":{"post":{"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountResearchCallbackPayloadDto"},"examples":{"completed":{"summary":"The research completed.","value":{"event_type":"account.research.completed","timestamp":"2026-09-01T20:16:51.123Z","research_id":"c28f5658-8131-4139-88b1-c183c115f971","status":"completed","entity":{"id":"507f1f77bcf86cd799439011","name":"City of Sacramento","state":"CA"},"result":{"system_name":"Example ERP","evidence":"The account named the system in a public agenda."},"reasoning":"The result uses a recent primary source.","citations":[{"title":"Council agenda","url":"https://app.pursuit.us/d/eyJvIjoiLi4uIn0.signature","type":"Agenda","date":"2026-08-20","date_type":"meeting_date"},{"title":"City selects new ERP vendor","url":"https://www.example.com/news/erp-vendor","type":null,"date":null,"date_type":null}]}},"failed":{"summary":"The research failed.","value":{"event_type":"account.research.completed","timestamp":"2026-09-01T20:16:51.123Z","research_id":"c28f5658-8131-4139-88b1-c183c115f971","status":"failed","errors":[{"message":"The account research failed."}]}}}}}},"responses":{"204":{"description":"The callback was accepted."}},"parameters":[{"name":"X-Webhook-Delivery-Id","in":"header","required":true,"description":"The account research identifier for this delivery.","schema":{"type":"string","format":"uuid"}},{"name":"X-Webhook-Timestamp","in":"header","required":true,"description":"The Unix timestamp used in the signature input: `<timestamp>.<raw JSON body>`.","schema":{"type":"string","example":"1788207411"}},{"name":"X-Webhook-Signature","in":"header","required":true,"description":"The lowercase hexadecimal HMAC SHA-256 signature, prefixed with `sha256=`. Use the returned signing secret.","schema":{"type":"string","example":"sha256=0123456789abcdef"}}]}}}},"description":"Matches one account, researches the prompt, and sends the structured result to callback_url.","operationId":"AccountResearchController_startResearch","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAccountResearchRequestDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAccountResearchResponseDto"}}}},"400":{"description":"The request, callback URL, or output schema is invalid."},"401":{"description":"An API key is missing or invalid."},"402":{"description":"The company or user does not have sufficient credits."},"429":{"description":"The company rate limit was exceeded."}},"security":[{"api_key":[]}],"summary":"Start asynchronous research for one account","tags":["Research"]}},"/v1/account/research/{researchId}":{"get":{"operationId":"AccountResearchController_getResearch","parameters":[{"name":"researchId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAccountResearchResponseDto"}}}},"404":{"description":"The account research run was not found."}},"security":[{"api_key":[]}],"summary":"Get an account research run","tags":["Research"]}},"/v1/documents/{token}":{"get":{"description":"The token is the credential. Returns document metadata for the Pursuit app viewer, plus a short-lived S3 download URL when the file is stored in Pursuit. YouTube and HLS sources omit download_url so the player can use the crawled url. A logged-out FOIA request sets sign_in_required and omits download_url. A signed-in FOIA request includes viewer_path for the account page.","operationId":"PublicDocumentsController_get","parameters":[{"name":"token","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicDocumentResponseDto"}}}},"404":{"description":"The token is invalid, expired, or the document is not entitled."}},"summary":"Redeem an HMAC document token for in-app viewer metadata","tags":["Documents"]}}},"info":{"title":"Pursuit Public API","description":"External REST API for Pursuit. Authenticate with a user API key via the `x-api-key` header.","version":"1.0.0","contact":{}},"tags":[{"name":"Accounts","description":"Account search, enrichment, and matching"},{"name":"Contacts","description":"Contact enrichment and persona search at an account"},{"name":"Signals","description":"Asynchronous public-sector signal sourcing"},{"name":"Documents","description":"Unauthenticated HMAC document metadata for the in-app viewer"},{"name":"Research","description":"Asynchronous account research and read-only access to Research-mode workbooks, worksheets, and cells"}],"servers":[],"components":{"securitySchemes":{"api_key":{"type":"apiKey","in":"header","name":"x-api-key"}},"schemas":{"NumberRangeDto":{"type":"object","properties":{"lower":{"type":"number"},"upper":{"type":"number"}},"required":["lower","upper"]},"PopulationComparisonDto":{"type":"object","properties":{"neq":{"type":"number"},"gt":{"type":"number"},"gte":{"type":"number"},"lt":{"type":"number"},"lte":{"type":"number"},"in":{"type":"array","items":{"type":"number"}},"between":{"$ref":"#/components/schemas/NumberRangeDto"}}},"GeoWithinDto":{"type":"object","properties":{"latitude":{"type":"number"},"longitude":{"type":"number"},"radius":{"type":"number","description":"Radius in metres."}},"required":["latitude","longitude","radius"]},"LocationComparisonDto":{"type":"object","properties":{"geoWithin":{"$ref":"#/components/schemas/GeoWithinDto"}},"required":["geoWithin"]},"SearchComparisonDto":{"type":"object","properties":{"search":{"type":"array","items":{"type":"string"}},"notsearch":{"type":"array","items":{"type":"string"}}}},"StringListComparisonDto":{"type":"object","properties":{"in":{"type":"array","items":{"type":"string"}},"notIn":{"type":"array","items":{"type":"string"}}}},"StateComparisonDto":{"type":"object","properties":{"in":{"type":"array","items":{"type":"string"}},"notIn":{"type":"array","items":{"type":"string"}},"eq":{"type":"string"}}},"UrlComparisonDto":{"type":"object","properties":{"search":{"type":"array","items":{"type":"string"}},"notsearch":{"type":"array","items":{"type":"string"}},"iLike":{"type":"string"},"notILike":{"type":"string"}}},"SumLevComparisonDto":{"type":"object","properties":{"in":{"type":"array","items":{"type":"number"}},"notIn":{"type":"array","items":{"type":"number"}}}},"AccountFilterDto":{"type":"object","properties":{"and":{"type":"array","items":{"$ref":"#/components/schemas/AccountFilterDto"}},"or":{"type":"array","items":{"$ref":"#/components/schemas/AccountFilterDto"}},"pop_estimate_2022":{"$ref":"#/components/schemas/PopulationComparisonDto"},"location":{"$ref":"#/components/schemas/LocationComparisonDto"},"display_name":{"$ref":"#/components/schemas/SearchComparisonDto"},"country":{"$ref":"#/components/schemas/StringListComparisonDto"},"state_abbr":{"$ref":"#/components/schemas/StateComparisonDto"},"url":{"$ref":"#/components/schemas/UrlComparisonDto"},"sum_lev":{"description":"Census summary-level codes. Use `GET /v1/accounts/types` for the supported account types and code arrays.","allOf":[{"$ref":"#/components/schemas/SumLevComparisonDto"}]}}},"SearchAccountDto":{"type":"object","properties":{"pursuit_id":{"type":"string","description":"Pursuit identifier for the matched account."},"pursuit_parent_id":{"type":"string","description":"Pursuit identifier for the matched account’s parent entity, when one exists."},"display_name":{"type":"string","description":"Canonical display name in Pursuit."},"full_name":{"type":"string","description":"Longer-form full name, when present."},"state_abbr":{"type":"string","description":"US state or territory abbreviation."},"url":{"type":"string","description":"Canonical website URL."},"sum_lev":{"description":"Census Summary Level codes for this entity.","type":"array","items":{"type":"number"}},"lsadc":{"type":"string","description":"Census Legal/Statistical Area Description code."},"state_fips":{"type":"string","description":"State FIPS code."},"place_fips":{"type":"string","description":"Place FIPS code."},"county_fips":{"type":"string","description":"County FIPS code."},"lea_id":{"type":"string","description":"NCES local education agency id."},"nces_id":{"type":"string","description":"NCES school id."},"ipeds_id":{"type":"string","description":"IPEDS institution id."},"ori_id":{"type":"string","description":"ORI law-enforcement agency id."},"pa_id":{"type":"number","description":"USASpending PA id."},"lat":{"type":"number","description":"Latitude."},"long":{"type":"number","description":"Longitude."},"pop_estimate_2022":{"type":"number","description":"2022 population estimate, when known."},"account_type":{"type":"string","description":"Human-readable account type derived from `sum_lev` (e.g. `County`, `Municipality`, `School District (Unified)`). Comma-separated when an account carries several summary levels.","example":"Municipality"}},"required":["pursuit_id","display_name","state_abbr"]},"ListAccountsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SearchAccountDto"}},"next_cursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or `null` on the last page. Opaque — do not construct or parse it."},"has_more":{"type":"boolean","description":"Whether more accounts match the filter beyond this page."},"total_count":{"type":"number","description":"Total accounts matching the filter, ignoring pagination. This is the number to read as the size of the addressable market the filter describes.","example":1284}},"required":["data","next_cursor","has_more","total_count"]},"AccountTypeDto":{"type":"object","properties":{"name":{"type":"string","example":"Counties"},"sum_levs":{"example":[50,157],"type":"array","items":{"type":"number"}}},"required":["name","sum_levs"]},"AccountTypesResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AccountTypeDto"}}},"required":["data"]},"EnrichAccountRequestDto":{"type":"object","properties":{"display_name":{"type":"string","description":"Account name to look up (e.g., the CRM \"Account Name\" or \"Display Name\"). Required.","example":"Yreka Union High School District","maxLength":500},"state":{"type":"string","description":"US state or territory. Accepts a 2-letter abbreviation (`CA`) or the full name (`California`). One of `state` or `state_abbr` is required.","example":"California","minLength":2,"maxLength":50},"state_abbr":{"type":"string","description":"Deprecated alias of `state`. Accepts the 2-letter abbreviation only.","example":"CA","minLength":2,"maxLength":2,"deprecated":true},"url":{"type":"string","description":"Optional account website URL. When provided, improves match precision.","example":"https://yreka.k12.ca.us","maxLength":2048}},"required":["display_name"]},"EnrichMatchDto":{"type":"object","properties":{"status":{"type":"string","enum":["matched","no_match","out_of_scope"],"description":"`matched` when an account was found, `no_match` when none of the candidates were a confident match, `out_of_scope` when the input was missing or invalid."},"category":{"type":"string","description":"Raw match category from the matcher (e.g., `exact_match`, `ai_match`, `no_match`, `out_of_scope`)."},"score":{"type":"number","description":"Numeric match score on a 0-5 scale. 5 = exact, 4 = AI-determined, 1 = no match, 0 = out of scope."},"reason":{"type":"string","description":"Human-readable explanation of the match decision."},"confidence":{"type":"number","description":"AI confidence (0-1) when the match was decided by the AI matcher."},"details":{"type":"object","description":"Why the matcher reached its decision — match type, candidate count, AI reasoning, etc."}},"required":["status","category","score","reason"]},"EnrichAccountDto":{"type":"object","properties":{"pursuit_id":{"type":"string","description":"Pursuit identifier for the matched account."},"pursuit_parent_id":{"type":"string","description":"Pursuit identifier for the matched account’s parent entity, when one exists."},"display_name":{"type":"string","description":"Canonical display name in Pursuit."},"full_name":{"type":"string","description":"Longer-form full name, when present."},"state_abbr":{"type":"string","description":"US state or territory abbreviation."},"url":{"type":"string","description":"Canonical website URL."},"sum_lev":{"description":"Census Summary Level codes for this entity.","type":"array","items":{"type":"number"}},"lsadc":{"type":"string","description":"Census Legal/Statistical Area Description code."},"state_fips":{"type":"string","description":"State FIPS code."},"place_fips":{"type":"string","description":"Place FIPS code."},"county_fips":{"type":"string","description":"County FIPS code."},"lea_id":{"type":"string","description":"NCES local education agency id."},"nces_id":{"type":"string","description":"NCES school id."},"ipeds_id":{"type":"string","description":"IPEDS institution id."},"ori_id":{"type":"string","description":"ORI law-enforcement agency id."},"pa_id":{"type":"number","description":"USASpending PA id."},"lat":{"type":"number","description":"Latitude."},"long":{"type":"number","description":"Longitude."},"pop_estimate_2022":{"type":"number","description":"2022 population estimate, when known."}},"required":["pursuit_id","display_name","state_abbr"]},"EnrichAccountResponseDto":{"type":"object","properties":{"match":{"$ref":"#/components/schemas/EnrichMatchDto"},"account":{"description":"Populated when `match.status === \"matched\"`.","allOf":[{"$ref":"#/components/schemas/EnrichAccountDto"}]}},"required":["match"]},"EnrichContactRequestDto":{"type":"object","properties":{"pursuit_id":{"type":"string","description":"Pursuit account identifier (24-char Mongo ObjectId, from `POST /v1/accounts/enrich`). When provided, the account lookup is skipped. If absent, `display_name` + `state` (or `state_abbr`) are required.","example":"507f1f77bcf86cd799439011"},"display_name":{"type":"string","description":"Account name to look up (e.g., the CRM \"Account Name\"). Required when `pursuit_id` is not provided.","example":"City of Ann Arbor","maxLength":500},"state":{"type":"string","description":"US state or territory. Accepts a 2-letter abbreviation (`CA`) or the full name (`California`). Required when `pursuit_id` is not provided.","example":"California","minLength":2,"maxLength":50},"state_abbr":{"type":"string","description":"Deprecated alias of `state`. Accepts the 2-letter abbreviation only.","example":"CA","minLength":2,"maxLength":2,"deprecated":true},"url":{"type":"string","description":"Optional account website URL. Only used when running an account match (ignored if `pursuit_id` is provided).","example":"https://yreka.k12.ca.us","maxLength":2048},"titles":{"description":"Filter results to contacts whose best title matches any of these terms (fuzzy match). Up to 10 entries.","example":["Superintendent","Director of IT"],"maxItems":10,"type":"array","items":{"type":"string"}},"departments":{"description":"Filter results to contacts whose best department matches any of these terms (fuzzy match). Up to 10 entries.","example":["Information Technology","Finance"],"maxItems":10,"type":"array","items":{"type":"string"}},"limit":{"type":"number","description":"Maximum number of contacts to return. Default 5, max 10.","example":5,"minimum":1,"maximum":10,"default":5}}},"EnrichContactDto":{"type":"object","properties":{"pursuit_contact_id":{"type":"string","description":"Pursuit identifier for the contact."},"pursuit_id":{"type":"string","description":"Pursuit identifier for the account the contact belongs to."},"first_name":{"type":"string","description":"Contact first name."},"last_name":{"type":"string","description":"Contact last name."},"title":{"type":"string","description":"Best-known job title."},"department":{"type":"string","description":"Best-known department."},"email":{"type":"string","description":"Best-known email address."},"phone":{"type":"string","description":"Best-known phone number."},"confidence_score":{"type":"number","description":"Internal confidence score (0-100) reflecting data reliability. Higher is better."}},"required":["pursuit_contact_id","pursuit_id","first_name","last_name"]},"EnrichContactResponseDto":{"type":"object","properties":{"match":{"$ref":"#/components/schemas/EnrichMatchDto"},"account":{"description":"Populated when `match.status === \"matched\"`.","allOf":[{"$ref":"#/components/schemas/EnrichAccountDto"}]},"contacts":{"description":"Contacts ranked by relevance. Empty array when no account match or no candidates meet quality thresholds.","type":"array","items":{"$ref":"#/components/schemas/EnrichContactDto"}}},"required":["match","contacts"]},"AccountIdentityDto":{"type":"object","properties":{"pursuit_id":{"type":"string","description":"Pursuit account identifier (24-char Mongo ObjectId, from `POST /v1/accounts/enrich`). When provided, the account lookup is skipped. If absent, `display_name` + `state` (or `state_abbr`) are required.","example":"507f1f77bcf86cd799439011"},"display_name":{"type":"string","description":"Account name to look up (e.g., the CRM \"Account Name\"). Required when `pursuit_id` is not provided.","example":"City of Ann Arbor","maxLength":500},"state":{"type":"string","description":"US state or territory. Accepts a 2-letter abbreviation (`CA`) or the full name (`California`). Required when `pursuit_id` is not provided.","example":"California","minLength":2,"maxLength":50},"state_abbr":{"type":"string","description":"Deprecated alias of `state`. Accepts the 2-letter abbreviation only.","example":"CA","minLength":2,"maxLength":2,"deprecated":true},"url":{"type":"string","description":"Optional account website URL. Only used when running an account match (ignored if `pursuit_id` is provided).","example":"https://yreka.k12.ca.us","maxLength":2048}}},"ValidateContactInputDto":{"type":"object","properties":{"first_name":{"type":"string","description":"Contact first name.","example":"Jane","maxLength":200},"last_name":{"type":"string","description":"Contact last name.","example":"Doe","maxLength":200},"title":{"type":"string","description":"Job title — sharpens the contact match.","maxLength":200},"email":{"type":"string","description":"Current CRM email, if any. Reconciled against Pursuit data.","maxLength":320},"phone":{"type":"string","description":"Current CRM phone, if any.","maxLength":50}},"required":["first_name","last_name"]},"ValidateContactRequestDto":{"type":"object","properties":{"account":{"$ref":"#/components/schemas/AccountIdentityDto"},"contact":{"$ref":"#/components/schemas/ValidateContactInputDto"}},"required":["account","contact"]},"ContactInfoDto":{"type":"object","properties":{"first_name":{"type":"string","description":"First name."},"last_name":{"type":"string","description":"Last name."},"email":{"type":"string","description":"Best email."},"phone":{"type":"string","description":"Phone."},"title":{"type":"string","description":"Best job title."},"titles":{"description":"All known titles. Match only.","type":"array","items":{"type":"string"}},"department":{"type":"string","description":"Department. Match only."},"linkedin_url":{"type":"string","description":"LinkedIn profile URL found via web search. Match only."}}},"ScoreReasoningDto":{"type":"object","properties":{"summary":{"type":"string","nullable":true,"description":"Human-readable summary explaining the employment determination."},"is_current":{"type":"boolean","nullable":true,"description":"Currently at the org."},"is_current_in_role":{"type":"boolean","nullable":true,"description":"Currently in the role."},"is_retired":{"type":"boolean","nullable":true,"description":"Appears retired."},"has_moved":{"type":"boolean","nullable":true,"description":"Evidence the contact moved on."},"matched_title":{"type":"string","nullable":true,"description":"Title we matched on."},"email_verified":{"type":"boolean","nullable":true,"description":"Best email verified."},"email_generic":{"type":"boolean","nullable":true,"description":"Best email is generic."}}},"ValidateContactResponseDto":{"type":"object","properties":{"reason":{"type":"string","enum":["match","score","no_match","account_not_found"],"description":"Where the result came from."},"status":{"type":"string","enum":["active","not_in_role","moved","retired","unknown"],"description":"Inferred employment status."},"pursuit_id":{"type":"string","description":"Matched Pursuit contact id; empty when not a known contact."},"confidence_score":{"type":"number","description":"Confidence score (0–100), or null when not determined.","nullable":true},"contact":{"description":"Contact info; null when no contact is available.","nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/ContactInfoDto"}]},"score_reasoning":{"description":"Why the confidence score was assigned; null when not scored.","nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/ScoreReasoningDto"}]}},"required":["reason","status","pursuit_id"]},"WorkbookDto":{"type":"object","properties":{"id":{"type":"number","description":"Workbook identifier.","example":167},"name":{"type":"string","description":"Workbook name.","example":"Michigan districts"},"visibility":{"type":"string","description":"Workbook visibility within the account.","enum":["private","company"],"example":"company"},"default_worksheet_id":{"type":"number","description":"Identifier of the default worksheet, when one is set.","example":283478470},"created_at":{"format":"date-time","type":"string","description":"Creation time (ISO-8601).","example":"2026-02-27T15:43:32.213Z"},"updated_at":{"format":"date-time","type":"string","description":"Last update time (ISO-8601).","example":"2026-06-16T19:55:49.345Z"}},"required":["id","name","visibility","created_at","updated_at"]},"ListWorkbooksResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WorkbookDto"}},"next_cursor":{"type":"string","description":"Cursor to pass as `cursor` to fetch the next page. Null when there are no more pages.","nullable":true,"example":"187"},"has_more":{"type":"boolean","description":"Whether more pages are available.","example":true}},"required":["data","has_more"]},"ColumnSchemaDto":{"type":"object","properties":{"column_id":{"type":"number","description":"Stable identifier for this column. Cells reference it as `column_id`; use it as the join/upsert key (it survives column renames, unlike `column_key`).","example":411},"column_key":{"type":"string","description":"Human-readable column key (the normalized column name). Convenient for mapping to a warehouse field, but can change if the column is renamed — prefer `column_id` as the stable key.","example":"annual_budget"},"name":{"type":"string","description":"Human-readable column name.","example":"Annual Budget"},"description":{"type":"string","description":"Column description, when configured.","example":"The district’s most recent annual operating budget."},"type":{"type":"string","description":"Whether the column applies to accounts or contacts.","enum":["account","contact"],"example":"account"},"is_ai_generated":{"type":"boolean","description":"True when the column value is produced by the AI research pipeline.","example":true}},"required":["column_id","column_key","name","type","is_ai_generated"]},"WorksheetSummaryDto":{"type":"object","properties":{"id":{"type":"number","description":"Worksheet identifier.","example":283478468},"name":{"type":"string","description":"Worksheet name.","example":"Michigan budgets"},"workbook_id":{"type":"number","description":"Identifier of the workbook this worksheet belongs to.","example":166},"cell_count":{"type":"number","description":"Number of completed research cells in the worksheet.","example":750},"columns":{"description":"Column schema.","type":"array","items":{"$ref":"#/components/schemas/ColumnSchemaDto"}},"created_at":{"format":"date-time","type":"string","description":"Creation time (ISO-8601).","example":"2026-02-27T21:06:11.487Z"},"updated_at":{"format":"date-time","type":"string","description":"Last update time (ISO-8601).","example":"2026-03-02T18:17:19.269Z"}},"required":["id","name","workbook_id","cell_count","columns","created_at","updated_at"]},"ListWorksheetsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WorksheetSummaryDto"}},"next_cursor":{"type":"string","description":"Cursor to pass as `cursor` to fetch the next page. Null when there are no more pages.","nullable":true,"example":"283478474"},"has_more":{"type":"boolean","description":"Whether more pages are available.","example":false}},"required":["data","has_more"]},"WorksheetDetailDto":{"type":"object","properties":{"id":{"type":"number","description":"Worksheet identifier.","example":283478468},"name":{"type":"string","description":"Worksheet name.","example":"Michigan budgets"},"workbook_id":{"type":"number","description":"Identifier of the workbook this worksheet belongs to.","example":166},"columns":{"description":"Column schema.","type":"array","items":{"$ref":"#/components/schemas/ColumnSchemaDto"}},"created_at":{"format":"date-time","type":"string","description":"Creation time (ISO-8601).","example":"2026-02-27T21:06:11.487Z"},"updated_at":{"format":"date-time","type":"string","description":"Last update time (ISO-8601).","example":"2026-03-02T18:17:19.269Z"}},"required":["id","name","workbook_id","columns","created_at","updated_at"]},"WorksheetCellDto":{"type":"object","properties":{"record_type":{"type":"string","description":"Whether this cell belongs to an account or a contact — matches the column’s `type`.","enum":["account","contact"],"example":"account"},"record_id":{"type":"string","description":"Pursuit identifier of the account or contact this cell belongs to. Group cells by `(record_type, record_id)` to form rows; combine with `column_id` as the warehouse upsert key.","example":"65a603f7c769fa16f65938d3"},"column_id":{"type":"number","description":"Identifier of the column this cell belongs to (matches `column_id` in the worksheet schema).","example":411},"column_key":{"type":"string","description":"Human-readable column key (the normalized column name).","example":"annual_budget"},"result":{"type":"object","description":"The research output value — string, number, boolean, or JSON object depending on the column.","example":"For FY2025-2026 the district adopted a total operating budget of $412.8M, up 6.3% year over year."},"reasoning":{"type":"string","description":"Model reasoning behind the value.","example":"Extracted from the district’s adopted budget book and CAFR; figures cross-checked against the finance office summary."},"citations":{"description":"Deduplicated source URLs supporting the value. Empty when the value has no web citations.","example":["https://example.gov/fy2026-adopted-budget"],"type":"array","items":{"type":"string"}},"updated_at":{"format":"date-time","type":"string","description":"Last update time of this cell (ISO-8601).","example":"2026-03-02T18:17:55.318Z"}},"required":["record_type","record_id","column_id","column_key","citations","updated_at"]},"CellsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WorksheetCellDto"}},"next_cursor":{"type":"string","description":"Opaque cursor for the next page. Pass as `cursor`; for incremental pages re-send the same `updated_after` (it is bound into the cursor and a mismatch is rejected). Null when there are no more pages.","nullable":true,"example":"Y2VsbDo2OWEyMGJmYzhjYzkwNTc1ZWYwMWIwZjJ8MHwxNzcx"},"next_updated_after":{"type":"string","description":"Server-issued incremental-sync watermark, captured at the start of this scan and stable across all pages of a pass. When `has_more` is false, use this value as `updated_after` to start your next pass (do not compute it yourself).","nullable":true,"example":"2026-03-02T18:20:00.000Z"},"has_more":{"type":"boolean","description":"Whether more pages are available.","example":true}},"required":["data","has_more"]},"CallbackEntityDto":{"type":"object","properties":{"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"name":{"type":"string","example":"City of Sacramento"},"state":{"type":"string","example":"CA"}},"required":["id","name","state"]},"CallbackDocumentDto":{"type":"object","properties":{"title":{"type":"string","nullable":true,"example":"Council agenda"},"url":{"type":"string","format":"uri","example":"https://app.pursuit.us/d/eyJvIjoiLi4uIn0.signature"},"type":{"type":"string","nullable":true,"example":"Agenda"},"date":{"type":"string","format":"date","nullable":true,"example":"2026-08-20"},"date_type":{"type":"string","nullable":true,"example":"meeting_date"}},"required":["title","url","type","date","date_type"]},"SignalRunCallbackSignalDto":{"type":"object","properties":{"id":{"type":"string","description":"Stable identifier for this signal, unique across runs.","example":"918273"},"entity":{"$ref":"#/components/schemas/CallbackEntityDto"},"summary":{"type":"string","example":"The city selected a new permitting software platform."},"document":{"$ref":"#/components/schemas/CallbackDocumentDto"}},"required":["id","entity","summary","document"]},"CallbackErrorDto":{"type":"object","properties":{"code":{"type":"string","example":"place_lookup_failed"},"message":{"type":"string","example":"The account did not match a Pursuit account."}},"required":["message"]},"SignalRunCallbackPayloadDto":{"type":"object","properties":{"event_type":{"type":"string","enum":["signals.run.completed"],"example":"signals.run.completed"},"timestamp":{"type":"string","format":"date-time","example":"2026-08-31T20:16:51.123Z"},"run_id":{"type":"string","format":"uuid"},"source_id":{"type":"string","example":"external-source-123"},"profile_id":{"type":"number","example":42},"status":{"type":"string","enum":["completed","no_data","partial","failed"],"example":"completed"},"result_count":{"type":"number","minimum":0,"example":1},"signals":{"type":"array","items":{"$ref":"#/components/schemas/SignalRunCallbackSignalDto"}},"errors":{"type":"array","items":{"$ref":"#/components/schemas/CallbackErrorDto"}}},"required":["event_type","timestamp","run_id","source_id","status","result_count","signals"]},"SignalCriterionDto":{"type":"object","properties":{"description":{"type":"string"},"priority":{"type":"number","minimum":1,"maximum":5}},"required":["description","priority"]},"SignalProfileDto":{"type":"object","properties":{"description":{"type":"string"},"criteria":{"type":"array","items":{"$ref":"#/components/schemas/SignalCriterionDto"}}},"required":["description","criteria"]},"CreateSignalRunRequestDto":{"type":"object","properties":{"source_id":{"type":"string","description":"Stable source identifier used to reuse a signal profile."},"callback_url":{"type":"string"},"accounts":{"type":"array","items":{"$ref":"#/components/schemas/AccountIdentityDto"}},"account_filters":{"$ref":"#/components/schemas/AccountFilterDto"},"signal_profile":{"$ref":"#/components/schemas/SignalProfileDto"},"date_range":{"type":"string","enum":["past_30_days","past_60_days","past_90_days","past_6_months"],"default":"past_30_days","description":"How far back the document search should look."},"limit":{"type":"number","default":100,"minimum":1,"maximum":1000}},"required":["source_id","callback_url","signal_profile"]},"CreateSignalRunResponseDto":{"type":"object","properties":{"run_id":{"type":"string"},"status":{"type":"string","enum":["queued"]},"signing_secret":{"type":"string","description":"HMAC SHA-256 secret for callback signature verification."}},"required":["run_id","status","signing_secret"]},"AccountResearchCallbackPayloadDto":{"type":"object","properties":{"event_type":{"type":"string","enum":["account.research.completed"]},"timestamp":{"type":"string","format":"date-time"},"research_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["completed","failed"]},"entity":{"$ref":"#/components/schemas/CallbackEntityDto"},"result":{"description":"The value defined by the request output_schema.","oneOf":[{"type":"object","additionalProperties":true},{"type":"array","items":{}},{"type":"string","nullable":true},{"type":"number"},{"type":"boolean"}]},"reasoning":{"type":"string"},"citations":{"description":"Pursuit documents and web pages the result was drawn from. Web pages have null type, date, and date_type.","type":"array","items":{"$ref":"#/components/schemas/CallbackDocumentDto"}},"errors":{"type":"array","items":{"$ref":"#/components/schemas/CallbackErrorDto"}}},"required":["event_type","timestamp","research_id","status"]},"JsonSchemaDto":{"type":"object","properties":{"type":{"type":"string","enum":["object","array","string","number","integer","boolean","null"],"example":"object"},"title":{"type":"string","example":"Research result"},"description":{"type":"string","example":"Structured evidence for the research result."},"format":{"type":"string","example":"date"},"pattern":{"type":"string","example":"^[A-Z]{2}$"},"properties":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/JsonSchemaDto"},"description":"Named fields for an object result."},"required":{"example":["system_name","evidence"],"type":"array","items":{"type":"string"}},"items":{"oneOf":[{"$ref":"#/components/schemas/JsonSchemaDto"},{"type":"array","items":{"$ref":"#/components/schemas/JsonSchemaDto"}}]},"enum":{"type":"array","items":{},"example":["active","retired"]},"const":{"type":"object","description":"The only permitted value."},"default":{"type":"object","description":"The default result value."},"minimum":{"type":"number","minimum":0},"maximum":{"type":"number","minimum":0},"minLength":{"type":"number","minimum":0},"maxLength":{"type":"number","minimum":0},"minItems":{"type":"number","minimum":0},"maxItems":{"type":"number","minimum":0},"additionalProperties":{"oneOf":[{"type":"boolean"},{"$ref":"#/components/schemas/JsonSchemaDto"}],"default":true},"$defs":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/JsonSchemaDto"}},"$ref":{"type":"string","example":"#/$defs/address"},"allOf":{"type":"array","items":{"$ref":"#/components/schemas/JsonSchemaDto"}},"anyOf":{"type":"array","items":{"$ref":"#/components/schemas/JsonSchemaDto"}},"oneOf":{"type":"array","items":{"$ref":"#/components/schemas/JsonSchemaDto"}},"not":{"oneOf":[{"$ref":"#/components/schemas/JsonSchemaDto"}],"allOf":[{"$ref":"#/components/schemas/JsonSchemaDto"}]}},"required":["type"]},"CreateAccountResearchRequestDto":{"type":"object","properties":{"account":{"$ref":"#/components/schemas/AccountIdentityDto"},"prompt":{"type":"string","description":"The research question for the matched account.","example":"What enterprise resource planning system does this account use?","maxLength":20000},"output_schema":{"description":"A JSON Schema that defines the result field in the completed callback.","example":{"type":"object","properties":{"system_name":{"type":"string"},"evidence":{"type":"string"}},"required":["system_name","evidence"],"additionalProperties":false},"allOf":[{"$ref":"#/components/schemas/JsonSchemaDto"}]},"callback_url":{"type":"string","format":"uri","example":"https://example.com/callbacks/account-research"}},"required":["account","prompt","output_schema","callback_url"]},"CreateAccountResearchResponseDto":{"type":"object","properties":{"research_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued"]},"signing_secret":{"type":"string","description":"HMAC SHA-256 secret for callback signature verification."}},"required":["research_id","status","signing_secret"]},"GetAccountResearchResponseDto":{"type":"object","properties":{"research_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["running","completed","failed"]},"entity":{"$ref":"#/components/schemas/CallbackEntityDto"},"result":{"type":"object","description":"The value defined by the submitted output_schema.","nullable":true},"reasoning":{"type":"string","nullable":true},"citations":{"type":"array","items":{"$ref":"#/components/schemas/CallbackDocumentDto"}},"errors":{"type":"array","items":{"type":"object","additionalProperties":true}},"created_at":{"format":"date-time","type":"string"},"completed_at":{"format":"date-time","type":"string","nullable":true}},"required":["research_id","status","citations","errors","created_at"]},"PublicDocumentEntityDto":{"type":"object","properties":{"id":{"type":"string","example":"507f1f77bcf86cd799439011"},"display_name":{"type":"string","example":"City of Sacramento"},"state_abbr":{"type":"string","example":"CA"}},"required":["id","display_name","state_abbr"]},"PublicDocumentTranscriptSegmentDto":{"type":"object","properties":{"start":{"type":"number"},"end":{"type":"number"},"text":{"type":"string"}},"required":["start","end","text"]},"PublicDocumentTranscriptDto":{"type":"object","properties":{"segments":{"type":"array","items":{"$ref":"#/components/schemas/PublicDocumentTranscriptSegmentDto"}}},"required":["segments"]},"PublicDocumentResponseDto":{"type":"object","properties":{"entity":{"$ref":"#/components/schemas/PublicDocumentEntityDto"},"url_id":{"type":"string","example":"aHR0cHM6Ly9jaXR5Lmdvdi9hZ2VuZGEucGRm"},"url":{"type":"string","nullable":true,"example":"https://city.gov/agenda.pdf"},"file_name":{"type":"string","nullable":true,"example":"agenda.pdf"},"title":{"type":"string","nullable":true,"example":"Council agenda"},"summary":{"type":"string","nullable":true},"document_type":{"type":"string","nullable":true,"example":"Agenda"},"dates":{"nullable":true,"type":"array","items":{"type":"string"}},"relevant_date_value":{"type":"string","nullable":true,"example":"2026-08-20"},"page":{"type":"number","nullable":true,"example":4},"ts":{"type":"number","nullable":true,"example":90},"transcript":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/PublicDocumentTranscriptDto"}]},"download_url":{"type":"string","format":"uri","description":"Short-lived S3 GET for files the in-app viewer must fetch. Omitted for YouTube/HLS."},"viewer_path":{"type":"string","example":"/account/city-of-sacramento-ca/507f1f77bcf86cd799439011/doc/aHR0cHM6Ly9jaXR5Lmdvdi9hZ2VuZGEucGRm?page=4"},"sign_in_required":{"type":"boolean","description":"Set for a FOIA document when the caller is logged out. url, file_name, summary, and download_url are omitted; the app should ask them to sign in and open viewer_path."}},"required":["entity","url_id","url","file_name","title","summary","document_type","dates","relevant_date_value","page","ts","viewer_path"]}}}}