{"openapi":"3.1.0","info":{"title":"Callboard API","description":"AI-powered concert booking brief engine","version":"0.1.0"},"paths":{"/admin/waitlist":{"get":{"tags":["admin"],"summary":"Admin Waitlist","description":"Return all waitlist entries for admin dashboard.","operationId":"admin_waitlist_admin_waitlist_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users":{"get":{"tags":["admin"],"summary":"Admin Users","description":"Return all users for admin dashboard.","operationId":"admin_users_admin_users_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/exit-surveys":{"get":{"tags":["admin"],"summary":"Admin Exit Surveys","description":"Return exit survey responses for admin dashboard.","operationId":"admin_exit_surveys_admin_exit_surveys_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/day15-feedback":{"get":{"tags":["admin"],"summary":"Admin Day15 Feedback","description":"Return Day 15 feedback responses with Q3 average for admin dashboard.","operationId":"admin_day15_feedback_admin_day15_feedback_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/beta-emails":{"get":{"tags":["admin"],"summary":"Admin Beta Emails","description":"Return sent beta lifecycle emails for admin dashboard.","operationId":"admin_beta_emails_admin_beta_emails_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/cohort-stats":{"get":{"tags":["admin"],"summary":"Admin Cohort Stats","description":"Return per-cohort beta metrics for the cohort tracking dashboard.","operationId":"admin_cohort_stats_admin_cohort_stats_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/cron/recovery-emails":{"post":{"tags":["admin"],"summary":"Admin Run Recovery Emails","description":"Manually trigger the recovery email scheduler.\n\nSame code path as the standalone cron entrypoint\n(`python -m app.cron.recovery_emails`). Returns the run summary so the\ncaller can see how many emails were considered / sent / skipped.\n\nUseful for: smoke-testing in prod without waiting for the next cron tick,\nor running on-demand from an external trigger that doesn't use Railway's\nnative cron.","operationId":"admin_run_recovery_emails_admin_cron_recovery_emails_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/cron/digest-compute":{"post":{"tags":["admin"],"summary":"Admin Run Digest Compute","description":"Manually trigger the Market Digest compute phase (watchlist lane).\n\nSame code path as `python -m app.cron.digest_compute`. Returns the run\nsummary (profiles processed, candidates scored, rows queued for research) so\nthe caller can smoke-test in dev/prod without waiting for the cron tick.\nHonors DIGEST_COMPUTE_TEST_PROFILE for a single-profile run.","operationId":"admin_run_digest_compute_admin_cron_digest_compute_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/cron/digest-confirm":{"post":{"tags":["admin"],"summary":"Admin Run Digest Confirm","description":"Manually trigger the Market Digest confirm phase (the API fallback).\n\nSame code path as `python -m app.cron.digest_confirm`. Clears 'needs_research'\nstragglers the local booking-scout loop didn't reach, writing verdicts through\nthe same state machine. Returns the run summary (rows processed, confirmed /\nkilled) so you can smoke-test in dev/prod without waiting for the cron tick.\n\nOFF BY DEFAULT — set USE_DIGEST_API_CONFIRM=true to arm it. Honors\nDIGEST_CONFIRM_TEST_PROFILE for a single-profile run and DIGEST_DRY_RUN for a\nwrite-nothing preview.","operationId":"admin_run_digest_confirm_admin_cron_digest_confirm_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/cron/digest-deliver":{"post":{"tags":["admin"],"summary":"Admin Run Digest Deliver","description":"Manually trigger the Market Digest deliver phase (the Monday email).\n\nSame code path as `python -m app.cron.digest_deliver`. Sends each entitled\nprofile's digest of confirmed watchlist artists; profiles with nothing\nconfirmed get no email; once-only per artist via digest_runs delivered_ids.\nReturns the run summary so you can smoke-test in dev/prod without waiting\nfor the cron tick.\n\nHonors DIGEST_DELIVER_TEST_PROFILE (single-profile run, bypasses the\nentitlement gate), DIGEST_DELIVER_TEST_RECIPIENT (reroute all email +\nskip the once-only recording), and DIGEST_DRY_RUN (render, send nothing).","operationId":"admin_run_digest_deliver_admin_cron_digest_deliver_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/cron/annotation-queue":{"post":{"tags":["admin"],"summary":"Admin Run Annotation Queue","description":"Manually trigger the brief-review annotation-queue router.\n\nSame code path as `python -m app.cron.annotation_queue`. Read-mostly scan of\nrecent brief_pipeline traces that routes errored / low-rated / venue-out-of-\nband / internal-terms briefs into the LangSmith annotation queue. Returns the\nrun summary (candidates, per-signal counts, added) so you can smoke-test the\nselection without waiting for the cron tick.\n\nDRY-RUN BY DEFAULT — set ANNOTATION_QUEUE_DRY_RUN=false to actually populate\nthe queue once a dry-run's selection looks right.","operationId":"admin_run_annotation_queue_admin_cron_annotation_queue_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/me/onboarding":{"get":{"tags":["onboarding"],"summary":"Get Onboarding","description":"Hydrate the onboarding screen on load.\n\nReturns the user's current onboarding status + step + their persisted\nmarket profile (or null if they haven't started). The frontend uses this\nto resume — close the browser on Step 4, come back later, land on Step 4.","operationId":"get_onboarding_me_onboarding_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["onboarding"],"summary":"Patch Onboarding","description":"Partial update to onboarding state + market profile.\n\nCalled on every step's \"Continue\" with the step's slice of state. The\nfrontend can also use this to bump the current_step on Save & Exit.\n\nWhen status flips to 'complete', this also writes\npublicMetadata.onboarding_complete=true on the Clerk user so the\nmiddleware will let them past /onboarding on their next request.","operationId":"patch_onboarding_me_onboarding_patch","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/metro-waitlist":{"post":{"tags":["onboarding"],"summary":"Join Metro Waitlist","description":"Capture an unsupported-metro request from Step 2.\n\nPublic-ish — auth is optional. A signed-in user (post-payment, blocked\nat metro selection) gets linked via user_id so we can notify them when\nwe ship their market.","operationId":"join_metro_waitlist_metro_waitlist_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MetroWaitlistRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/onboarding/preview-digest":{"post":{"tags":["onboarding"],"summary":"Preview Digest","description":"Step 8 — return 3 artist IDs matched to the user's just-built profile.\n\nStaged: matches against a small in-memory pool, not real Chartmetric.\nThe client renders the loading sequence (8-15s) regardless of how long\nthis takes — the endpoint is fast on purpose so we don't introduce a\nspurious latency budget that production matching has to fit inside.","operationId":"preview_digest_onboarding_preview_digest_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/me/watchlist":{"get":{"tags":["watchlist"],"summary":"Get Watchlist","description":"Active watchlist for the caller. A caller with no local user row yet, or no\nmarket profile, gets an empty list (the UI renders the empty state) — not an\nerror: there's simply nothing saved.","operationId":"get_watchlist_me_watchlist_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["watchlist"],"summary":"Add To Watchlist","description":"Add an artist to the watchlist (idempotent — see add_saved_artist).","operationId":"add_to_watchlist_me_watchlist_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedArtistCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/me/watchlist/{cm_artist_id}":{"delete":{"tags":["watchlist"],"summary":"Remove From Watchlist","description":"Dismiss an artist (soft-delete). 404 when it isn't on the watchlist.","operationId":"remove_from_watchlist_me_watchlist__cm_artist_id__delete","parameters":[{"name":"cm_artist_id","in":"path","required":true,"schema":{"type":"integer","exclusiveMinimum":0,"title":"Cm Artist Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/me":{"get":{"summary":"Get Me","description":"Return current user's role and access level from Clerk JWT.","operationId":"get_me_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/brief":{"post":{"summary":"Create Brief","description":"Stream a Promoter Brief for the given natural language query.\n\nEvents emitted via SSE:\n- `{\"type\": \"agent_complete\", \"agent\": \"<name>\", \"data\": {...}}`\n- `{\"type\": \"brief_complete\", \"data\": {PromoterBrief}}`\n- `{\"type\": \"stream_end\"}`","operationId":"create_brief_brief_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/briefs":{"get":{"summary":"List Briefs Endpoint","description":"List briefs for the authenticated user.","operationId":"list_briefs_endpoint_briefs_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"default":20,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/briefs/{brief_id}":{"get":{"summary":"Get Brief Endpoint","description":"Get a single brief by ID. Access rules:\n\n- Admins can fetch any brief (used by the admin dashboard's per-brief link).\n- The brief's owner (briefs.user_id == caller.id) can fetch their own.\n- Members of a paid team can fetch briefs scoped to that team\n  (briefs.team_id == caller.team_id, both non-null).\n- Everyone else — including unauthenticated callers and other signed-in\n  users who aren't on the brief's team — gets 404. We deliberately don't\n  distinguish \"not found\" from \"you can't see it\" so brief IDs don't\n  leak existence to people who shouldn't see them.\n\nTier-locked fields (pricing, risk_output, langsmith_run_id) are still\nstripped per the caller's access tier inside _filter_brief_for_access.","operationId":"get_brief_endpoint_briefs__brief_id__get","parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","title":"Brief Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/briefs/public/{brief_id}":{"get":{"summary":"Get Public Brief Endpoint","description":"Anonymous read of a brief that has been explicitly published.\n\nLookup is keyed on (id, is_public=true), so unpublished briefs return\n404 even with a valid id — no signal leaks about whether the id maps\nto a real-but-private brief. Output is filtered through the existing\n\"full\" access tier so promoter viewers see pricing, risk_output, and\nrevenue, but identity fields (user_id, team_id, langsmith_run_id,\nagent_errors) are stripped before responding.","operationId":"get_public_brief_endpoint_briefs_public__brief_id__get","parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","title":"Brief Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/history":{"get":{"summary":"List History Endpoint","description":"List query history for the authenticated user.","operationId":"list_history_endpoint_history_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"default":50,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/waitlist":{"post":{"summary":"Join Waitlist","description":"Add to the waitlist with qualifier fields (upserts on email).","operationId":"join_waitlist_waitlist_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaitlistRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/feedback/exit-survey":{"post":{"summary":"Submit Exit Survey","description":"Store an exit survey response from a churned beta user.","operationId":"submit_exit_survey_feedback_exit_survey_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExitSurveyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/feedback/day15":{"post":{"summary":"Submit Day15 Feedback","description":"Store Day 15 mid-beta feedback from an eligible user.","operationId":"submit_day15_feedback_feedback_day15_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Day15FeedbackRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/feedback/brief":{"post":{"summary":"Submit Brief Feedback","description":"Store per-brief feedback from an authenticated user.\n\nSame access gate as GET /briefs/{brief_id}: only the brief's owner,\npaid-team members of the brief's team, or admins may submit feedback.\nWithout this check any signed-in user could pollute feedback rows for\narbitrary briefs they've never seen (UNIQUE(brief_id, user_id) blocks\nduplicates from the same user but doesn't stop a user from spamming\nmany distinct brief IDs).\n\nReturns 404 (not 403) on access denial — same semantics as the read\nendpoint, so brief existence isn't leaked.","operationId":"submit_brief_feedback_feedback_brief_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefFeedbackRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/feedback/brief/{brief_id}":{"get":{"summary":"Get Brief Feedback Status","description":"Check if the current user has already submitted feedback for a brief.","operationId":"get_brief_feedback_status_feedback_brief__brief_id__get","parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","title":"Brief Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/convert":{"post":{"summary":"Convert To Paid","description":"Convert the current beta user to a paid tier.","operationId":"convert_to_paid_convert_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConvertRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/monitor":{"post":{"summary":"Create Monitor","description":"Create an artist monitor — notify when data improves enough for a reliable brief.","operationId":"create_monitor_monitor_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonitorRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/artists/search":{"get":{"summary":"Artists Search","description":"Lightweight artist search for the disambiguation UI re-search flow.\n\nReturns all matching artists without running the full brief pipeline.","operationId":"artists_search_api_artists_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"title":"Q"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"summary":"Health","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/admin/api-keys":{"get":{"summary":"Admin Api Keys","description":"Check which external API keys are configured (admin only).","operationId":"admin_api_keys_admin_api_keys_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/resend-inbound":{"post":{"summary":"Resend Inbound Webhook","description":"Receive Resend inbound-parse webhook and auto-acknowledge the sender.\n\nIdempotent within reason: if the inbound payload itself is an\nauto-responder bounce-back (Auto-Submitted header set), we skip the\nack to avoid loops. Failures are logged but the endpoint always returns\n200 so Resend's retry queue doesn't keep firing on a malformed payload.","operationId":"resend_inbound_webhook_webhooks_resend_inbound_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"BriefFeedbackRequest":{"properties":{"brief_id":{"type":"string","title":"Brief Id"},"rating":{"type":"integer","title":"Rating"},"comment":{"type":"string","title":"Comment","default":""}},"type":"object","required":["brief_id","rating"],"title":"BriefFeedbackRequest"},"BriefRequest":{"properties":{"query":{"type":"string","title":"Query"},"force_low_confidence":{"type":"boolean","title":"Force Low Confidence","default":false},"force_refresh":{"type":"boolean","title":"Force Refresh","default":false}},"type":"object","required":["query"],"title":"BriefRequest"},"ConvertRequest":{"properties":{"tier":{"type":"string","title":"Tier","default":"buyer"}},"type":"object","title":"ConvertRequest"},"Day15FeedbackRequest":{"properties":{"q1_useful":{"type":"string","title":"Q1 Useful"},"q2_missing":{"type":"string","title":"Q2 Missing","default":""},"q3_pay":{"type":"integer","title":"Q3 Pay"}},"type":"object","required":["q1_useful","q3_pay"],"title":"Day15FeedbackRequest"},"ExitSurveyRequest":{"properties":{"response":{"type":"string","title":"Response"}},"type":"object","required":["response"],"title":"ExitSurveyRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"MarketProfilePatch":{"properties":{"primary_market":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Primary Market"},"rooms":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Rooms"},"genres_selected":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Genres Selected"},"genres_excluded":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Genres Excluded"},"guarantee_lo":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Guarantee Lo"},"guarantee_hi":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Guarantee Hi"},"lead_time_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Lead Time Days"},"secondary_markets":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Secondary Markets"},"radius_miles":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Radius Miles"},"digest_day":{"anyOf":[{"type":"integer","maximum":6.0,"minimum":0.0},{"type":"null"}],"title":"Digest Day"},"digest_hour":{"anyOf":[{"type":"integer","maximum":23.0,"minimum":0.0},{"type":"null"}],"title":"Digest Hour"},"digest_tz":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Digest Tz"},"digest_recipients":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Digest Recipients"},"listener_floor":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Listener Floor"},"listener_ceiling":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Listener Ceiling"}},"type":"object","title":"MarketProfilePatch"},"MetroWaitlistRequest":{"properties":{"email":{"type":"string","title":"Email"},"requested_metro":{"type":"string","title":"Requested Metro"},"requested_country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requested Country"}},"type":"object","required":["email","requested_metro"],"title":"MetroWaitlistRequest"},"MonitorRequest":{"properties":{"artist_name":{"type":"string","title":"Artist Name"},"market":{"type":"string","title":"Market"},"listener_threshold":{"type":"integer","title":"Listener Threshold","default":100000}},"type":"object","required":["artist_name","market"],"title":"MonitorRequest"},"OnboardingPatch":{"properties":{"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"current_step":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Step"},"market_profile":{"anyOf":[{"$ref":"#/components/schemas/MarketProfilePatch"},{"type":"null"}]},"exited":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Exited"}},"type":"object","title":"OnboardingPatch"},"SavedArtistCreate":{"properties":{"cm_artist_id":{"type":"integer","exclusiveMinimum":0.0,"title":"Cm Artist Id"},"artist_name":{"type":"string","minLength":1,"title":"Artist Name"}},"type":"object","required":["cm_artist_id","artist_name"],"title":"SavedArtistCreate"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WaitlistRequest":{"properties":{"email":{"type":"string","title":"Email"},"markets":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Markets"},"volume":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Volume"},"tools":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tools"}},"type":"object","required":["email"],"title":"WaitlistRequest"}}}}