{"openapi":"3.0.3","info":{"title":"InvestorMatch.Pro API","version":"1.0.0","description":"Public REST API for InvestorMatch.Pro. Upload a pitch deck for AI analysis, retrieve job status, and fetch ranked investor matches. All endpoints are authenticated with a Bearer API key (im_live_… for production, im_test_… for test mode). The paid deck/campaign endpoints are credit-metered and rate-limited; the blog endpoint is a separate, admin-only, free, unmetered publishing channel.","contact":{"name":"InvestorMatch.Pro","url":"https://investormatch.pro"}},"servers":[{"url":"https://investormatch.pro","description":"Production"}],"tags":[{"name":"Analysis","description":"Paid deck-analysis endpoints. Usable by any valid API key. Credit-metered and rate-limited (60 req/min, 1,000 req/day on paid tier)."},{"name":"Blog","description":"Admin-only blog publishing. Requires an API key owned by an admin; any other valid key receives 403. Free, unmetered, and not rate-limited. The only programmatic blog action is creating a draft — listing, editing, and publishing happen in the admin dashboard, not via the API."},{"name":"Credits","description":"Check your credit balance and free-quota usage, list purchasable bundles, and start a Stripe checkout to top up. Authenticated and rate-limited; these calls do not themselves consume analysis credits."},{"name":"API Keys","description":"Programmatically create, list, and revoke your own API keys. Authenticated with an existing key and rate-limited; these calls do not consume analysis credits."},{"name":"Auto-Refill","description":"Read and update your automatic credit top-up settings (threshold, amount, monthly cap, and saved payment method). Authenticated and rate-limited."},{"name":"Investors","description":"Read-only access to the verified investor database — search firms and fetch a firm's partners and portfolio. Authenticated and rate-limited; does not consume analysis credits."}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Pass your API key as a Bearer token: `Authorization: Bearer im_live_…` (production) or `Authorization: Bearer im_test_…` (test mode, no credits charged)."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","example":"Bad Request"},"message":{"type":"string","example":"Invalid request body"},"details":{"type":"array","items":{"type":"object"},"description":"Optional field-level validation issues."}},"required":["error","message"]},"AnalyzeRequest":{"type":"object","description":"Submit either a multipart `deck` file (PDF) OR a `deck_url`. When sending a file, use multipart/form-data; otherwise application/json with deck_url.","properties":{"deck_url":{"type":"string","format":"uri","description":"Public URL to a pitch deck (PDF). Provide this OR a `deck` file."},"target_check_size":{"type":"string","description":"Optional target check size, e.g. \"$500k\"."},"webhook_url":{"type":"string","format":"uri","description":"Optional URL notified when analysis completes."},"contact_email":{"type":"string","format":"email","description":"Optional founder contact email."}}},"AnalyzeResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"job_id":{"type":"string","example":"job_abc123"},"status":{"type":"string","example":"pending"},"estimated_time_seconds":{"type":"integer","example":120}}},"JobStatusResponse":{"type":"object","properties":{"job_id":{"type":"string"},"status":{"type":"string","enum":["pending","processing","completed","failed"]},"created_at":{"type":"string","format":"date-time"},"completed_at":{"type":"string","format":"date-time","nullable":true},"match_count":{"type":"integer","nullable":true},"error":{"type":"string","nullable":true}}},"MatchPartner":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string","nullable":true},"linkedin_url":{"type":"string","nullable":true},"role":{"type":"string","nullable":true}}},"Match":{"type":"object","properties":{"rank":{"type":"integer"},"firm_name":{"type":"string"},"fit_score":{"type":"number"},"fit_reasoning":{"type":"string"},"check_size_min":{"type":"number","nullable":true},"check_size_max":{"type":"number","nullable":true},"stages":{"type":"array","items":{"type":"string"}},"sectors":{"type":"array","items":{"type":"string"}},"website":{"type":"string","nullable":true},"partners":{"type":"array","items":{"$ref":"#/components/schemas/MatchPartner"}}}},"MatchesResponse":{"type":"object","properties":{"matches":{"type":"array","items":{"$ref":"#/components/schemas/Match"}}}},"BlogPostRequest":{"type":"object","required":["title","content"],"description":"Create a blog post DRAFT. Field names are snake_case. HTML in `content` is sanitized server-side to a safe subset of tags. Posts are always created as drafts and never auto-published. Do not send `status` (or set it to \"draft\"). The article body must meet the GEO content standard or the request is rejected with 400 and nothing is saved:\n- Any article of 800+ words must be broken into at least 5 <h2> sections (the FAQ heading counts as one).\n- Every article must end with an <h2>Frequently Asked Questions</h2> section containing 3-5 Q&As, each question in an <h3> and each answer in the <p> that follows it.\n- Every FAQ answer must be at least 40 characters — a full self-contained sentence an answer engine can quote.\n- Every statistic or data claim (a percentage, a dollar figure, a ratio, a median/average) must carry an inline source citation in the same sentence: a link to the source, \"according to <publication>\", or a \"(Source: Publication, Year)\" parenthetical.\n- Numbers inside double quotes are read as illustrative examples (a sample founder script, say) and are exempt from the citation rule — do not use quotes to smuggle a real claim past it.\n- FAQPage JSON-LD is generated automatically from the on-page FAQ section — do not hand-write a schema block; it will be ignored and can conflict with the generated one.","properties":{"title":{"type":"string","minLength":3,"maxLength":200},"content":{"type":"string","minLength":1,"maxLength":80000,"description":"Article HTML. Sanitized server-side; <h1> demoted to <h2>. Must carry at least 5 <h2> sections once it reaches 800 words, a closing \"<h2>Frequently Asked Questions</h2>\" section of 3-5 <h3>question</h3><p>answer</p> pairs (the FAQPage JSON-LD is generated from it), and an inline source citation on every statistic."},"excerpt":{"type":"string","maxLength":500},"slug":{"type":"string","maxLength":80,"description":"Optional. Auto-generated from the title if omitted."},"meta_title":{"type":"string","maxLength":200},"meta_description":{"type":"string","maxLength":320},"category":{"type":"string","maxLength":80},"tags":{"type":"array","maxItems":15,"items":{"type":"string","minLength":1,"maxLength":40}},"featured_image":{"type":"string","maxLength":2000,"description":"Optional. Must be a valid http(s) URL."},"featured_image_alt":{"type":"string","maxLength":300},"og_image":{"type":"string","maxLength":2000,"description":"Optional. Must be a valid http(s) URL."},"author_name":{"type":"string","maxLength":120},"author_title":{"type":"string","maxLength":160},"author_bio":{"type":"string","maxLength":1000},"ai_prompt":{"type":"string","maxLength":5000}}},"BlogPostResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"id":{"type":"integer","example":123},"slug":{"type":"string","example":"ai-reshaping-seed-fundraising"},"status":{"type":"string","example":"draft"},"message":{"type":"string","example":"Draft created. An admin must review and publish it before it appears publicly."},"preview_url":{"type":"string","format":"uri"}}},"CreditBalance":{"type":"object","properties":{"balance":{"type":"integer","description":"Purchased credits remaining.","example":250},"free_calls_remaining":{"type":"integer","description":"Free monthly calls left before credits are charged.","example":7},"total_used":{"type":"integer","description":"Lifetime credits consumed.","example":412},"monthly_usage":{"type":"integer","description":"Calls made in the current month.","example":23}}},"CreditBundle":{"type":"object","properties":{"id":{"type":"integer","example":2},"name":{"type":"string","example":"Growth"},"credits":{"type":"integer","example":100},"price_cents":{"type":"integer","description":"Price in US cents.","example":9900}}},"PurchaseRequest":{"type":"object","required":["bundle_id"],"properties":{"bundle_id":{"type":"integer","description":"ID of the bundle from GET /api/v1/credits/bundles.","example":2}}},"PurchaseResponse":{"type":"object","properties":{"checkout_url":{"type":"string","format":"uri","description":"Stripe Checkout URL to redirect the buyer to."},"session_id":{"type":"string","example":"cs_test_a1b2c3"}}},"CreateKeyRequest":{"type":"object","properties":{"name":{"type":"string","description":"Human-friendly label for the key."},"is_test_mode":{"type":"boolean","description":"When true, mints an im_test_… key that never charges credits.","default":false}}},"CreateKeyResponse":{"type":"object","description":"The full API key is returned ONCE at creation time and never again — store it securely.","properties":{"api_key":{"type":"string","example":"im_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"},"name":{"type":"string","nullable":true},"is_test_mode":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}}},"ApiKey":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true},"key_prefix":{"type":"string","description":"First characters of the key followed by '...'. The full key is never returned.","example":"im_live_a1b2c3d..."},"is_test_mode":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":"string","format":"date-time","nullable":true},"total_requests":{"type":"integer"},"is_active":{"type":"boolean"},"revoked_at":{"type":"string","format":"date-time","nullable":true}}},"AutoRefillSettings":{"type":"object","nullable":true,"description":"Null when the user has never configured auto-refill.","properties":{"enabled":{"type":"boolean"},"refill_threshold":{"type":"integer","description":"Trigger a top-up when the balance drops to/below this."},"refill_amount":{"type":"integer","description":"Credits added per top-up."},"stripe_payment_method_id":{"type":"string","nullable":true,"description":"Masked saved card, e.g. '****4242'. Never the raw payment-method ID."},"monthly_limit":{"type":"integer","description":"Max credits auto-refilled per month."},"monthly_used":{"type":"integer","description":"Credits auto-refilled so far this month."}}},"AutoRefillUpdate":{"type":"object","description":"All fields optional; only the provided fields are updated.","properties":{"enabled":{"type":"boolean"},"refill_threshold":{"type":"integer","minimum":1},"refill_amount":{"type":"integer","minimum":1},"stripe_payment_method_id":{"type":"string"},"monthly_limit":{"type":"integer","minimum":1}}},"InvestorPartner":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"title":{"type":"string","nullable":true},"seniority":{"type":"string","nullable":true},"focus_areas":{"type":"array","items":{"type":"string"}},"linkedin_url":{"type":"string","nullable":true},"public_email":{"type":"string","nullable":true,"description":"Only present when a deliverable public email is on file."}}},"PortfolioItem":{"type":"object","properties":{"id":{"type":"integer"},"company_name":{"type":"string"},"company_url":{"type":"string","nullable":true},"stage":{"type":"string","nullable":true},"sector":{"type":"string","nullable":true},"investment_year":{"type":"integer","nullable":true}}},"InvestorFirmSummary":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"website":{"type":"string","nullable":true},"hq":{"type":"string","nullable":true},"stage_focus":{"type":"array","items":{"type":"string"}},"check_size_min":{"type":"integer","nullable":true},"check_size_max":{"type":"integer","nullable":true},"sectors":{"type":"array","items":{"type":"string"}},"thesis_summary":{"type":"string","nullable":true},"submission_url":{"type":"string","nullable":true},"linkedin_url":{"type":"string","nullable":true},"twitter_handle":{"type":"string","nullable":true},"logo_url":{"type":"string","nullable":true},"partner_count":{"type":"integer"},"portfolio_count":{"type":"integer"}}},"InvestorFirmDetail":{"allOf":[{"$ref":"#/components/schemas/InvestorFirmSummary"},{"type":"object","properties":{"partners":{"type":"array","items":{"$ref":"#/components/schemas/InvestorPartner"}},"portfolio":{"type":"array","items":{"$ref":"#/components/schemas/PortfolioItem"}}}}]},"InvestorListResponse":{"type":"object","properties":{"total":{"type":"integer","description":"Total matching firms before paging."},"limit":{"type":"integer"},"offset":{"type":"integer"},"firms":{"type":"array","items":{"$ref":"#/components/schemas/InvestorFirmSummary"}}}}}},"security":[{"bearerAuth":[]}],"paths":{"/api/v1/analyze":{"post":{"tags":["Analysis"],"summary":"Submit a pitch deck for analysis","description":"Queues a pitch deck for AI analysis and investor matching. Provide either a multipart `deck` file (PDF) or a JSON `deck_url`. Credit-metered and rate-limited. Returns a job_id to poll.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeRequest"}},"multipart/form-data":{"schema":{"type":"object","properties":{"deck":{"type":"string","format":"binary","description":"Pitch deck PDF."},"target_check_size":{"type":"string"},"webhook_url":{"type":"string","format":"uri"},"contact_email":{"type":"string","format":"email"}}}}}},"responses":{"202":{"description":"Analysis queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeResponse"}}}},"400":{"description":"Invalid request (missing deck/deck_url or bad body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits / free quota exhausted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/jobs/{id}":{"get":{"tags":["Analysis"],"summary":"Get job status","description":"Poll the status of an analysis job. Credit-metered access counts only on matches.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The job_id returned by /api/v1/analyze."}],"responses":{"200":{"description":"Current job status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobStatusResponse"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Job not found (or not owned by this key).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/jobs/{id}/matches":{"get":{"tags":["Analysis"],"summary":"Get ranked investor matches","description":"Returns up to 20 ranked investor matches for a completed job. The first access charges one credit (unless test mode or free quota). Use ?format=csv, ?format=scorecard-pdf, ?format=emails-pdf, or ?format=warm-referrals-pdf for alternative export formats; the default is JSON.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The job_id returned by /api/v1/analyze."},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["json","csv","scorecard-pdf","emails-pdf","warm-referrals-pdf"]},"description":"Response format. Defaults to JSON."}],"responses":{"200":{"description":"Ranked matches (JSON) or a file download for non-JSON formats.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchesResponse"}}}},"400":{"description":"Job is not completed yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits to access matches.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Job not found (or not owned by this key).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/blog/posts":{"post":{"tags":["Blog"],"summary":"Create a blog post draft (admin-only)","description":"ADMIN-ONLY, FREE, AND UNMETERED. Requires an API key owned by an admin; any other valid key receives 403. This endpoint does NOT consume credits, does NOT count against any quota, and has NO rate limit. Posts are always created as DRAFTS and are never auto-published — an admin reviews and publishes them from the dashboard. Creating a draft is the ONLY programmatic blog action; there are no list/edit/delete/publish API endpoints.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPostRequest"}}}},"responses":{"201":{"description":"Draft created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPostResponse"}}}},"400":{"description":"Invalid payload, non-draft status requested, empty content after sanitization, a non-http(s) image URL, or an article that fails the GEO content standard. GEO failures include `requirements` (the rules) and `details` (an array of { code, message, detail }) so the article can be corrected and resubmitted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key is valid for the deck/campaign API but is not owned by an admin, so it is not authorized to publish blog posts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/credits":{"get":{"tags":["Credits"],"summary":"Get credit balance and usage","description":"Returns your purchased credit balance, remaining free monthly calls, lifetime usage, and this month's usage. Authenticated and rate-limited.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Current credit balance and usage.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditBalance"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/credits/bundles":{"get":{"tags":["Credits"],"summary":"List purchasable credit bundles","description":"Returns the available credit bundles and their prices. Public — no API key required.","security":[],"responses":{"200":{"description":"Available credit bundles.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CreditBundle"}}}}}}}},"/api/v1/credits/purchase":{"post":{"tags":["Credits"],"summary":"Start a credit purchase (Stripe Checkout)","description":"Creates a Stripe Checkout session for the chosen bundle and returns a URL to redirect the buyer to. Credits are granted via the Stripe webhook after payment. Authenticated and rate-limited.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseRequest"}}}},"responses":{"200":{"description":"Checkout session created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseResponse"}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Credit bundle not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/credits/webhook":{"post":{"tags":["Credits"],"summary":"Stripe payment webhook (internal)","description":"Stripe-only endpoint that grants credits after a successful checkout. Authenticated by Stripe signature (Stripe-Signature header), NOT by an API key. Not intended to be called by API consumers; documented for completeness.","security":[],"requestBody":{"required":true,"description":"Raw Stripe event payload.","content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Event received (and processed if it was a completed checkout).","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean","example":true},"status":{"type":"string","example":"already_processed"}}}}}},"400":{"description":"Missing signature/raw body, invalid signature, or bad metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/keys":{"post":{"tags":["API Keys"],"summary":"Create a new API key","description":"Mints a new API key for the authenticated account. The full key is returned ONCE and never again. Authenticated with an existing key and rate-limited.","security":[{"bearerAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyRequest"}}}},"responses":{"201":{"description":"Key created. Store api_key securely — it is shown only once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyResponse"}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["API Keys"],"summary":"List your API keys","description":"Lists all API keys for the authenticated account (prefixes only; full keys are never returned). Authenticated and rate-limited.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Your API keys.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/keys/{id}":{"delete":{"tags":["API Keys"],"summary":"Revoke an API key","description":"Revokes one of your API keys by numeric ID. Authenticated and rate-limited.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Numeric ID of the key to revoke (from GET /api/v1/keys)."}],"responses":{"200":{"description":"Key revoked.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true}}}}}},"400":{"description":"Invalid key ID or key already revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"API key not found (or not owned by this account).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auto-refill":{"get":{"tags":["Auto-Refill"],"summary":"Get auto-refill settings","description":"Returns your automatic credit top-up settings, or null if never configured. The saved payment method is masked. Authenticated and rate-limited.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Auto-refill settings (or null).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoRefillSettings"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"tags":["Auto-Refill"],"summary":"Update auto-refill settings","description":"Updates your automatic credit top-up settings. All fields are optional; only the provided fields are changed. Authenticated and rate-limited.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoRefillUpdate"}}}},"responses":{"200":{"description":"Updated auto-refill settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoRefillSettings"}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/investors":{"get":{"tags":["Investors"],"summary":"Search the verified investor database","description":"Returns active, website-verified investor firms with partner/portfolio counts. Supports filtering and paging via query params. Use ?format=csv for a CSV download; the default is JSON. Authenticated and rate-limited.","security":[{"bearerAuth":[]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Case-insensitive firm name search."},{"name":"stage","in":"query","required":false,"schema":{"type":"string"},"description":"Filter by stage focus (e.g. seed, series-a)."},{"name":"sector","in":"query","required":false,"schema":{"type":"string"},"description":"Filter by sector tag."},{"name":"geo","in":"query","required":false,"schema":{"type":"string"},"description":"Filter by HQ location (alias: hq)."},{"name":"checkSizeMin","in":"query","required":false,"schema":{"type":"integer"},"description":"Only firms whose max check is at least this."},{"name":"checkSizeMax","in":"query","required":false,"schema":{"type":"integer"},"description":"Only firms whose min check is at most this."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"minimum":1,"maximum":500},"description":"Page size (1–500)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"minimum":0},"description":"Pagination offset."},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["json","csv"]},"description":"Response format. Defaults to JSON."}],"responses":{"200":{"description":"Matching firms (JSON) or a CSV download when format=csv.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvestorListResponse"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/investors/{id}":{"get":{"tags":["Investors"],"summary":"Get a single investor firm","description":"Returns a verified investor firm with its full partner list and portfolio. Authenticated and rate-limited.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Numeric firm ID."}],"responses":{"200":{"description":"Investor firm detail.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvestorFirmDetail"}}}},"400":{"description":"Invalid firm ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Firm not found, inactive, or unverified.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/investors/{id}/partners":{"get":{"tags":["Investors"],"summary":"Get a firm's partners","description":"Returns the (non-rejected) partners for a verified investor firm. Authenticated and rate-limited.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Numeric firm ID."}],"responses":{"200":{"description":"The firm's partners.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InvestorPartner"}}}}},"400":{"description":"Invalid firm ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Firm not found, inactive, or unverified.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/investors/{id}/portfolio":{"get":{"tags":["Investors"],"summary":"Get a firm's portfolio","description":"Returns the portfolio companies for a verified investor firm. Authenticated and rate-limited.","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Numeric firm ID."}],"responses":{"200":{"description":"The firm's portfolio companies.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PortfolioItem"}}}}},"400":{"description":"Invalid firm ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Firm not found, inactive, or unverified.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}