{"openapi":"3.1.0","info":{"title":"Growth.Talent API","version":"1.3.0","description":"The first agent-native job platform for growth marketing. Public read-only endpoints (jobs, talent, companies) need no auth. Authenticated endpoints (me, applications, posting jobs) require a Bearer API key generated by an admin-verified member at https://www.growthtalent.org/settings/api-keys.\n\nThis spec is auto-generated from zod schemas — runtime validation and the published spec are guaranteed in sync.","contact":{"name":"Growth.Talent","url":"https://www.growthtalent.org/agents"},"license":{"name":"API terms","url":"https://www.growthtalent.org/terms"}},"servers":[{"url":"https://www.growthtalent.org/api/v1"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Send `Authorization: Bearer gt_live_...`. Generate a key at https://www.growthtalent.org/settings/api-keys after admin verification."},"apiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Alternative to Bearer — same key value."}},"schemas":{"ApiError":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"validation_error"},"message":{"type":"string","example":"Invalid request body"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"number"}]}},"message":{"type":"string"},"code":{"type":"string"}},"required":["path","message","code"]},"description":"Present on 422 validation errors — zod issue list."},"requiredScope":{"type":"string"},"keyScopes":{"type":"array","items":{"type":"string"}},"retryAfterSec":{"type":"number"}},"required":["code","message"]}},"required":["error"]},"PatchMeBody":{"type":"object","properties":{"name":{"type":["string","null"]},"headline":{"type":["string","null"],"maxLength":180},"bio":{"type":["string","null"],"maxLength":1000},"currentTitle":{"type":["string","null"]},"city":{"type":["string","null"]},"country":{"type":["string","null"]},"linkedinUrl":{"type":["string","null"],"format":"uri"},"resumeUrl":{"type":["string","null"],"format":"uri"},"reportsTo":{"type":["string","null"]},"salaryMin":{"type":["integer","null"],"minimum":0},"salaryMax":{"type":["integer","null"],"minimum":0},"salaryCurrency":{"type":["string","null"],"minLength":3,"maxLength":3},"isOpenToWork":{"type":"boolean"},"isInGrowthTeam":{"type":"boolean"},"seniority":{"$ref":"#/components/schemas/Seniority"},"profileVisibility":{"$ref":"#/components/schemas/ProfileVisibility"},"teamScope":{"$ref":"#/components/schemas/TeamScope"},"tools":{"type":"array","items":{"type":"string"},"maxItems":30},"markets":{"type":"array","items":{"$ref":"#/components/schemas/Market"}}},"example":{"isOpenToWork":true,"headline":"VP Growth at Spendesk → CEO at The Mobile First Company","salaryMin":200000,"salaryMax":280000,"salaryCurrency":"USD","tools":["HubSpot","Mixpanel","Segment","Braze"]}},"Seniority":{"type":"string","enum":["INTERN","JUNIOR","MID","SENIOR","LEAD","MANAGER","DIRECTOR","VP","C_LEVEL"],"description":"Career level"},"ProfileVisibility":{"type":"string","enum":["PUBLIC","PRIVATE","EMPLOYERS_ONLY"]},"TeamScope":{"type":"string","enum":["solo","1-5","5-15","15-50","50+"]},"Market":{"type":"string","enum":["USA","FRANCE","LATAM"]},"PatchJobBody":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":120},"description":{"type":"string","minLength":100},"applyUrl":{"type":"string","format":"uri"},"salaryMin":{"type":["integer","null"],"minimum":0},"salaryMax":{"type":["integer","null"],"minimum":0},"salaryCurrency":{"type":["string","null"],"minLength":3,"maxLength":3},"seniority":{"$ref":"#/components/schemas/Seniority"},"remote":{"$ref":"#/components/schemas/Remote"},"contractType":{"$ref":"#/components/schemas/ContractType"},"status":{"$ref":"#/components/schemas/JobStatus"},"city":{"type":["string","null"]},"country":{"type":["string","null"]}},"example":{"status":"EXPIRED"}},"Remote":{"type":"string","enum":["REMOTE","HYBRID","ONSITE"],"description":"Work location model"},"ContractType":{"type":"string","enum":["FULL_TIME","PART_TIME","CONTRACT","FREELANCE","INTERNSHIP"]},"JobStatus":{"type":"string","enum":["APPROVED","EXPIRED"],"description":"Use EXPIRED to close a job, APPROVED to reopen it."},"PostApplicationBody":{"type":"object","properties":{"jobSlugOrId":{"type":"string","minLength":1},"message":{"type":["string","null"],"maxLength":2000},"via":{"$ref":"#/components/schemas/ApplyVia"}},"required":["jobSlugOrId"],"example":{"jobSlugOrId":"head-of-growth-the-mobile-first-company","message":"Hi — I built growth at Spendesk #4 to unicorn. Would love to chat about Allo.","via":"magic"}},"ApplyVia":{"type":"string","enum":["magic","external"],"description":"magic: we email the company directly with the candidate profile + cover note. external: agent records the apply but the user is still expected to click through to applyUrl."},"PostJobBody":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":120},"category":{"type":"string","minLength":1},"applyUrl":{"type":"string","format":"uri"},"description":{"type":"string","minLength":100},"seniority":{"$ref":"#/components/schemas/Seniority"},"contractType":{"$ref":"#/components/schemas/ContractType"},"remote":{"$ref":"#/components/schemas/Remote"},"market":{"$ref":"#/components/schemas/Market"},"city":{"type":"string"},"country":{"type":"string"},"salaryMin":{"type":"integer","minimum":0},"salaryMax":{"type":"integer","minimum":0},"salaryCurrency":{"type":"string","minLength":3,"maxLength":3}},"required":["title","category","applyUrl","description"],"example":{"title":"Head of Growth","category":"head-of-growth","applyUrl":"https://jobs.example.com/head-of-growth","description":"We're hiring a Head of Growth to own acquisition, activation, and retention across paid, organic, and lifecycle channels. 5+ years scaling a B2B or PLG product from $1M to $10M+ ARR. Mixpanel, paid ads, attribution.","seniority":"VP","remote":"HYBRID","market":"USA","city":"New York","country":"US","salaryMin":180000,"salaryMax":240000,"salaryCurrency":"USD"}}},"parameters":{}},"paths":{"/health":{"get":{"summary":"Service health check","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}}}}},"/jobs":{"get":{"summary":"Search jobs","description":"Public read. Filter by category, city, market, seniority, contract, salary, remote, free-text q. Returns the standard {data, pagination, meta} envelope. Cached for 1800s at the edge.","parameters":[{"schema":{"type":"string","enum":["usa","france","latam"]},"required":false,"name":"market","in":"query"},{"schema":{"type":"string","description":"Comma-separated category slugs"},"required":false,"description":"Comma-separated category slugs","name":"category","in":"query"},{"schema":{"type":"string","description":"Comma-separated city slugs"},"required":false,"description":"Comma-separated city slugs","name":"city","in":"query"},{"schema":{"type":"string"},"required":false,"name":"seniority","in":"query"},{"schema":{"type":"string"},"required":false,"name":"contractType","in":"query"},{"schema":{"type":"boolean"},"required":false,"name":"remote","in":"query"},{"schema":{"type":"integer"},"required":false,"name":"salaryMin","in":"query"},{"schema":{"type":"string","description":"Free-text search across title + description + company"},"required":false,"description":"Free-text search across title + description + company","name":"q","in":"query"},{"schema":{"type":"integer","default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","maximum":100,"default":30},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated job list","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}}}},"post":{"summary":"Post a new job (companies only)","description":"Requires `jobs:post` scope on a company-owned API key. AI moderation runs async; jobs scoring under 7 are flagged. Published immediately at /{market}/{category}/{companySlug}/{slug}.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostJobBody"}}}},"responses":{"201":{"description":"Job created","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"403":{"description":"Requires company-owned key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Duplicate slug at this company","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit (5/min, 50/day for jobs:post)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/jobs/{slug}":{"get":{"summary":"Get a single job","parameters":[{"schema":{"type":"string"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Job","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"summary":"Update or close a job (companies only — must own this job)","description":"Requires `jobs:post` scope on a company-owned API key. The job's companyId must match the key's company. Use `status: \"EXPIRED\"` to close, `status: \"APPROVED\"` to reopen.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"slug","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchJobBody"}}}},"responses":{"200":{"description":"Updated. Returns new job + array of fields that changed.","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"400":{"description":"No updatable fields, or description too short","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Wrong scope or wrong company","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Job not found / not owned by this company","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation error — see issues array","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/talent":{"get":{"summary":"Search talent profiles","parameters":[{"schema":{"type":"string"},"required":false,"name":"seniority","in":"query"},{"schema":{"type":"string"},"required":false,"name":"city","in":"query"},{"schema":{"type":"string","description":"e.g. HubSpot, Mixpanel"},"required":false,"description":"e.g. HubSpot, Mixpanel","name":"tool","in":"query"},{"schema":{"type":"boolean"},"required":false,"name":"openToWork","in":"query"},{"schema":{"type":"string"},"required":false,"name":"q","in":"query"},{"schema":{"type":"integer","default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","maximum":100,"default":30},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated talent list","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}}}}},"/talent/{slug}":{"get":{"summary":"Get a single talent profile","parameters":[{"schema":{"type":"string"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Profile","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/companies":{"get":{"summary":"Search companies hiring in growth","parameters":[{"schema":{"type":"string","enum":["usa","france","latam"]},"required":false,"name":"market","in":"query"},{"schema":{"type":"string"},"required":false,"name":"q","in":"query"},{"schema":{"type":"integer","default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","maximum":100,"default":30},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated companies","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}}}}},"/companies/{slug}":{"get":{"summary":"Get a single company with open jobs","parameters":[{"schema":{"type":"string"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Company","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/categories":{"get":{"summary":"Job category taxonomy with counts","responses":{"200":{"description":"Categories list","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}}}}},"/auth/device":{"post":{"summary":"Start OAuth 2.0 device-code flow","description":"Returns device_code (long secret, polled by CLI) + user_code (8-char human code) + verification_uri. 15-minute TTL.","responses":{"200":{"description":"Device flow initiated","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}}}}},"/auth/device/poll":{"post":{"summary":"Poll for the issued API key after the user authorizes the device","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"device_code":{"type":"string"}},"required":["device_code"]}}}},"responses":{"200":{"description":"Issued — { access_token, token_type, scope }","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"400":{"description":"Pending or rejected. error in {authorization_pending, expired_token, access_denied, invalid_grant, already_issued}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Account not yet verified by an admin","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/me":{"get":{"summary":"Whoami — return current candidate or company","description":"Includes private fields (email, salary expectations, markets, resume URL, profile visibility) for the authenticated candidate. Includes rate-limit remaining and key metadata.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Current account info, scopes, rate-limit remaining.","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Scope or rate-limit error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"summary":"Update the authenticated candidate's own profile","description":"Requires `me:write` scope. Whitelisted fields only. Pass `null` to clear a string field.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchMeBody"}}}},"responses":{"200":{"description":"Updated. Returns new candidate + array of fields that changed.","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"400":{"description":"No updatable fields provided","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Wrong scope (need me:write) or banned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation error — see issues array","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit (60/min, 5,000/day for me:*)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/me/applications":{"get":{"summary":"List applications submitted by the authenticated candidate","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"integer","maximum":100,"default":30},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Paginated applications","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}}}}},"/me/applications/{id}":{"get":{"summary":"Get a single application's full detail (with the linked job)","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Application detail","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"404":{"description":"Not found, or not owned by the authenticated candidate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/applications":{"post":{"summary":"Apply to a job on behalf of the authenticated candidate","description":"Requires `jobs:apply` scope. If `via=magic` and the company has a contact email, the company receives a direct email with the candidate's profile and message; otherwise the application is recorded as `external` and the agent should also direct the user to `applyUrl`.\n\nNote: to LIST past applications you also need the `me:read` scope (GET /me/applications).","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostApplicationBody"}}}},"responses":{"201":{"description":"Application created (or upserted)","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"403":{"description":"Wrong scope or banned account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Job not found / not approved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit (5/min, 100/day for jobs:apply)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/facets":{"get":{"summary":"Faceted counts across every dimension for a filter set","description":"Returns total + counts by city, category, seniority, contractType, remoteType, salaryBand. Use this when an agent needs to compose answers to compositional questions ('how many remote growth jobs are there?', 'which cities have the most CMO openings?') without paginating through every result.","parameters":[{"schema":{"type":"string","enum":["USA","FRANCE","LATAM"]},"required":false,"name":"market","in":"query"},{"schema":{"type":"string"},"required":false,"name":"category","in":"query"},{"schema":{"type":"string"},"required":false,"name":"city","in":"query"},{"schema":{"type":"string"},"required":false,"name":"q","in":"query"}],"responses":{"200":{"description":"Facet counts","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}}}}},"/stats/salary":{"get":{"summary":"Salary distribution for a role × market × city","description":"Returns count, p25, median, p75, min, max, currency, disclosureRate. Use this to answer 'what's the median Head of Growth salary in NY?' in one call instead of paginating through hundreds of jobs. Sample capped at 1,000 most-recent listings with disclosed salary.","parameters":[{"schema":{"type":"string","description":"Role slug — see /api/v1/categories"},"required":false,"description":"Role slug — see /api/v1/categories","name":"role","in":"query"},{"schema":{"type":"string","enum":["USA","FRANCE","LATAM"]},"required":false,"name":"market","in":"query"},{"schema":{"type":"string"},"required":false,"name":"city","in":"query"},{"schema":{"type":"string"},"required":false,"name":"category","in":"query"}],"responses":{"200":{"description":"Salary distribution stats","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}}}}},"/stats/companies":{"get":{"summary":"Top companies hiring for a role × market","description":"Use this to answer 'who's hiring CMOs in 2026?' or 'which companies are hiring the most growth marketers in France?'. Returns name + slug + logo + domain + count, sorted by count desc.","parameters":[{"schema":{"type":"string","description":"Role slug — required"},"required":true,"description":"Role slug — required","name":"role","in":"query"},{"schema":{"type":"string","enum":["USA","FRANCE","LATAM"]},"required":false,"name":"market","in":"query"},{"schema":{"type":"integer","maximum":50,"default":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Top companies","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"400":{"description":"Missing or unknown role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/compare/roles":{"get":{"summary":"Side-by-side comparison of two roles","description":"Returns full sideBySide payload for both roles plus auto-generated comparison FAQs (which pays more, which is more remote-friendly, which is in higher demand). Use when a user asks 'Head of Growth vs CMO' or 'PMM vs Growth Marketer'.","parameters":[{"schema":{"type":"string","description":"First role slug"},"required":true,"description":"First role slug","name":"a","in":"query"},{"schema":{"type":"string","description":"Second role slug"},"required":true,"description":"Second role slug","name":"b","in":"query"},{"schema":{"type":"string","enum":["USA","FRANCE","LATAM"]},"required":false,"name":"market","in":"query"}],"responses":{"200":{"description":"Comparison payload","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"400":{"description":"Invalid roles or duplicate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/digest/weekly":{"get":{"summary":"Top jobs from the past 7 days","description":"Pure citation candy. Time-bounded snapshot of the freshest hiring activity — boosted first, then most recent. Refreshes hourly. Use when an LLM wants to ground its answer in 'this week' rather than the all-time index.","parameters":[{"schema":{"type":"string","enum":["USA","FRANCE","LATAM"]},"required":false,"name":"market","in":"query"},{"schema":{"type":"string"},"required":false,"name":"category","in":"query"},{"schema":{"type":"integer","maximum":50,"default":25},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Weekly digest","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}}}}},"/search/ask":{"post":{"summary":"Natural-language search — replaces 3-5 tool calls with one","description":"POST a free-form question and get back parsed filters + matching jobs + a one-line summary in a single call. Backed by Claude Haiku 4.5. Cost-bounded by per-IP rate limit: 30 calls/day unauthenticated, 300/day with API key.\n\nExample request body: {\"question\":\"head of growth jobs in New York paying $200K+\"}","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"question":{"type":"string","minLength":1,"maxLength":500},"limit":{"type":"integer","maximum":50,"default":20}},"required":["question"]}}}},"responses":{"200":{"description":"Parsed filters + jobs + summary","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"400":{"description":"Missing question","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}},"webhooks":{}}