{"openapi":"3.0.3","info":{"title":"Idynic API","description":"API for Idynic - AI Career Companion","version":"1.0.0","contact":{"name":"Idynic Support","url":"https://idynic.app"}},"servers":[{"url":"https://idynic.app/api/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key in format: idn_xxxxxxxx"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"request_id":{"type":"string"}}}}},"Profile":{"type":"object","properties":{"contact":{"type":"object","properties":{"name":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"linkedin_url":{"type":"string","nullable":true},"github_url":{"type":"string","nullable":true},"website_url":{"type":"string","nullable":true},"logo_url":{"type":"string","nullable":true}}},"identity":{"$ref":"#/components/schemas/Identity"},"experience":{"type":"array","items":{"$ref":"#/components/schemas/WorkHistory"}},"ventures":{"type":"array","items":{"$ref":"#/components/schemas/Venture"}},"skills":{"type":"array","items":{"$ref":"#/components/schemas/Claim"}},"education":{"type":"array","items":{"$ref":"#/components/schemas/Claim"}},"certifications":{"type":"array","items":{"$ref":"#/components/schemas/Claim"}},"achievements":{"type":"array","items":{"$ref":"#/components/schemas/Claim"}}}},"Identity":{"type":"object","nullable":true,"description":"AI-generated professional identity snapshot synthesized from claims","properties":{"archetype":{"type":"string","nullable":true,"description":"One of: Builder, Optimizer, Connector, Guide, Stabilizer, Specialist, Strategist, Advocate, Investigator, Performer"},"headline":{"type":"string","nullable":true,"description":"6-10 word professional tagline"},"bio":{"type":"string","nullable":true,"description":"2-3 sentence narrative in second person"},"keywords":{"type":"array","items":{"type":"string"},"description":"3-5 defining professional attributes"},"matches":{"type":"array","items":{"type":"string"},"description":"3 specific job titles the user would excel at"},"generated_at":{"type":"string","format":"date-time","description":"When the identity was last generated"}}},"Claim":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["skill","achievement","attribute","education","certification"]},"label":{"type":"string"},"description":{"type":"string","nullable":true},"confidence":{"type":"number"}}},"ClaimDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string"},"label":{"type":"string"},"description":{"type":"string","nullable":true},"confidence":{"type":"number"},"evidence":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"source_document_id":{"type":"string","format":"uuid","nullable":true},"content":{"type":"string","nullable":true}}}},"issues":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string"},"message":{"type":"string"}}}}}},"WorkHistory":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"company":{"type":"string"},"title":{"type":"string"},"start_date":{"type":"string","nullable":true},"end_date":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"summary":{"type":"string","nullable":true},"company_domain":{"type":"string","nullable":true},"logo_override_path":{"type":"string","nullable":true}}},"Venture":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"role":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"start_date":{"type":"string","nullable":true},"end_date":{"type":"string","nullable":true},"url":{"type":"string","nullable":true},"status":{"type":"string","nullable":true}}},"Opportunity":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"company":{"type":"string","nullable":true},"url":{"type":"string","nullable":true},"status":{"type":"string","enum":["tracking","applied","interviewing","offer","rejected","archived"]},"match_score":{"type":"number","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"PendingOpportunity":{"type":"object","description":"An intake review-queue item awaiting triage (add or dismiss).","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string","nullable":true},"company":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"url":{"type":"string","nullable":true},"source_type":{"type":"string","nullable":true},"original_sender":{"type":"string","nullable":true},"original_subject":{"type":"string","nullable":true},"is_duplicate":{"type":"boolean","nullable":true},"status":{"type":"string","nullable":true,"description":"pending, suppressed, dismissed, expired, needs_attention, added, removed."},"dismissed_reason":{"type":"string","nullable":true,"description":"Machine-readable reason the system filed away or closed the row; null for a user dismissal. Values include below_relevance_floor, over_daily_cap, scoring_unavailable, tracked_duplicate, dismissed_before, pending_duplicate (another copy of the same posting is already pending) and posting_closed."},"relevance_score":{"type":"integer","nullable":true,"description":"0-100 fit against saved preferences. Null means NOT SCORED, never zero."},"relevance_reason":{"type":"string","nullable":true},"received_at":{"type":"string","format":"date-time","nullable":true}}},"DiscoveryQueryHealth":{"type":"object","description":"One saved query's yield over the requested rolling window.","properties":{"query_key":{"type":"string"},"source":{"type":"string"},"keyword":{"type":"string"},"location":{"type":"string","nullable":true},"runs":{"type":"integer"},"fetched":{"type":"integer"},"deduped":{"type":"integer"},"queued":{"type":"integer"},"suppressed":{"type":"integer"},"failed":{"type":"integer"},"scored":{"type":"integer"},"over_threshold":{"type":"integer"},"noise_rate":{"type":"number","nullable":true,"description":"suppressed / (queued + suppressed). Null when nothing survived dedup."},"score_avg":{"type":"number","nullable":true},"score_min":{"type":"integer","nullable":true},"score_max":{"type":"integer","nullable":true},"errored_runs":{"type":"integer"},"last_error":{"type":"string","nullable":true},"first_run_at":{"type":"string","format":"date-time"},"last_run_at":{"type":"string","format":"date-time"},"verdict":{"type":"string","enum":["healthy","noisy","dry","failing","insufficient_data"]},"recommendation":{"type":"string"}}},"PreferenceFeedbackEvent":{"type":"object","description":"One append-only correction event.","properties":{"id":{"type":"string","format":"uuid"},"event_type":{"type":"string","enum":["rescue","high_score_dismissal","threshold_change","query_edit","suppression_disagreement"]},"subject_type":{"type":"string","nullable":true},"subject_id":{"type":"string","nullable":true},"surface":{"type":"string"},"payload":{"type":"object"},"created_at":{"type":"string","format":"date-time"}}},"Document":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string"},"filename":{"type":"string","nullable":true},"status":{"type":"string"},"evidence_count":{"type":"integer"},"created_at":{"type":"string","format":"date-time"}}},"DocumentDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string"},"filename":{"type":"string","nullable":true},"status":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"evidence":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"content":{"type":"string","nullable":true},"claim_id":{"type":"string","format":"uuid","nullable":true}}}}}},"AsyncJob":{"type":"object","properties":{"job_id":{"type":"string"},"status":{"type":"string","enum":["processing"]},"message":{"type":"string"}}},"Preferences":{"type":"object","properties":{"industries":{"type":"array","items":{"type":"string"}},"company_sizes":{"type":"array","items":{"type":"string"}},"company_stages":{"type":"array","items":{"type":"string"}},"role_levels":{"type":"array","items":{"type":"string"}},"tech_stack":{"type":"array","items":{"type":"string"}},"work_styles":{"type":"array","items":{"type":"string"}},"culture_signals":{"type":"array","items":{"type":"string"}},"locations":{"type":"array","items":{"type":"string"}},"company_attributes":{"type":"array","items":{"type":"string"}},"compensation_floor":{"type":"number","nullable":true},"raw_description":{"type":"string","nullable":true}}},"TailoredProfile":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"opportunity_id":{"type":"string","format":"uuid"},"narrative":{"type":"string","nullable":true},"resume_data":{"type":"object","nullable":true},"talking_points":{"type":"array","items":{"type":"string"},"nullable":true},"edited_fields":{"type":"object","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"MatchResult":{"type":"object","properties":{"opportunity":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"company":{"type":"string","nullable":true}}},"scores":{"type":"object","properties":{"overall":{"type":"number"},"must_have":{"type":"number"},"nice_to_have":{"type":"number"}}},"strengths":{"type":"array","items":{"type":"object","properties":{"requirement":{"type":"string"},"match":{"type":"object","properties":{"claim":{"type":"string"},"type":{"type":"string"},"similarity":{"type":"number"}}}}}},"gaps":{"type":"array","items":{"type":"object","properties":{"requirement":{"type":"string"},"type":{"type":"string"},"category":{"type":"string"}}}}}},"ShareLink":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"token":{"type":"string"},"url":{"type":"string"},"expires_at":{"type":"string","format":"date-time","nullable":true},"existing":{"type":"boolean","description":"True if an existing share link was returned"}}},"SiteSettings":{"type":"object","properties":{"handle":{"type":"string"},"published":{"type":"boolean"},"published_at":{"type":"string","format":"date-time","nullable":true},"sections":{"type":"object","properties":{"career":{"type":"boolean"},"projects":{"type":"boolean"},"resume":{"type":"boolean"},"fit_check":{"type":"boolean"},"ask":{"type":"boolean"},"contact":{"type":"boolean"}}},"show_in_search":{"type":"boolean"},"theme":{"type":"string"},"public_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"old_handles":{"type":"array","items":{"type":"object","properties":{"handle":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}}}}}},"ClaimSummaryCategory":{"type":"object","properties":{"count":{"type":"integer"},"avgConfidence":{"type":"number"},"topLabels":{"type":"array","items":{"type":"string"},"description":"Top 3 labels by confidence"}}}}},"paths":{"/auth/verify":{"get":{"summary":"Verify API key","description":"Verify that an API key is valid. Useful for testing connections.","tags":["Auth"],"responses":{"200":{"description":"API key is valid","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean"},"user_id":{"type":"string"}}}}}},"401":{"description":"Unauthorized - Invalid API key"}}}},"/profile":{"get":{"summary":"Get user profile","description":"Returns the full profile including contact info, identity, work history, ventures, skills, education, certifications, and achievements.","tags":["Profile"],"responses":{"200":{"description":"User profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Profile"}}}},"401":{"description":"Unauthorized"}}},"patch":{"summary":"Update profile contact info","tags":["Profile"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"location":{"type":"string"},"linkedin":{"type":"string"},"github":{"type":"string"},"website":{"type":"string"},"logo_url":{"type":"string"}}}}}},"responses":{"200":{"description":"Updated contact info"},"401":{"description":"Unauthorized"}}}},"/profile/base-resume/regenerate":{"post":{"summary":"Regenerate the base resume","description":"Queue a full regeneration of the synthesized base resume from the caller's current claims and work history. Returns as soon as the job is enqueued — it does not return the resume. Generation takes roughly 1-2 minutes; poll GET /profile/base-resume for the result. Shares one rate-limit budget with the web app's Regenerate button.","tags":["Profile"],"responses":{"200":{"description":"Regeneration queued","content":{"application/json":{"schema":{"type":"object","properties":{"queued":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized"},"403":{"description":"API key lacks the write:profile scope"},"429":{"description":"Rate limited"}}}},"/reviews/self-review":{"post":{"summary":"Generate a self-review","description":"Compose a cited self-review for a period from the evidence on the caller's record. Synchronous (up to about a minute). An empty period returns status 'quiet' with no AI call. Errors keep the composer's code: review_source_read_failed, review_generation_failed, cost_ceiling. The response waits for the run, so a failure always carries its HTTP status (500, 502, 403), never a 200.","tags":["Reviews"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["period_start","period_end"],"properties":{"period_start":{"type":"string","format":"date-time"},"period_end":{"type":"string","format":"date-time"},"audience":{"type":"string","enum":["manager","self"]}}}}}},"responses":{"200":{"description":"The self-review content"},"400":{"description":"Invalid period or audience"},"401":{"description":"Unauthorized"},"403":{"description":"API key lacks the read:profile scope, or the AI cost ceiling was reached"},"429":{"description":"Rate limited"},"500":{"description":"Evidence read failed"},"502":{"description":"Generation produced no usable output"}}}},"/profile/work-history":{"get":{"summary":"List work history","description":"Returns all work history entries ordered by position.","tags":["Profile"],"responses":{"200":{"description":"List of work history entries","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkHistory"}}}}},"401":{"description":"Unauthorized"}}}},"/profile/work-history/{id}":{"patch":{"summary":"Update work history entry","tags":["Profile"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"company":{"type":"string"},"title":{"type":"string"},"start_date":{"type":"string"},"end_date":{"type":"string"},"location":{"type":"string"},"summary":{"type":"string"},"company_domain":{"type":"string"},"logo_override_path":{"type":"string"}}}}}},"responses":{"200":{"description":"Updated work history entry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkHistory"}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}},"delete":{"summary":"Delete work history entry","tags":["Profile"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}}},"/profile/ventures":{"get":{"summary":"List ventures","description":"Returns all confirmed ventures (side projects, businesses, etc.).","tags":["Profile"],"responses":{"200":{"description":"List of ventures","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Venture"}}}}},"401":{"description":"Unauthorized"}}}},"/claims":{"get":{"summary":"List identity claims","description":"Returns identity claims (skills, achievements, attributes, education, certifications) sorted by confidence.","tags":["Claims"],"parameters":[{"name":"type","in":"query","schema":{"type":"string","enum":["skill","achievement","attribute","education","certification"]},"description":"Filter by claim type"}],"responses":{"200":{"description":"List of claims","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Claim"}}}}},"401":{"description":"Unauthorized"}}}},"/claims/summary":{"get":{"summary":"Get claims summary","description":"Returns aggregated claim statistics by category with counts, average confidence, and top labels.","tags":["Claims"],"responses":{"200":{"description":"Claims summary by type","content":{"application/json":{"schema":{"type":"object","properties":{"skill":{"$ref":"#/components/schemas/ClaimSummaryCategory"},"achievement":{"$ref":"#/components/schemas/ClaimSummaryCategory"},"attribute":{"$ref":"#/components/schemas/ClaimSummaryCategory"},"education":{"$ref":"#/components/schemas/ClaimSummaryCategory"},"certification":{"$ref":"#/components/schemas/ClaimSummaryCategory"}}}}}},"401":{"description":"Unauthorized"}}}},"/claims/{id}":{"get":{"summary":"Get claim detail","description":"Returns a single claim with associated evidence and active issues.","tags":["Claims"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Claim detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimDetail"}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}},"patch":{"summary":"Update claim","description":"Update a claim's label, description, or type.","tags":["Claims"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","enum":["skill","achievement","attribute","education","certification"]}}}}}},"responses":{"200":{"description":"Updated claim","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Claim"}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}},"delete":{"summary":"Delete claim","tags":["Claims"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"id":{"type":"string","format":"uuid"}}}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}}},"/claims/{id}/dismiss":{"post":{"summary":"Dismiss claim issues","description":"Dismiss all active issues for a claim.","tags":["Claims"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Issues dismissed","content":{"application/json":{"schema":{"type":"object","properties":{"dismissed":{"type":"integer"},"message":{"type":"string"}}}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}}},"/documents":{"get":{"summary":"List documents","description":"Returns all uploaded documents with evidence counts, ordered by creation date.","tags":["Documents"],"responses":{"200":{"description":"List of documents","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Document"}}}}},"401":{"description":"Unauthorized"}}}},"/documents/{id}":{"get":{"summary":"Get document detail","description":"Returns a document with its extracted evidence items.","tags":["Documents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Document detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentDetail"}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}},"delete":{"summary":"Delete document","description":"Deletes a document and its associated evidence.","tags":["Documents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}}},"/documents/resume":{"post":{"summary":"Upload resume","description":"Upload a PDF resume for processing. Triggers async extraction of identity claims.","tags":["Documents"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"PDF file (max 10MB)"}}}}}},"responses":{"200":{"description":"Resume accepted for processing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsyncJob"}}}},"400":{"description":"Invalid file or duplicate"},"401":{"description":"Unauthorized"}}}},"/documents/story":{"post":{"summary":"Add career story","description":"Submit a text career story for processing. Triggers async extraction of identity claims.","tags":["Documents"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string","description":"Story text (200-10,000 characters)","minLength":200,"maxLength":10000}}}}}},"responses":{"200":{"description":"Story accepted for processing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsyncJob"}}}},"400":{"description":"Invalid text or duplicate"},"401":{"description":"Unauthorized"}}}},"/opportunities":{"get":{"summary":"List opportunities","tags":["Opportunities"],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["tracking","applied","interviewing","offer","rejected","archived"]},"description":"Filter by status"}],"responses":{"200":{"description":"List of opportunities","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Opportunity"}}}}},"401":{"description":"Unauthorized"}}},"post":{"summary":"Add opportunity","tags":["Opportunities"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","description"],"properties":{"url":{"type":"string","description":"Job posting URL"},"description":{"type":"string","description":"Job description text"}}}}}},"responses":{"200":{"description":"Created opportunity","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"company":{"type":"string","nullable":true},"status":{"type":"string"},"source":{"type":"string"},"requirements":{"type":"object","properties":{"must_have_count":{"type":"integer"},"nice_to_have_count":{"type":"integer"}}},"created_at":{"type":"string","format":"date-time"}}}}}},"401":{"description":"Unauthorized"}}}},"/opportunities/async":{"post":{"summary":"Queue an opportunity for background processing","description":"Queue a job URL (or pasted description) for the durable pipeline and return immediately with a job id. Unlike `/opportunities/add-and-match`, which does scraping, extraction, match scoring and talking points in one 30s+ request, this returns in milliseconds — poll `GET /jobs/{id}` for progress. The pipeline also auto-queues tailoring on completion.","tags":["Opportunities"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"Job posting URL"},"description":{"type":"string","description":"Job description text (if no URL)"},"title_hint":{"type":"string","description":"Best-effort title (e.g. the browser tab's), used only to label the job while it runs"}}}}}},"responses":{"202":{"description":"Accepted; processing job queued","content":{"application/json":{"schema":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"status":{"type":"string"}}}}}},"400":{"description":"Neither url nor description supplied"},"401":{"description":"Unauthorized"},"403":{"description":"Opportunity limit reached"},"409":{"description":"Already saved"}}}},"/jobs/{id}":{"get":{"summary":"Get background job progress","description":"Progress for one background job, scoped to the caller. `phase` uses the same vocabulary the web app's processing banner labels (validating, enriching, extracting, embeddings, researching, matching). Once the job links an opportunity, its title, company and match score are included. Another user's job id is a 404.","tags":["Jobs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Job ID"}],"responses":{"200":{"description":"Job status","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"job_type":{"type":"string"},"status":{"type":"string","enum":["queued","pending","processing","completed","failed"]},"phase":{"type":"string","nullable":true},"error":{"type":"string","nullable":true},"opportunity_id":{"type":"string","format":"uuid","nullable":true},"title":{"type":"string","nullable":true},"company":{"type":"string","nullable":true},"match_score":{"type":"integer","nullable":true},"already_tracked":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}}},"/opportunities/pending":{"get":{"summary":"List pending opportunities (intake review queue)","description":"List opportunities awaiting review — the intake queue (e.g. jobs forwarded to your intake email) not yet added or dismissed. Pass `status=suppressed` to read the near-miss pile instead: jobs discovery scored below the relevance floor, that triage memory recognised, or that were another copy of a job already pending (`dismissed_reason=pending_duplicate`), and filed away without showing them. Bounded: at most `limit` rows (default 50, max 200), with `meta.has_more` when a tail exists. Read-only.","tags":["Opportunities"],"parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","default":"pending"},"description":"Which pile to read: pending (default), suppressed, dismissed, expired, needs_attention, or `all`. Comma-separate to combine. An unknown value is a 400, not a silent fallback."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Max rows to return."}],"responses":{"200":{"description":"Pending review queue","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PendingOpportunity"}}}}},"401":{"description":"Unauthorized"}}}},"/opportunities/discovered":{"get":{"summary":"Get the Discovered feed","description":"The ranked discovery shortlist, the same payload the web app's Discovered view uses: `items` (pending discovery rows, highest relevance first), `near_miss` (a preview of up to 20 suppressed rows, below the relevance floor or over the daily cap), `on_deck_count` (the full promotable on-deck pool) and `today_count` (rows received since the start of the UTC day). Read-only.","tags":["Opportunities"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Max rows in `items`."}],"responses":{"200":{"description":"Discovered feed","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PendingOpportunity"}},"near_miss":{"type":"array","items":{"$ref":"#/components/schemas/PendingOpportunity"}},"on_deck_count":{"type":"integer"},"today_count":{"type":"integer"}}}}}},"401":{"description":"Unauthorized"},"403":{"description":"Missing read:opportunities scope"}}}},"/opportunities/pending/bulk":{"post":{"summary":"Bulk-triage pending opportunities","description":"Add and/or dismiss many pending review-queue items in one call. `add` ids are promoted to tracked opportunities (each starts enrichment); `dismiss` ids drop out of the queue. At least one of `add`/`dismiss` must be non-empty. Partial failures do not fail the call — each id gets a per-id outcome and the response summarizes added/dismissed/failed counts.","tags":["Opportunities"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"add":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Pending ids to promote to tracked opportunities"},"dismiss":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Pending ids to dismiss from the queue"}}}}}},"responses":{"200":{"description":"Per-id triage outcome","content":{"application/json":{"schema":{"type":"object","properties":{"added":{"type":"integer"},"dismissed":{"type":"integer"},"failed":{"type":"integer"},"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"action":{"type":"string","enum":["added","dismissed"]},"status":{"type":"string","enum":["ok","failed"]},"reason":{"type":"string"},"job_id":{"type":"string","format":"uuid"}}}}}}}}},"400":{"description":"No ids provided, batch too large, or add/dismiss overlap"},"401":{"description":"Unauthorized"}}}},"/opportunities/pending/{id}/add":{"post":{"summary":"Add a pending opportunity","description":"Promote a pending review-queue item to a tracked opportunity and start enrichment (scraping, extraction, company research, match scoring).","tags":["Opportunities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Pending opportunity ID"}],"responses":{"200":{"description":"Promoted; enrichment job queued","content":{"application/json":{"schema":{"type":"object","properties":{"pending_id":{"type":"string","format":"uuid"},"job_id":{"type":"string","format":"uuid"},"status":{"type":"string"},"title":{"type":"string","nullable":true},"company":{"type":"string","nullable":true}}}}}},"401":{"description":"Unauthorized"},"403":{"description":"Opportunity limit reached"},"404":{"description":"Not found"}}}},"/opportunities/pending/{id}/dismiss":{"post":{"summary":"Dismiss a pending opportunity","description":"Dismiss a pending review-queue item so it drops out of the intake queue. Does not affect tracked opportunities.","tags":["Opportunities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Pending opportunity ID"}],"responses":{"200":{"description":"Dismissed","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"dismissed":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}}},"/opportunities/pending/{id}/restore":{"post":{"summary":"Restore a dismissed pending opportunity","description":"Undo a dismissal: put a dismissed review-queue item back in the queue and clear the remembered dismissal, so the job is no longer auto-suppressed at intake. Only a currently-dismissed item can be restored.","tags":["Opportunities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Pending opportunity ID"}],"responses":{"200":{"description":"Restored","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"restored":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found, or not currently dismissed"}}}},"/opportunities/pending/{id}/rescue":{"post":{"summary":"Rescue a suppressed pending opportunity","description":"Overturn the system's suppression: put a near-miss (a job filed away as below the relevance floor, or by triage memory) back in the review queue, clear the suppression that would re-file it on the next discovery run, and append a `rescue` correction event carrying the score and reason the system had assigned. No event is recorded for an `over_daily_cap` or `pending_duplicate` row, which was never judged (`feedback_event_id` is null). Distinct from `/restore`, which undoes a *user's* dismissal and emits no event. Acts on `suppressed` and `expired` rows only.","tags":["Opportunities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Pending opportunity ID"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"surface":{"type":"string","enum":["api","web","mcp","inngest"],"description":"Which door the rescue came through. Recorded on the correction event."},"note":{"type":"string","maxLength":500,"description":"Why the suppression was wrong, in the user's words."}}}}}},"responses":{"200":{"description":"Rescued; correction event recorded","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"rescued":{"type":"boolean"},"previous_status":{"type":"string"},"relevance_score":{"type":"integer","nullable":true},"feedback_event_id":{"type":"string","format":"uuid","nullable":true}}}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found, or not currently suppressed"}}}},"/opportunities/pending/promote-next":{"post":{"summary":"Promote the next tranche from the daily on-deck pool","description":"\"Show me the next 12.\" Discovery surfaces at most `DISCOVERY_DAILY_CAP` rows per day, best-scored first; overflow that still cleared the relevance floor is held as a ranked on-deck pool (`suppressed` / `dismissed_reason=over_daily_cap`) instead of dropped. This moves the next best-scored N rows out of that pool and back into the review queue, repeatable until the pool is dry. Unlike `/rescue`, promoting is not a correction — these rows were never judged unfit, just queued behind better ones — so no preference-feedback event is recorded, and the call is not itself limited by the day's remaining budget.","tags":["Opportunities"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":"integer","minimum":1,"maximum":100,"description":"Tranche size. Defaults to `DISCOVERY_DAILY_CAP` (the size of one day's pace)."}}}}}},"responses":{"200":{"description":"Promoted","content":{"application/json":{"schema":{"type":"object","properties":{"promoted":{"type":"integer"},"promoted_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"remaining_on_deck":{"type":"integer","description":"How many over_daily_cap rows are still waiting after this call."}}}}}},"401":{"description":"Unauthorized"}}}},"/discovery/query-health":{"get":{"summary":"Per-query discovery health","description":"Rolling-window yield for each saved discovery query: how much it fetched, how much reached the review queue, how much was suppressed, the score distribution, and a verdict (`healthy`, `noisy`, `dry`, `failing`, `insufficient_data`) with a one-sentence recommendation. A computed resource, not a page — the web Job Sources section, the MCP `get_discovery_query_health` tool and an agent proposing tighter queries all read this. `has_data: false` means discovery has not run in the window; it does not mean everything is healthy.","tags":["Discovery"],"parameters":[{"name":"window_days","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":90,"default":14},"description":"Rolling window. A verdict needs 3 runs behind it."},{"name":"source","in":"query","required":false,"schema":{"type":"string","enum":["linkedin","google_jobs","fractionaljobs"]},"description":"Only report queries run by this source."}],"responses":{"200":{"description":"Per-query health over the window","content":{"application/json":{"schema":{"type":"object","properties":{"window_days":{"type":"integer"},"since":{"type":"string","format":"date-time"},"runs_observed":{"type":"integer"},"has_data":{"type":"boolean"},"queries":{"type":"array","items":{"$ref":"#/components/schemas/DiscoveryQueryHealth"}}}}}}},"400":{"description":"Unknown source"},"401":{"description":"Unauthorized"}}}},"/discovery/feedback":{"get":{"summary":"Read the correction-event stream","description":"Append-only record of every user override of a discovery or scoring decision — rescues, dismissals of high-scoring items, threshold and query edits, explicit disagreement with a suppression reason. Newest first, bounded (default 50, max 200). The calibration input for the reflection loop.","tags":["Discovery"],"parameters":[{"name":"event_type","in":"query","required":false,"schema":{"type":"string"},"description":"Comma-separated filter: rescue, high_score_dismissal, threshold_change, query_edit, suppression_disagreement."},{"name":"since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only events after this ISO 8601 timestamp."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Correction events, newest first","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PreferenceFeedbackEvent"}}}}},"400":{"description":"Unknown event_type, or malformed since"},"401":{"description":"Unauthorized"}}},"post":{"summary":"Record a correction event","description":"The one write path for corrections that have no verb of their own — an explicit disagreement with a suppression reason, or a threshold change. Verbs that already imply a correction (`/rescue`, `/dismiss`, a `discovery_queries` edit through `/preferences`) emit their own event server-side; posting a second one here double-counts it.","tags":["Discovery"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["event_type"],"properties":{"event_type":{"type":"string","enum":["rescue","high_score_dismissal","threshold_change","query_edit","suppression_disagreement"]},"subject_type":{"type":"string","enum":["pending_opportunity","discovery_query","search_preferences"]},"subject_id":{"type":"string"},"surface":{"type":"string","enum":["api","web","mcp","inngest"]},"payload":{"type":"object"}}}}}},"responses":{"200":{"description":"Recorded","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"event_type":{"type":"string"},"recorded":{"type":"boolean"}}}}}},"400":{"description":"Unknown event_type or malformed body"},"401":{"description":"Unauthorized"}}}},"/opportunities/{id}":{"get":{"summary":"Get opportunity","tags":["Opportunities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Opportunity detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Opportunity"}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}},"patch":{"summary":"Update opportunity","tags":["Opportunities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["tracking","applied","interviewing","offer","rejected","archived"]},"url_status":{"type":"string"},"is_starred":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated opportunity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Opportunity"}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}}},"/opportunities/{id}/match":{"get":{"summary":"Get opportunity match analysis","description":"Returns match scores and strength/gap analysis for an opportunity.","tags":["Opportunities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Match result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchResult"}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}}},"/opportunities/{id}/tailor":{"post":{"summary":"Generate tailored profile","description":"Triggers async generation of a tailored profile for this opportunity.","tags":["Opportunities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"regenerate":{"type":"boolean","description":"Force regeneration of existing profile"}}}}}},"responses":{"200":{"description":"Tailoring job started","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsyncJob"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Tailored profile limit reached"},"404":{"description":"Not found"}}}},"/opportunities/{id}/tailored-profile":{"get":{"summary":"Get tailored profile","description":"Returns the tailored profile for an opportunity.","tags":["Opportunities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Tailored profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TailoredProfile"}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}}},"/opportunities/{id}/share":{"post":{"summary":"Create share link","description":"Creates a shareable link to a tailored profile. Requires tailored profile to exist.","tags":["Opportunities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"expires_in_days":{"type":"integer","description":"Days until expiry (default: 30)"}}}}}},"responses":{"200":{"description":"Share link created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareLink"}}}},"401":{"description":"Unauthorized"},"404":{"description":"Opportunity or tailored profile not found"}}}},"/opportunities/add-and-tailor":{"post":{"summary":"Add opportunity and tailor","description":"Adds an opportunity and immediately starts tailoring. Combines POST /opportunities and POST /opportunities/:id/tailor.","tags":["Opportunities"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["description"],"properties":{"url":{"type":"string"},"description":{"type":"string"}}}}}},"responses":{"200":{"description":"Opportunity created and tailoring started","content":{"application/json":{"schema":{"type":"object","properties":{"opportunity":{"$ref":"#/components/schemas/Opportunity"},"job_id":{"type":"string"},"status":{"type":"string"},"message":{"type":"string"}}}}}},"401":{"description":"Unauthorized"},"403":{"description":"Tailored profile limit reached"}}}},"/opportunities/add-tailor-share":{"post":{"summary":"Add, tailor, and share","description":"All-in-one: adds opportunity, starts tailoring, and queues share link creation.","tags":["Opportunities"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["description"],"properties":{"url":{"type":"string"},"description":{"type":"string"}}}}}},"responses":{"200":{"description":"Opportunity created and processing started","content":{"application/json":{"schema":{"type":"object","properties":{"opportunity":{"$ref":"#/components/schemas/Opportunity"},"job_id":{"type":"string"},"status":{"type":"string"},"message":{"type":"string"}}}}}},"401":{"description":"Unauthorized"},"403":{"description":"Tailored profile limit reached"}}}},"/opportunities/bulk":{"post":{"summary":"Bulk import opportunities","description":"Import multiple opportunities from CSV, JSON, or plain text. Auto-detects format.","tags":["Opportunities"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["input"],"properties":{"input":{"type":"string","description":"CSV, JSON array, or newline-separated text of opportunities"}}}}}},"responses":{"200":{"description":"Import results","content":{"application/json":{"schema":{"type":"object","properties":{"imported":{"type":"integer"},"duplicates_skipped":{"type":"integer"},"invalid_skipped":{"type":"integer"},"total_processed":{"type":"integer"},"details":{"type":"object","properties":{"duplicates":{"type":"array","items":{"type":"string"}},"errors":{"type":"array","items":{"type":"string"}}}}}}}}},"401":{"description":"Unauthorized"}}}},"/opportunities/bulk-update":{"patch":{"summary":"Bulk update opportunity status","description":"Update the status of multiple opportunities at once (max 100).","tags":["Opportunities"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ids","status"],"properties":{"ids":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":100},"status":{"type":"string","enum":["tracking","applied","interviewing","offer","rejected","archived"]}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"updated":{"type":"integer"},"ids":{"type":"array","items":{"type":"string","format":"uuid"}}}}}}},"401":{"description":"Unauthorized"}}},"delete":{"summary":"Bulk delete opportunities","description":"Delete multiple opportunities at once (max 100).","tags":["Opportunities"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ids"],"properties":{"ids":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":100}}}}}},"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer"},"ids":{"type":"array","items":{"type":"string","format":"uuid"}}}}}}},"401":{"description":"Unauthorized"}}}},"/tailored-profile/{opportunityId}":{"patch":{"summary":"Edit tailored profile field","description":"Edit a specific field of a tailored profile. Supports direct value or AI-assisted rewrite via instruction.","tags":["Tailored Profiles"],"parameters":[{"name":"opportunityId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["field"],"properties":{"field":{"type":"string","description":"Field path (supports dot notation for nested fields)"},"value":{"description":"Direct replacement value"},"instruction":{"type":"string","description":"AI rewrite instruction (alternative to value)"},"selection":{"type":"object","properties":{"start":{"type":"integer"},"end":{"type":"integer"}},"description":"Character range for partial edits"}}}}}},"responses":{"200":{"description":"Field updated","content":{"application/json":{"schema":{"type":"object","properties":{"field":{"type":"string"},"value":{},"wasAiGenerated":{"type":"boolean"}}}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}},"post":{"summary":"Revert tailored profile field","description":"Revert an edited field to its original AI-generated value.","tags":["Tailored Profiles"],"parameters":[{"name":"opportunityId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["field"],"properties":{"field":{"type":"string","description":"Field path to revert"}}}}}},"responses":{"200":{"description":"Field reverted","content":{"application/json":{"schema":{"type":"object","properties":{"field":{"type":"string"},"value":{}}}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}}},"/preferences":{"get":{"summary":"Get preferences","description":"Returns career preferences (industries, company sizes, role levels, etc.).","tags":["Preferences"],"responses":{"200":{"description":"User preferences","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Preferences"}}}},"401":{"description":"Unauthorized"}}},"put":{"summary":"Update preferences","description":"Replace all career preferences. Unset fields default to empty arrays.","tags":["Preferences"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Preferences"}}}},"responses":{"200":{"description":"Updated preferences","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Preferences"}}}},"401":{"description":"Unauthorized"}}}},"/preferences/extract":{"post":{"summary":"Extract preferences from text","description":"Uses AI to parse career preferences from a natural language description.","tags":["Preferences"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["description"],"properties":{"description":{"type":"string","maxLength":2000,"description":"Natural language preference description"}}}}}},"responses":{"200":{"description":"Extracted preferences","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Preferences"}}}},"401":{"description":"Unauthorized"}}}},"/site":{"get":{"summary":"Get public site settings","description":"Returns the owner's public site (or null), a suggested handle when there is no site, whether the profile has a name, and the sites domain.","tags":["Site"],"responses":{"200":{"description":"Site settings","content":{"application/json":{"schema":{"type":"object","properties":{"site":{"allOf":[{"$ref":"#/components/schemas/SiteSettings"}],"nullable":true},"suggested_handle":{"type":"string","nullable":true},"profile_ready":{"type":"boolean"},"sites_domain":{"type":"string"}}}}}},"401":{"description":"Unauthorized"}}},"post":{"summary":"Claim a site address","description":"Claims the first handle and creates the site, unpublished.","tags":["Site"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["handle"],"properties":{"handle":{"type":"string"}}}}}},"responses":{"201":{"description":"The new site","content":{"application/json":{"schema":{"type":"object","properties":{"site":{"$ref":"#/components/schemas/SiteSettings"}}}}}},"400":{"description":"Validation error"},"401":{"description":"Unauthorized"},"409":{"description":"handle_taken or site_exists"},"422":{"description":"handle_invalid or handle_reserved"}}},"patch":{"summary":"Update the site","description":"Changes any of the handle, publish state, sections and search visibility. Publishing requires a profile name.","tags":["Site"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"handle":{"type":"string"},"published":{"type":"boolean"},"sections":{"type":"object","additionalProperties":false,"properties":{"career":{"type":"boolean"},"projects":{"type":"boolean"},"resume":{"type":"boolean"},"fit_check":{"type":"boolean"},"ask":{"type":"boolean"},"contact":{"type":"boolean"}}},"show_in_search":{"type":"boolean"}}}}}},"responses":{"200":{"description":"The updated site","content":{"application/json":{"schema":{"type":"object","properties":{"site":{"$ref":"#/components/schemas/SiteSettings"}}}}}},"400":{"description":"Validation error"},"401":{"description":"Unauthorized"},"404":{"description":"site_not_found"},"409":{"description":"handle_taken"},"422":{"description":"handle_invalid, handle_reserved or profile_name_required"}}}},"/site/handle-check":{"get":{"summary":"Check a site address","description":"Normalizes a handle and says whether the caller can use it.","tags":["Site"],"parameters":[{"name":"handle","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Availability","content":{"application/json":{"schema":{"type":"object","properties":{"handle":{"type":"string"},"available":{"type":"boolean"},"reason":{"type":"string","nullable":true,"enum":["current","invalid_format","reserved","taken"]}}}}}},"400":{"description":"Missing handle"},"401":{"description":"Unauthorized"}}}},"/site/preview":{"get":{"summary":"Preview the site","description":"Returns exactly what a visitor would see, plus site-hidden items flagged hidden. Works while unpublished.","tags":["Site"],"responses":{"200":{"description":"Preview content","content":{"application/json":{"schema":{"type":"object","properties":{"preview":{"type":"object"}}}}}},"401":{"description":"Unauthorized"},"404":{"description":"site_not_found"}}}},"/site/hidden-items":{"put":{"summary":"Hide or show an item on the site","description":"Hides an item from the public site only, or shows it again. Idempotent.","tags":["Site"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["item_type","item_id","hidden"],"properties":{"item_type":{"type":"string","enum":["claim","work_history","venture","project","evidence"]},"item_id":{"type":"string","format":"uuid"},"hidden":{"type":"boolean"}}}}}},"responses":{"200":{"description":"The item's new state","content":{"application/json":{"schema":{"type":"object","properties":{"item_type":{"type":"string"},"item_id":{"type":"string","format":"uuid"},"hidden":{"type":"boolean"}}}}}},"400":{"description":"Validation error"},"401":{"description":"Unauthorized"},"404":{"description":"site_not_found or item_not_found"}}}},"/usage":{"get":{"summary":"Get usage statistics","description":"Returns API key info and resource counts.","tags":["Account"],"responses":{"200":{"description":"Usage info","content":{"application/json":{"schema":{"type":"object","properties":{"api_keys":{"type":"array","items":{"type":"object","properties":{"prefix":{"type":"string"},"name":{"type":"string"},"last_used_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"}}}},"counts":{"type":"object","properties":{"documents":{"type":"integer"},"opportunities":{"type":"integer"},"active_share_links":{"type":"integer"}}}}}}}},"401":{"description":"Unauthorized"}}}},"/account/export":{"post":{"summary":"Export account data","description":"Exports all user data as a ZIP archive containing a JSON file and uploaded documents.","tags":["Account"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"ZIP archive of all user data","content":{"application/zip":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Unauthorized"}}}},"/account":{"delete":{"summary":"Delete account","description":"Permanently deletes the user account and all associated data. Requires password confirmation.","tags":["Account"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["password","confirmation"],"properties":{"password":{"type":"string","description":"Current account password"},"confirmation":{"type":"string","description":"Must be exactly \"DELETE MY ACCOUNT\""}}}}}},"responses":{"204":{"description":"Account deleted"},"400":{"description":"Invalid confirmation or password"},"401":{"description":"Unauthorized"}}}},"/shared/{token}/summary":{"get":{"summary":"Get AI candidate summary (public)","tags":["Shared"],"security":[],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"AI-generated summary","content":{"application/json":{"schema":{"type":"object","properties":{"candidate_name":{"type":"string"},"summary":{"type":"string"},"generated_at":{"type":"string","format":"date-time"}}}}}},"404":{"description":"Not found"},"410":{"description":"Expired or revoked"}}}}}}