{"openapi":"3.1.0","info":{"title":"Startup State Atlas API","version":"1.0","summary":"Utah's agent-readable map of startup companies and resources.","description":"The Startup State Atlas API powers the Founder Navigator, the Utah\nStartup Map, and the agent-native CLI/MCP surfaces.\n\n## Authentication\n\nTwo authentication modes are supported:\n\n- **Better Auth session cookie** (`auth.session_token`) — set by\n  `/sign-in` and `/sign-up`. Used by humans clicking through the\n  web UI. Roles: `owner`, `goeo_admin`, `superadmin`.\n- **`X-Atlas-Admin-Token`** header — service-account-style token\n  validated against `env.ATLAS_ADMIN_TOKEN`. Used by the local\n  CLI, the local stdio MCP server, and any external machine\n  client. Bypasses Better Auth and assumes a privileged caller.\n\nRead endpoints are open to both. Write endpoints accept either.\nFor owner-edit on `PATCH /api/v1/companies/{slug}`, the session\nuser id must match `companies.claimed_by_user_id`.\n\n## Error envelope\n\nEvery non-2xx response uses the same shape:\n\n```json\n{ \"error\": { \"code\": \"string\", \"message\": \"string\", \"details\": \"unknown?\" } }\n```\n\nCodes used: `bad_request`, `not_found`, `forbidden`, `unauthorized`,\n`conflict`, `internal`.\n\n## Wire format\n\nAll wire fields are `snake_case`. Internal TS uses `camelCase` and\nconverts at the boundary via `lib/api-codec.ts`.\n"},"servers":[{"url":"https://startupstateatlas.dev","description":"Production"},{"url":"https://startup.utah.gov","description":"Planned canonical (not yet live)"},{"url":"http://localhost:3000","description":"Local dev (Next.js)"},{"url":"http://localhost:8787","description":"Local Worker preview (`npm run preview`)"}],"security":[{"cookieAuth":[]},{"atlasAdminToken":[]},{}],"tags":[{"name":"founder-passports","description":"Intake profiles + cached recommendation plans."},{"name":"recommend","description":"Score Utah resources against a founder profile."},{"name":"resources","description":"The library of Utah startup resources."},{"name":"companies","description":"Utah startup company directory + ownership."},{"name":"ownership","description":"Business-owner verification flow."},{"name":"search","description":"Generic search across resources + companies."},{"name":"meta","description":"API metadata (OpenAPI spec, etc.)."},{"name":"investors","description":"Public investor directory + profiles (Phase 6 / Agent 8)."},{"name":"saved","description":"Investor watchlist of saved companies (Phase 6 / Agent 8)."},{"name":"intros","description":"Admin-brokered intro requests (Phase 6 / Agent 8)."}],"paths":{"/api/v1/founder-passports":{"post":{"tags":["founder-passports"],"operationId":"createFounderPassport","summary":"Create a founder passport without computing recommendations.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FounderPassportInput"}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","required":["passport_id"],"properties":{"passport_id":{"type":"string","pattern":"^fp_[a-zA-Z0-9_-]+$","example":"fp_x9p2w8KsPq"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"500":{"$ref":"#/components/responses/Internal"}}}},"/api/v1/founder-passports/enrich":{"post":{"tags":["founder-passports"],"operationId":"enrichFounderPassport","summary":"Extract intake fields from a website URL via fetch + LLM.","description":"The user pastes a website URL on the intake form; this endpoint\nfetches the page directly and pipes the text through Anthropic\nwith a structured-output prompt, returning a partial set of\nfields the front-end can use to prefill chips. **No persistence.**\nOn fetch failure / non-HTML response / LLM timeout / private-IP\nrejection the response degrades to `{ fields: [], degraded: true }`\n— never returns 5xx for upstream failures.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["website_url"],"properties":{"website_url":{"type":"string","format":"uri","description":"Must use http:// or https://. Social/profile hosts (linkedin, x, github, etc.) are rejected.","example":"https://example.com"}}}}}},"responses":{"200":{"description":"Extracted fields (possibly degraded).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrichResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"500":{"$ref":"#/components/responses/Internal"}}}},"/api/v1/founder-passports/{id}/plan":{"get":{"tags":["founder-passports"],"operationId":"getFounderPlan","summary":"Return cached recommendations for a passport.","security":[],"parameters":[{"$ref":"#/components/parameters/PassportId"}],"responses":{"200":{"description":"Plan (possibly empty if not yet computed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecommendResponse"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/Internal"}}}},"/api/v1/resources/recommend":{"post":{"tags":["recommend"],"operationId":"recommendResources","summary":"Score and bucket Utah resources for a founder's profile.","description":"Pass either `passport_id` (re-uses an existing passport row) or\na full `FounderPassportInput` (creates one) — but **not both**.\n\nLLM \"Because…\" is enabled by default; on Anthropic error/timeout\nthe response degrades gracefully (`because: \"\"`) — the endpoint\nnever 5xxs on the LLM.\n\nSide effects: if no `passport_id` is provided, a `founder_passports`\nrow is created. The full result set is persisted to\n`recommendations` (idempotent; any prior recs for the passport\nare deleted first).\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecommendRequest"}}}},"responses":{"200":{"description":"Scored recommendations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecommendResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/Internal"}}}},"/api/v1/resources":{"get":{"tags":["resources"],"operationId":"listResources","summary":"List startup resources, optionally filtered by free-text query.","security":[],"parameters":[{"in":"query","name":"q","schema":{"type":"string"},"description":"Optional free-text query against title + description."},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Resource list.","content":{"application/json":{"schema":{"type":"object","required":["resources"],"properties":{"resources":{"type":"array","items":{"$ref":"#/components/schemas/ResourceSummary"}}}}}}},"500":{"$ref":"#/components/responses/Internal"}}}},"/api/v1/resources/{id}":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","pattern":"^r_[a-zA-Z0-9_-]+$"},"description":"Resource id (e.g. `r_2628`)."}],"get":{"tags":["resources"],"operationId":"getResource","summary":"Fetch a single resource by id.","security":[],"responses":{"200":{"description":"Resource row.","content":{"application/json":{"schema":{"type":"object","required":["resource"],"properties":{"resource":{"$ref":"#/components/schemas/ResourceSummary"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/companies":{"get":{"tags":["companies"],"operationId":"listCompanies","summary":"Search companies by name / slug / website.","security":[],"parameters":[{"in":"query","name":"q","schema":{"type":"string"},"description":"Optional free-text search."},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"Companies.","content":{"application/json":{"schema":{"type":"object","required":["companies"],"properties":{"companies":{"type":"array","items":{"$ref":"#/components/schemas/CompanySummary"}}}}}}}}}},"/api/v1/companies/{slug}":{"parameters":[{"$ref":"#/components/parameters/CompanySlug"}],"get":{"tags":["companies"],"operationId":"getCompany","summary":"Fetch a single company by slug.","security":[],"responses":{"200":{"description":"Company row.","content":{"application/json":{"schema":{"type":"object","required":["company"],"properties":{"company":{"$ref":"#/components/schemas/Company"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["companies"],"operationId":"patchCompany","summary":"Update a company profile.","description":"Three auth modes are accepted:\n\n- **Owner**: Better Auth session whose `user.id` matches\n  `companies.claimed_by_user_id`. Owner edits are restricted\n  to a whitelist (no `slug`, `linkedin`, `address_text`).\n- **Admin**: Better Auth session with role `goeo_admin` or\n  `superadmin`. No whitelist.\n- **Machine**: `X-Atlas-Admin-Token` header. No whitelist.\n\nEvery successful PATCH writes an audit row to `profile_updates`.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyPatch"}}}},"responses":{"200":{"description":"Updated company row.","content":{"application/json":{"schema":{"type":"object","required":["company"],"properties":{"company":{"$ref":"#/components/schemas/Company"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/ownership-submissions":{"post":{"tags":["ownership"],"operationId":"submitOwnershipDoc","summary":"Upload an ownership-proof document (multipart).","description":"Multipart upload (PDF / PNG / JPEG / WebP, ≤10 MB) routed to the\nR2 `OWNERSHIP_DOCS` bucket. Creates a submission row with status\n`pending`. If the caller already has a pending submission for the\nsame company, the existing row is returned with `duplicate: true`.\n","security":[{"cookieAuth":[]},{"atlasAdminToken":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["company_slug","file"],"properties":{"company_slug":{"type":"string","description":"Slug of the company being claimed."},"file":{"type":"string","format":"binary","description":"PDF / PNG / JPEG / WebP, ≤10 MB."}}}}}},"responses":{"201":{"description":"Submission created (or pending duplicate returned).","content":{"application/json":{"schema":{"type":"object","required":["submission_id","status"],"properties":{"submission_id":{"type":"string","pattern":"^bos_[a-zA-Z0-9_-]+$"},"status":{"type":"string","enum":["pending"]},"duplicate":{"type":"boolean"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/search":{"get":{"tags":["search"],"operationId":"search","summary":"Generic search across resources + companies.","security":[],"parameters":[{"in":"query","name":"q","required":true,"schema":{"type":"string","minLength":1},"description":"Search query."},{"in":"query","name":"type","schema":{"type":"string","enum":["resources","companies","all"],"default":"all"}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"Mixed result list.","content":{"application/json":{"schema":{"type":"object","required":["results"],"properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/SearchResult"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/api/v1/investors":{"get":{"tags":["investors"],"operationId":"listVerifiedInvestors","summary":"List all verified investors with public profiles.","description":"Public — no auth required. Returns only investors whose admin\nreview has flipped them to `verification_status = \"verified\"` and\nwho have published a slug. Email is never included.\n","security":[],"responses":{"200":{"description":"Verified investors.","content":{"application/json":{"schema":{"type":"object","required":["investors","count"],"properties":{"count":{"type":"integer"},"investors":{"type":"array","items":{"$ref":"#/components/schemas/InvestorListItem"}}}}}}}}}},"/api/v1/investor-profiles/{slug}":{"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"get":{"tags":["investors"],"operationId":"getInvestorProfileBySlug","summary":"Return the public investor card for a given slug.","description":"Public — no auth required. Email is intentionally redacted; the\nonly way to surface contact info is via an admin-accepted intro\nrequest.\n","security":[],"responses":{"200":{"description":"Investor card.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvestorPublicCard"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["investors"],"operationId":"patchInvestorProfile","summary":"Update an investor's public profile.","description":"Three auth modes:\n- **Owner** (Better Auth session, `investor_profiles.user_id ===\n  session.user.id`): can write the public-fields whitelist\n  (display_name, bio, tagline, website, linkedin, slug while\n  unverified). Editing display_name or website on a verified\n  profile auto-flips `verification_status` back to `unverified`\n  for re-review.\n- **Admin** (`goeo_admin` / `superadmin`): can also write\n  `verification_status`. Admin edits do NOT auto-unverify.\n- **Machine** (`X-Atlas-Admin-Token`): same as admin.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvestorPublicPatch"}}}},"responses":{"200":{"description":"Updated investor card.","content":{"application/json":{"schema":{"type":"object","required":["profile"],"properties":{"profile":{"$ref":"#/components/schemas/InvestorPublicCard"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Slug already taken.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/saved-companies":{"get":{"tags":["saved"],"operationId":"listSavedCompanies","summary":"The caller's saved-companies watchlist.","description":"Session required. Returns the calling investor's watchlist\njoined to companies. Returns an empty list if the caller has\nno investor profile.\n","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Saved companies.","content":{"application/json":{"schema":{"type":"object","required":["saved"],"properties":{"saved":{"type":"array","items":{"$ref":"#/components/schemas/SavedCompany"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"tags":["saved"],"operationId":"saveCompany","summary":"Save a company to the caller's watchlist.","security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["company_id"],"properties":{"company_id":{"type":"string","pattern":"^co_[a-z0-9]+$"},"note":{"type":"string","nullable":true,"maxLength":1000}}}}}},"responses":{"201":{"description":"Saved.","content":{"application/json":{"schema":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string","pattern":"^sc_[a-z0-9]+$"},"status":{"type":"string","enum":["created"]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Already saved.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"tags":["saved"],"operationId":"updateSavedCompanyNote","summary":"Update the note on a saved company.","security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["company_id","note"],"properties":{"company_id":{"type":"string","pattern":"^co_[a-z0-9]+$"},"note":{"type":"string","nullable":true,"maxLength":1000}}}}}},"responses":{"200":{"description":"Updated.","content":{"application/json":{"schema":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string"},"status":{"type":"string","enum":["updated"]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["saved"],"operationId":"unsaveCompany","summary":"Remove a company from the caller's watchlist.","security":[{"cookieAuth":[]}],"parameters":[{"in":"query","name":"company_id","required":true,"schema":{"type":"string","pattern":"^co_[a-z0-9]+$"}}],"responses":{"204":{"description":"Unsaved."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/intro-requests":{"get":{"tags":["intros"],"operationId":"listIntroRequests","summary":"The caller's intro requests (outbound + inbound).","description":"Session required. Returns:\n- `outbound`: all rows where the caller is the requester, any status.\n- `inbound`: rows targeted at the caller (their investor profile\n  or claimed company), but only after admin moves them to\n  `accepted` or `introduced`.\n","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"Outbound + inbound.","content":{"application/json":{"schema":{"type":"object","required":["outbound","inbound"],"properties":{"outbound":{"type":"array","items":{"$ref":"#/components/schemas/IntroRequestOutbound"}},"inbound":{"type":"array","items":{"$ref":"#/components/schemas/IntroRequestInbound"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"tags":["intros"],"operationId":"createIntroRequest","summary":"Submit a new intro request to GOEO.","description":"Body's `target` is a discriminated union — exactly one of\n`investor` or `company` may be specified. The requester's role\nis snapshotted at request time so the audit trail reflects what\nthey were when they asked, not what they are now.\n","security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntroRequestCreate"}}}},"responses":{"201":{"description":"Pending.","content":{"application/json":{"schema":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string","pattern":"^irq_[a-z0-9]+$"},"status":{"type":"string","enum":["pending"]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/intro-requests/{id}":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","pattern":"^irq_[a-z0-9]+$"}}],"get":{"tags":["intros"],"operationId":"getIntroRequest","summary":"Read a single intro request.","description":"Allowed for the requester, an admin, or the target party — but\nthe target only sees the row after admin moves it to `accepted`\nor `introduced`.\n","security":[{"cookieAuth":[]}],"responses":{"200":{"description":"The intro.","content":{"application/json":{"schema":{"type":"object","required":["intro"],"properties":{"intro":{"$ref":"#/components/schemas/IntroRequest"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["intros"],"operationId":"reviewIntroRequest","summary":"Admin review — accept, decline, or mark introduced.","description":"Admin only (`goeo_admin` / `superadmin`). Body's `status` must\nbe one of `accepted | declined | introduced`.\n\nConcurrent-action guard: only `pending` rows are actionable.\nTwo admins acting near-simultaneously will see one win and one\nget a 409.\n\nEmail side-effects:\n- `accepted`: emails both parties with each other's contact info\n  (or just the requester if the target hasn't claimed their\n  profile, with a note that GOEO will follow up).\n- `declined`: emails the requester only.\n- `introduced`: emails the requester only (used when admin made\n  the intro out-of-band).\n","security":[{"cookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntroRequestPatch"}}}},"responses":{"200":{"description":"Updated intro.","content":{"application/json":{"schema":{"type":"object","required":["intro"],"properties":{"intro":{"$ref":"#/components/schemas/IntroRequest"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Intro already moved past `pending`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/openapi.json":{"get":{"tags":["meta"],"operationId":"getOpenApiSpec","summary":"Return this OpenAPI 3.1 specification as JSON.","security":[],"responses":{"200":{"description":"The OpenAPI spec.","content":{"application/json":{"schema":{"type":"object","description":"OpenAPI 3.1 document."}}}}}}}},"components":{"parameters":{"PassportId":{"in":"path","name":"id","required":true,"schema":{"type":"string","pattern":"^fp_[a-zA-Z0-9_-]+$"},"description":"Founder-passport id."},"CompanySlug":{"in":"path","name":"slug","required":true,"schema":{"type":"string"},"description":"Company slug."}},"responses":{"BadRequest":{"description":"Validation error or malformed request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"Unauthorized":{"description":"Missing or invalid authentication.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"Forbidden":{"description":"Authenticated but not authorized for this resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"NotFound":{"description":"Resource does not exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"Internal":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"auth.session_token","description":"Better Auth session cookie. Set by `/sign-in` or `/sign-up`.\nRoles: `owner`, `goeo_admin`, `superadmin`. Owner edits gate\non `companies.claimed_by_user_id` matching `user.id`.\n"},"atlasAdminToken":{"type":"apiKey","in":"header","name":"X-Atlas-Admin-Token","description":"Service-account-style token validated against\n`env.ATLAS_ADMIN_TOKEN`. The local CLI (`npm run cli` from a\ncheckout), the local stdio MCP server (`npm run mcp`), and\nexternal machine clients use this header for write operations.\nBypasses Better Auth and assumes a privileged caller.\n"}},"schemas":{"ApiError":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","example":"bad_request"},"message":{"type":"string"},"details":{}}}}},"FounderStage":{"type":"string","enum":["idea","pre_seed","mvp","paying_customers","growth","mature"]},"FounderGoal":{"type":"string","enum":["start_business","raise_seed_round","raise_growth_round","find_customers","hire","export","commercialize_research","find_workspace","find_mentors","scale_business"]},"FounderUrgency":{"type":"string","enum":["this_week","this_month","this_quarter","next_quarter","this_year"]},"FounderPassportInput":{"type":"object","required":["stage","industry","goal","communities","needs","constraints"],"properties":{"county":{"type":"string"},"city":{"type":"string"},"stage":{"$ref":"#/components/schemas/FounderStage"},"industry":{"type":"string","example":"B2B SaaS"},"communities":{"type":"array","items":{"type":"string"},"default":[]},"goal":{"$ref":"#/components/schemas/FounderGoal"},"urgency":{"$ref":"#/components/schemas/FounderUrgency"},"business_size":{"type":"string"},"business_type":{"type":"string"},"needs":{"type":"array","items":{"type":"string"},"default":[]},"constraints":{"type":"array","items":{"type":"string"},"default":[]},"website_url":{"type":"string","format":"uri","description":"Must use http:// or https://."},"enrichment_source":{"type":"string","minLength":1,"maxLength":64,"description":"Set by the front-end to the enrich provider id (e.g.\n`anthropic-fetch`) when the enrich path ran before\nsubmission. Server stamps `enriched_at = now`.\n"}}},"RecommendRequestById":{"type":"object","required":["passport_id"],"additionalProperties":false,"properties":{"passport_id":{"type":"string","pattern":"^fp_[a-zA-Z0-9_-]+$"}},"description":"Re-use a saved passport row by id. No other fields permitted.\n"},"RecommendRequestByBody":{"allOf":[{"$ref":"#/components/schemas/FounderPassportInput"},{"not":{"required":["passport_id"]}}]},"RecommendRequest":{"oneOf":[{"$ref":"#/components/schemas/RecommendRequestById"},{"$ref":"#/components/schemas/RecommendRequestByBody"}],"description":"Either pass `passport_id` (re-uses an existing passport row)\nOR a full `FounderPassportInput` (creates one) — never both.\nSending both is a 400 with `code: bad_request`.\n"},"Bucket":{"type":"string","enum":["now","next","ignore"]},"RecommendedResource":{"type":"object","required":["resource_id","title","score","bucket","reasons","because","action_text"],"properties":{"resource_id":{"type":"string","pattern":"^r_[0-9]+$","example":"r_2628"},"title":{"type":"string","example":"Small Business Administration (SBA)"},"score":{"type":"integer","minimum":0,"maximum":100,"example":78},"bucket":{"$ref":"#/components/schemas/Bucket"},"reasons":{"type":"array","items":{"type":"string"}},"because":{"type":"string","description":"Humanized one-liner — for now/next this is the strongest\nscoring reason translated to natural language; for ignore\nit's the negative `explainSkip` output. Always non-empty\nfor actionable rows. Never contains snake_case enums.\n"},"action_text":{"type":"string","description":"Suggested next action; empty string when none."},"kind":{"type":"string","description":"Primary topic, lowercased."},"source_url":{"type":"string","format":"uri"},"contact_email":{"type":"string","format":"email"}}},"RecommendResponse":{"type":"object","required":["passport_id","narrative","recommendations","generated_at"],"properties":{"passport_id":{"type":"string","pattern":"^fp_[a-zA-Z0-9_-]+$"},"narrative":{"type":"string","description":"Plan-scoped synthesis paragraph (60–100 words). Hedges\nadjacency in plain English, names specific orgs, uses\nthe founder's chosen labels. Falls back to a deterministic\ntemplated paragraph when Anthropic fails, so this is\nnon-empty whenever there is at least one positive\nrecommendation; \"\" only when there are no recommendations.\n"},"recommendations":{"type":"array","items":{"$ref":"#/components/schemas/RecommendedResource"}},"generated_at":{"type":"string","format":"date-time"}}},"EnrichField":{"type":"object","required":["name","value","confidence"],"properties":{"name":{"type":"string","example":"industry"},"value":{},"confidence":{"type":"number","minimum":0,"maximum":1}}},"EnrichResponse":{"type":"object","required":["fields"],"properties":{"fields":{"type":"array","items":{"$ref":"#/components/schemas/EnrichField"}},"degraded":{"type":"boolean","description":"Set when fetch / parse / LLM degraded; `fields` will be empty."}}},"ResourceSummary":{"type":"object","required":["id","title"],"properties":{"id":{"type":"string","pattern":"^r_[0-9]+$"},"title":{"type":"string"},"description":{"type":"string"},"kind":{"type":"string"},"source_url":{"type":"string","format":"uri"},"contact_email":{"type":"string","format":"email"}}},"CompanySummary":{"type":"object","required":["id","slug","name","status"],"properties":{"id":{"type":"string","pattern":"^co_[a-zA-Z0-9_-]+$"},"slug":{"type":"string"},"name":{"type":"string"},"website":{"type":"string","format":"uri","nullable":true},"sector":{"type":"string","nullable":true},"stage":{"type":"string","nullable":true},"employee_count":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"county":{"type":"string","nullable":true},"status":{"type":"string","enum":["claimed","pending","unclaimed"],"description":"Computed from `claimed_by_user_id` + presence of pending ownership submission."}}},"Company":{"type":"object","required":["id","slug","name"],"properties":{"id":{"type":"string","pattern":"^co_[a-zA-Z0-9_-]+$"},"slug":{"type":"string"},"name":{"type":"string"},"website":{"type":"string","format":"uri","nullable":true},"description":{"type":"string","nullable":true},"sector":{"type":"string","nullable":true},"stage":{"type":"string","nullable":true},"employee_count":{"type":"string","nullable":true},"hiring_status":{"type":"boolean","nullable":true},"founding_year":{"type":"integer","nullable":true},"logo_url":{"type":"string","format":"uri","nullable":true},"founder_team_json":{"type":"string","nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"address_text":{"type":"string","nullable":true},"linkedin":{"type":"string","nullable":true},"claimed_by_user_id":{"type":"string","nullable":true},"verified_at":{"type":"string","format":"date-time","nullable":true},"claimed_at":{"type":"string","format":"date-time","nullable":true}}},"CompanyPatch":{"type":"object","description":"Partial update. Owner edits accept only the whitelisted keys\nbelow. Admin / machine token may also send `slug`, `linkedin`,\nand `address_text`.\n","properties":{"name":{"type":"string"},"website":{"type":"string","format":"uri"},"description":{"type":"string"},"sector":{"type":"string"},"stage":{"type":"string"},"employee_count":{"type":"string"},"hiring_status":{"type":"boolean"},"founding_year":{"type":"integer"},"logo_url":{"type":"string","format":"uri"},"founder_team_json":{"type":"string"},"lat":{"type":"number"},"lng":{"type":"number"},"slug":{"type":"string","description":"Admin / machine only."},"linkedin":{"type":"string","description":"Admin / machine only."},"address_text":{"type":"string","description":"Admin / machine only."}}},"SearchResult":{"type":"object","required":["kind","id","title"],"properties":{"kind":{"type":"string","enum":["resource","company"]},"id":{"type":"string"},"slug":{"type":"string","description":"Present when `kind == \"company\"`."},"title":{"type":"string"},"summary":{"type":"string"},"score":{"type":"number","description":"Relevance score (higher is better)."}}},"InvestorListItem":{"type":"object","required":["id","slug","display_name"],"properties":{"id":{"type":"string","pattern":"^inv_[a-z0-9]+$"},"slug":{"type":"string"},"display_name":{"type":"string"},"firm_name":{"type":"string","nullable":true},"tagline":{"type":"string","nullable":true},"investor_type":{"type":"string","nullable":true},"stages":{"type":"array","items":{"type":"string"}},"sectors":{"type":"array","items":{"type":"string"}},"geo_focus":{"type":"array","items":{"type":"string"}},"verified_at":{"type":"string","format":"date-time","nullable":true},"canonical_url":{"type":"string","example":"/investors/pelion"}}},"InvestorPublicCard":{"type":"object","required":["id","slug","display_name","stages","sectors","geo_focus","verification","canonical_url","agent_card_urls"],"properties":{"id":{"type":"string","pattern":"^inv_[a-z0-9]+$"},"slug":{"type":"string"},"display_name":{"type":"string"},"firm_name":{"type":"string","nullable":true},"investor_type":{"type":"string","nullable":true},"investor_type_display":{"type":"string","nullable":true},"tagline":{"type":"string","nullable":true},"bio":{"type":"string","nullable":true},"website":{"type":"string","format":"uri","nullable":true},"linkedin":{"type":"string","format":"uri","nullable":true},"stages":{"type":"array","items":{"type":"string"}},"stages_display":{"type":"array","items":{"type":"string"}},"sectors":{"type":"array","items":{"type":"string"}},"sectors_display":{"type":"array","items":{"type":"string"}},"check_size_min":{"type":"integer","nullable":true},"check_size_max":{"type":"integer","nullable":true},"geo_focus":{"type":"array","items":{"type":"string"}},"geo_focus_display":{"type":"array","items":{"type":"string"}},"verification":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["verified","unverified"]},"verified_at":{"type":"string","format":"date-time","nullable":true}}},"last_updated_at":{"type":"string","format":"date-time","nullable":true},"canonical_url":{"type":"string"},"agent_card_urls":{"type":"object","required":["html","markdown","json","api"],"properties":{"html":{"type":"string"},"markdown":{"type":"string"},"json":{"type":"string"},"api":{"type":"string"}}}}},"InvestorPublicPatch":{"type":"object","description":"All fields optional. Owner edits are limited to the public\nwhitelist (display_name, bio, tagline, website, linkedin, slug\nwhen unverified). `verification_status` is admin/machine only.\n","properties":{"slug":{"type":"string","pattern":"^[a-z0-9-]+$","maxLength":80},"display_name":{"type":"string","maxLength":120},"bio":{"type":"string","nullable":true,"maxLength":2000},"tagline":{"type":"string","nullable":true,"maxLength":200},"website":{"type":"string","format":"uri","nullable":true},"linkedin":{"type":"string","format":"uri","nullable":true},"verification_status":{"type":"string","enum":["unverified","verified"],"description":"Admin / machine only."}}},"SavedCompany":{"type":"object","required":["id","saved_at"],"properties":{"id":{"type":"string","pattern":"^sc_[a-z0-9]+$"},"saved_at":{"type":"string","format":"date-time","nullable":true},"note":{"type":"string","nullable":true},"company":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"sector":{"type":"string","nullable":true},"stage":{"type":"string","nullable":true},"logo_url":{"type":"string","nullable":true}}}}},"IntroRequestCreate":{"type":"object","required":["target","message_text"],"properties":{"target":{"oneOf":[{"type":"object","required":["type","id"],"properties":{"type":{"type":"string","enum":["investor"]},"id":{"type":"string","pattern":"^inv_[a-z0-9]+$"}}},{"type":"object","required":["type","id"],"properties":{"type":{"type":"string","enum":["company"]},"id":{"type":"string","pattern":"^co_[a-z0-9]+$"}}}]},"message_text":{"type":"string","minLength":20,"maxLength":2000}}},"IntroRequestPatch":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["accepted","declined","introduced"]},"admin_notes":{"type":"string","maxLength":2000,"nullable":true}}},"IntroRequest":{"type":"object","required":["id","status","message_text","requester_role","target_type"],"properties":{"id":{"type":"string","pattern":"^irq_[a-z0-9]+$"},"status":{"type":"string","enum":["pending","accepted","declined","introduced"]},"message_text":{"type":"string"},"admin_notes":{"type":"string","nullable":true},"submitted_at":{"type":"string","format":"date-time","nullable":true},"reviewed_at":{"type":"string","format":"date-time","nullable":true},"requester_role":{"type":"string"},"requester_user_id":{"type":"string"},"target_type":{"type":"string","enum":["investor","company"]},"target_id":{"type":"string","nullable":true},"target_name":{"type":"string","nullable":true},"target_url":{"type":"string","nullable":true}}},"IntroRequestOutbound":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending","accepted","declined","introduced"]},"submitted_at":{"type":"string","format":"date-time","nullable":true},"reviewed_at":{"type":"string","format":"date-time","nullable":true},"admin_notes":{"type":"string","nullable":true},"message_text":{"type":"string"},"target_type":{"type":"string","enum":["investor","company"]},"target_id":{"type":"string","nullable":true},"target_name":{"type":"string","nullable":true},"target_url":{"type":"string","nullable":true}}},"IntroRequestInbound":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string"},"status":{"type":"string","enum":["accepted","introduced"]},"submitted_at":{"type":"string","format":"date-time","nullable":true},"reviewed_at":{"type":"string","format":"date-time","nullable":true},"admin_notes":{"type":"string","nullable":true},"message_text":{"type":"string"},"requester_role":{"type":"string"},"requester_name":{"type":"string","nullable":true},"requester_email":{"type":"string","format":"email","nullable":true}}}}}}