{"openapi":"3.1.0","info":{"title":"Rodeo Recruiting Public API","version":"v1","description":"The Rodeo Recruiting public API lets you search, export, and reveal licensed insurance agents programmatically from your own tools. All responses carry masked contact data unless a reveal credit has been spent for that agent by your workspace. Reveals spend from the same credit pool as the in-app UI.\n\n**Base URL:** `https://app.rodeorecruiting.com/api/v1`\n\n**Authentication:** Pass your API key as a Bearer token:\n```\nAuthorization: Bearer rk_live_…\n```\n\nKeys are workspace-scoped and can be created in Settings → API & webhooks."},"servers":[{"url":"https://app.rodeorecruiting.com/api/v1","description":"Production"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Your API key. Generate one in Settings → API & webhooks. Pass as `Authorization: Bearer rk_live_…`."}},"schemas":{"AgentRow":{"type":"object","required":["id","name","licenseNumber","licenseDesc","state","licensedDate","isCell","isNew","phone","email","revealed"],"description":"A licensed insurance agent row as returned by GET /agents/search and GET /agents/export. Contact fields (phone, email) are masked teaser strings until your workspace has spent a reveal credit for this agent — they are NEVER null. Example masked phone: \"(•••) •••-1234\" (last 4 kept). Example masked email: \"•••••@domain.com\" (domain kept). After a reveal, phone and email carry the real formatted values.","properties":{"id":{"type":"string","description":"Stable agent ID (cuid2)."},"name":{"type":"string","description":"Full name, or \"—\" if unavailable."},"licenseNumber":{"type":"string","description":"License number, or \"—\"."},"licenseDesc":{"type":"string","description":"Title-cased license type description, or \"—\"."},"state":{"type":"string","description":"2-letter US state code, or \"—\"."},"licensedDate":{"type":"string","description":"Formatted date of earliest license (e.g. \"Feb 7, 2026\"), or \"—\"."},"isCell":{"type":"boolean","description":"True if the phone number on file is a wireless/cell number."},"isNew":{"type":"boolean","description":"True if the agent was recently licensed."},"phone":{"type":"string","description":"When revealed=false: a masked teaser string like \"(•••) •••-1234\" (last 4 digits kept). When revealed=true: the real formatted phone number. Never null. Source: src/lib/format.ts maskPhone / formatPhone."},"email":{"type":"string","description":"When revealed=false: a masked teaser string like \"•••••@domain.com\" (domain kept). When revealed=true: the real email address. Never null. Source: src/lib/format.ts maskEmail."},"revealed":{"type":"boolean","description":"Whether this workspace has already spent a reveal credit for this agent."}}},"SearchResult":{"type":"object","required":["data","page","pageSize","total","hasMore"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AgentRow"}},"page":{"type":"integer","minimum":1},"pageSize":{"type":"integer","description":"Fixed at 25 for search."},"total":{"type":"integer","description":"Capped at 10,000 (anti-scrape ceiling). May be lower than the real match count."},"hasMore":{"type":"boolean","description":"Whether at least one more page is reachable within the 10k ceiling."}}},"ExportResult":{"type":"object","required":["data","page","pageSize","total","hasMore"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AgentRow"}},"page":{"type":"integer","minimum":1},"pageSize":{"type":"integer","description":"Requested page size. Default 200, max 200."},"total":{"type":"integer","description":"Capped at 10,000 (anti-scrape ceiling)."},"hasMore":{"type":"boolean"}}},"RevealResult":{"type":"object","required":["revealed","phone","email","credits","creditsSpent"],"properties":{"revealed":{"type":"boolean","enum":[true],"description":"Always true on a successful reveal response."},"phone":{"type":"string","description":"The now-unmasked real formatted phone number for this agent."},"email":{"type":"string","description":"The now-unmasked real email address for this agent."},"credits":{"type":"object","description":"Workspace credit meter snapshot after this reveal (MeterSnapshot). Source: src/lib/credits/balance.ts MeterSnapshot interface.","required":["used","topUp"],"properties":{"used":{"type":"integer","description":"Credits used so far this period."},"total":{"type":["integer","null"],"description":"Monthly credit ceiling, or null for unlimited (National plan)."},"topUp":{"type":"integer","description":"Top-up credits remaining (never reset)."}}},"creditsSpent":{"type":"integer","description":"1 if a new reveal credit was spent (first unlock on a finite plan); 0 if the agent was already unlocked by this workspace, or the plan is unlimited."}}},"ErrorResponse":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["UNAUTHENTICATED","FORBIDDEN","VALIDATION","NOT_FOUND","RATE_LIMITED","PAYMENT_REQUIRED","INTERNAL"],"description":"Machine-readable error code."},"message":{"type":"string","description":"Human-readable error message."},"fields":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}},"description":"Per-field validation messages (only on VALIDATION errors)."},"retryAfterSec":{"type":"integer","description":"Seconds until rate-limit window resets (only on RATE_LIMITED)."}}}}}}},"security":[{"bearerAuth":[]}],"paths":{"/agents/search":{"get":{"operationId":"searchAgents","summary":"Search agents","description":"Search the licensed-agent database with filters. Returns up to 25 agents per page. The total is capped at 10,000 (anti-scrape ceiling, up to page 400). Contact fields are masked unless your workspace has revealed the agent.\n\n**Rate limit:** 120 requests/min per workspace. Responses carry `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` headers.","parameters":[{"name":"state","in":"query","description":"Filter by US state code(s). Repeat for multiple states (e.g. `?state=FL&state=TX`). Results are limited to your covered states regardless of this filter.","schema":{"type":"array","items":{"type":"string","example":"FL"}},"style":"form","explode":true},{"name":"category","in":"query","description":"License category.","schema":{"type":"string","enum":["life","health","pc","adjuster","annuity"]}},{"name":"status","in":"query","description":"License status. Default: `VALID`.","schema":{"type":"string","default":"VALID","example":"VALID"}},{"name":"recency","in":"query","description":"Only return agents with license activity in the last N days.","schema":{"type":"integer","enum":[30,60,90]}},{"name":"cell","in":"query","description":"When `1` or `true`, only return agents with a cell phone on file.","schema":{"type":"string","enum":["1","true","0","false"]}},{"name":"sms","in":"query","description":"When `1` or `true`, only return agents with SMS capability.","schema":{"type":"string","enum":["1","true","0","false"]}},{"name":"q","in":"query","description":"Full-text search against agent name, NPN, city.","schema":{"type":"string","maxLength":120}},{"name":"city","in":"query","description":"Filter by city (exact, case-insensitive).","schema":{"type":"string","maxLength":80}},{"name":"county","in":"query","description":"Filter by county.","schema":{"type":"string","maxLength":80}},{"name":"zip","in":"query","description":"Filter by ZIP code.","schema":{"type":"string","maxLength":10}},{"name":"npn","in":"query","description":"Filter by National Producer Number (exact match).","schema":{"type":"string","maxLength":20}},{"name":"tag","in":"query","description":"Filter by an internal workspace tag ID (the cuid2 from your workspace tags). Not generally useful to external integrators — this is an org-internal identifier.","schema":{"type":"string","maxLength":40}},{"name":"res","in":"query","description":"Filter by residency type.","schema":{"type":"string","enum":["resident","nonresident"]}},{"name":"sort","in":"query","description":"Sort order. Default: `newest`.","schema":{"type":"string","enum":["newest","name"],"default":"newest"}},{"name":"page","in":"query","description":"Page number (1-indexed). Max reachable page is 400 (10k ceiling).","schema":{"type":"integer","minimum":1,"maximum":400,"default":1}}],"responses":{"200":{"description":"Success. Empty result when outside your covered states or subscription is paused.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/SearchResult"}}}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. Retry after the `Retry-After` seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/agents/export":{"get":{"operationId":"exportAgents","summary":"Export agents (bulk)","description":"Export agents with a larger page size (up to 200 per page, default 200). Identical filtering to search; the same masking and 10k ceiling apply. Use for bulk data pulls to your own system.\n\n**Rate limit:** 30 requests/min per workspace (tighter than search). Responses carry `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` headers.","parameters":[{"name":"state","in":"query","description":"Filter by US state code(s). Repeat for multiple states (e.g. `?state=FL&state=TX`). Results are limited to your covered states regardless of this filter.","schema":{"type":"array","items":{"type":"string","example":"FL"}},"style":"form","explode":true},{"name":"category","in":"query","description":"License category.","schema":{"type":"string","enum":["life","health","pc","adjuster","annuity"]}},{"name":"status","in":"query","description":"License status. Default: `VALID`.","schema":{"type":"string","default":"VALID","example":"VALID"}},{"name":"recency","in":"query","description":"Only return agents with license activity in the last N days.","schema":{"type":"integer","enum":[30,60,90]}},{"name":"cell","in":"query","description":"When `1` or `true`, only return agents with a cell phone on file.","schema":{"type":"string","enum":["1","true","0","false"]}},{"name":"sms","in":"query","description":"When `1` or `true`, only return agents with SMS capability.","schema":{"type":"string","enum":["1","true","0","false"]}},{"name":"q","in":"query","description":"Full-text search against agent name, NPN, city.","schema":{"type":"string","maxLength":120}},{"name":"city","in":"query","description":"Filter by city (exact, case-insensitive).","schema":{"type":"string","maxLength":80}},{"name":"county","in":"query","description":"Filter by county.","schema":{"type":"string","maxLength":80}},{"name":"zip","in":"query","description":"Filter by ZIP code.","schema":{"type":"string","maxLength":10}},{"name":"npn","in":"query","description":"Filter by National Producer Number (exact match).","schema":{"type":"string","maxLength":20}},{"name":"tag","in":"query","description":"Filter by an internal workspace tag ID (the cuid2 from your workspace tags). Not generally useful to external integrators — this is an org-internal identifier.","schema":{"type":"string","maxLength":40}},{"name":"res","in":"query","description":"Filter by residency type.","schema":{"type":"string","enum":["resident","nonresident"]}},{"name":"sort","in":"query","description":"Sort order. Default: `newest`.","schema":{"type":"string","enum":["newest","name"],"default":"newest"}},{"name":"page","in":"query","description":"Page number (1-indexed). Max reachable page is 400 (10k ceiling).","schema":{"type":"integer","minimum":1,"maximum":400,"default":1}},{"name":"pageSize","in":"query","description":"Rows per page. Default 200, max 200.","schema":{"type":"integer","minimum":1,"maximum":200,"default":200}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ExportResult"}}}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/agents/{id}/reveal":{"post":{"operationId":"revealAgent","summary":"Reveal an agent (paid)","description":"Unlock the full contact details (phone, email) for an agent. Spends one reveal credit from your workspace balance on the first reveal; re-revealing an already-unlocked agent is free (`creditsSpent: 0`). The agent must be within your covered states.\n\n**Rate limit:** 60 requests/min per workspace.\n\n**402 Payment Required:** Returned when your workspace has no active subscription or no remaining reveal credits. Reactivate your subscription or purchase a credit top-up in the app.","parameters":[{"name":"id","in":"path","required":true,"description":"The agent ID from a search or export response.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Agent revealed. Contact fields are now populated.","content":{"application/json":{"schema":{"type":"object","required":["ok","data"],"properties":{"ok":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/RevealResult"}}}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"No active subscription or no reveal credits remaining.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Agent not found or outside your covered states.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"x-rodeo-webhooks":{"description":"Outbound webhooks deliver signed POST requests to your registered endpoints when events occur in your workspace (e.g. agent revealed, pipeline stage changed).","headers":{"X-RR-Webhook-Id":"Stable delivery ID — idempotency key across retries.","X-RR-Webhook-Timestamp":"Unix timestamp (seconds) of the delivery attempt.","X-RR-Signature":"HMAC-SHA256 signature. Format: `sha256=<hex>`.","X-RR-Signature-Previous":"Previous signature during key rotation grace window. Accept either to verify."},"verification":{"description":"To verify a delivery: compute HMAC-SHA256 over `${X-RR-Webhook-Timestamp}.${rawBody}` using your endpoint signing secret. Compare the hex digest to the value after `sha256=` in `X-RR-Signature`. During key rotation also accept `X-RR-Signature-Previous`.","preImage":"${X-RR-Webhook-Timestamp}.${rawBody}","algorithm":"HMAC-SHA256"}}}