# Welcome to Clodo > Find the right people, enrich their contact details, and turn research into personalized outreach — all through a single API. ## Get started Create an account, get your API key, and make your first request. - [Get an API Key](https://docs.clodo.ai/get-api-key) — Create your account and connect to the Clodo API. - [Quickstart](https://docs.clodo.ai/api-quick-start) — Make your first request in just a few minutes. ## APIs Search for people with natural language, resolve their professional identities, and reach them from your own connected inbox. **Search API** - [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search) — The canonical agentic people search endpoint. Describe the people you want to find in as much detail as you like—their role, background, expertise, company, or specific experiences. Clodo searches and researches potential matches, returning people with evidence explaining why they fit your criteria. - [People Search](https://docs.clodo.ai/api-reference/endpoint/people-search) — Find sales leads, source candidates, and discover industry experts with a natural-language people search. Describe your target audience and retrieve matching professional profiles. - [Deep Search](https://docs.clodo.ai/api-reference/endpoint/deep-search) — Find and qualify sales prospects, candidates, and subject-matter experts against detailed criteria. Turn a targeted people search into a researched shortlist with professional context and fit scores. **Enrichment API** - [Email Enrichment](https://docs.clodo.ai/api-reference/endpoint/email-enrichment) — Find a professional email address from a profile URL or a name and company domain. - [Phone Enrichment](https://docs.clodo.ai/api-reference/endpoint/phone-enrichment) — Find a person’s phone number from their professional identity. - [Professional URL](https://docs.clodo.ai/api-reference/endpoint/professional-url) — Resolve a person’s identity to a professional profile URL. - [Professional Profile](https://docs.clodo.ai/api-reference/endpoint/professional-profile) — Retrieve a person’s work history, education, and current role from their profile URL. **Outreach API** - [Outreach Emails](https://docs.clodo.ai/api-reference/endpoint/outreach-emails) — Draft personalized emails, review them, and send from your connected inbox. - [Sequences](https://docs.clodo.ai/api-reference/endpoint/sequences) — Create, review, and run personalized email sequences with automatic follow-ups. - [Sending Accounts](https://docs.clodo.ai/api-reference/endpoint/sending-accounts) — Check your connected mailboxes before creating outreach emails or sequences. ## Build a workflow Start with a [people search API use case](https://docs.clodo.ai/guides/use-cases), then connect the endpoints your application needs: - [GTM lead generation](https://docs.clodo.ai/guides/gtm-lead-generation) — find B2B prospects, qualify decision-makers, enrich contact details, and prepare sales outreach. - [Recruiting and candidate sourcing](https://docs.clodo.ai/guides/recruiting-candidate-sourcing) — turn job requirements into a candidate search, review professional evidence, and draft recruiter messages. - [Expert discovery](https://docs.clodo.ai/guides/expert-discovery) — identify practitioners for industry interviews, customer research, and advisory projects. ## Build with your AI tools Every page is available as Markdown. Use **Copy page** to bring the documentation into your editor, or **Open in ChatGPT** and **Open in Claude** to ask questions about a specific page. The [documentation index](https://docs.clodo.ai/llms.txt) gives agents a list of all available pages. [Download the full documentation](https://docs.clodo.ai/llms-full.txt) for a single text reference. ## Developer resources - [Authentication](https://docs.clodo.ai/api-reference/authentication) — API keys and request headers. - [Webhook events](https://docs.clodo.ai/guides/webhook-events) — receive search and enrichment results. - [Async polling](https://docs.clodo.ai/guides/async-polling) — follow a job through completion. - [Handling errors](https://docs.clodo.ai/api-reference/errors) — understand failures and retry safely. Source: https://docs.clodo.ai/introduction --- # Get an API Key ## Create an account 1. Open [Get Started](https://clodo.ai/get-started) and choose **API pay as you go**. 2. Complete signup and checkout, then open the [API Console](https://clodo.ai/api-console). 3. Check your balance in **Credits** before making a paid request. See [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing) for endpoint costs. ## Get your API key 1. Open [API Console → API Keys](https://clodo.ai/api-console?tab=keys). 2. Click **Mint new key** and complete the key creation form. 3. Copy the raw key immediately. Allow up to 60 seconds before first use. > **Your API key is shown only once.** Store it in a secret manager or server environment variable. Keep it out of browser code, public repositories, and shared screenshots. If it is lost or leaked, revoke it in the console and create a new key. ## Authenticate your requests Pass the raw key in the `x-api-key` header. Do not add a `Bearer` prefix or place the key in the URL. ```http x-api-key: ck_live_ ``` The API base URL is `https://api-public.clodo.ai/api/public/v1/`. Include the trailing slash on endpoint paths. ## Make your first API call Follow the [Quickstart](https://docs.clodo.ai/api-quick-start) for a complete request, or choose an endpoint in the API Reference. For People Search, Deep Search, and Phone Enrichment, create a [webhook signing secret](https://docs.clodo.ai/guides/webhook-secrets) before making your first call. These endpoints require a public HTTPS webhook URL, even when you also use polling. Source: https://docs.clodo.ai/get-api-key --- # Quickstart Three steps from zero to your first response: mint an API key, send a request, read the result. ## 1. Mint an API key Open [API Console → API Keys](https://clodo.ai/api-console?tab=keys) and click **Mint new key**. Copy the raw secret immediately. We display it once and never store it in plaintext after that. Keys look like `ck_live_`. ## 2. Send your first request ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/enrich/email/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "first_name": "Patrick", "last_name": "Collison", "domain": "stripe.com" }' ``` A successful response: ```json { "email": "patrick@stripe.com" } ``` Response headers include `X-Request-Id` (a UUID per request). ## 3. Same call from Python ```python import requests response = requests.post( "https://api-public.clodo.ai/api/public/v1/enrich/email/", headers={"x-api-key": "ck_live_..."}, json={ "first_name": "Patrick", "last_name": "Collison", "domain": "stripe.com", }, timeout=30, ) response.raise_for_status() print(response.json()["email"]) ``` ## 3b. Same call from Node ```javascript const response = await fetch( "https://api-public.clodo.ai/api/public/v1/enrich/email/", { method: "POST", headers: { "x-api-key": "ck_live_...", "content-type": "application/json", }, body: JSON.stringify({ first_name: "Patrick", last_name: "Collison", domain: "stripe.com", }), }, ); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } const { email } = await response.json(); console.log(email); ``` ## Beyond enrichment: full outbound The API also covers outreach itself — find people with [Deep Search](https://docs.clodo.ai/api-reference/endpoint/deep-search), enrich their emails, then draft, review, and send from your own inbox with [Outreach Emails](https://docs.clodo.ai/api-reference/endpoint/outreach-emails): ```bash # Draft an email (202 -> poll -> review -> approve -> it sends) curl -X POST https://api-public.clodo.ai/api/public/v1/emails/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "to": {"email": "jackie@acme.com", "first_name": "Jackie", "company_name": "Acme"}, "instructions": "Casual note asking if she is open to a chat about our founding PM role." }' ``` With the default `review: "required"`, nothing sends without your explicit approval. No webhook receiver is required — polling is first-class for these endpoints. ## What to read next - [Authentication](https://docs.clodo.ai/api-reference/authentication) for the full key model and what headers we accept. - [API Base URL](https://docs.clodo.ai/api-reference/base-url) for the endpoint surface and which host to point at. - Per-endpoint reference for input shapes, output shapes, and credit costs. - People Search, Deep Search, and Phone Enrichment return `202 Accepted` and POST results to a webhook URL you specify. [Webhook Signing Secrets](https://docs.clodo.ai/guides/webhook-secrets) and [Webhook Signature Verification](https://docs.clodo.ai/guides/webhook-signing) cover wiring a receiver. [Async Polling](https://docs.clodo.ai/guides/async-polling) is the fallback when your receiver is unavailable. ## Costs in this example 5 credits per call. See [Credit Semantics](https://docs.clodo.ai/guides/credits-and-pricing) for the full table. Source: https://docs.clodo.ai/api-quick-start --- # Credits & Pricing Clodo uses credits for people discovery, contact enrichment, and email drafting. The amount charged depends on the endpoint and its result. ## Get started You need to **sign up first** on the [Get Started page](https://clodo.ai/get-started). Choose **API pay as you go** to create your account and add your starting credits, then open the [API Console](https://clodo.ai/api-console) to create a key. ## API pricing The self-serve API offer starts with a one-time credit purchase. There is no monthly API subscription. | | API pay as you go | |---|---| | Starting payment | $25 | | Starting balance | 1,250 credits | | Additional usage | $0.02 per credit | | Monthly commitment | None | Every additional 1,000 credits costs $20. Applicable taxes are extra. See [Get Started → Credit rates](https://clodo.ai/get-started#credit-rates-title) for current offer details and API rates. > API signup includes authorization for usage billing. After your starting balance is used, additional usage is charged automatically to your saved card at the stated per-credit rate. Review the checkout terms before completing signup. ## Manage your balance Open [API Console → Credits](https://clodo.ai/api-console?tab=credits) to review your available credits and purchase additional credits. The [Usage tab](https://clodo.ai/api-console?tab=usage) shows request history and credit charges. ## Per-endpoint cost | Endpoint | Cost | Charge model | |---|---|---| | Email Enrichment | 5 credits | Per call | | Phone Enrichment | 50 credits on hit, 0 on miss | Per hit | | Professional URL Enrichment | 3 credits | Per call | | Professional Profile Enrichment | 2 credits | Per call | | People Search | 1 credit per lead returned | Variable per result | | Deep Search | 200 credits (`mode: "standard"`) or 400 credits (`mode: "extended"`) | Fixed per run | | [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search) | Variable, capped by `max_credits` (300–2,000; default 500) | Reserve the cap, settle the final charge, release or refund the remainder | | Outreach Emails | 2 credits per drafted email | Per successful draft | Polling endpoints (`GET /v1/.../{job_id}/`, `GET /v1/emails/...`) consume no credits. For Outreach Emails, drafting is the only billable event — reviewing, editing, approving, cancelling, and sending are free, and a failed draft is not charged. Cancelling an already-drafted email does not refund the draft. ## Charge lifecycle Agentic Search charges 100 credits for a completed search that performs work but returns no qualifying leads. Internal failures and cancellation before paid work starts are not charged. Partial deliveries remain billable within your cap. See [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search) for the full budget and cancellation behavior. Each call creates an audit row that ends in one of these states (visible in the **Usage** tab of the Console): | `charge_status` | Customer pill | Meaning | |---|---|---| | `pending` | Pending | In flight (async endpoints, before settlement). | | `charged` | Completed | Charged the committed amount. | | `zero_result` | Completed | Variable-charge endpoint returned 0 results, no charge. | | `released` | Completed | Validation reject or upstream error, no charge. | `lost_*` states are operational anomalies. ## Topping up Use the **Credits** tab in the Console. ## See also - Per-endpoint reference pages for input shapes and full status mapping. Source: https://docs.clodo.ai/guides/credits-and-pricing --- # Authentication Every request needs the `x-api-key` header. ``` x-api-key: ck_live_ ``` Keys are minted in [API Console → API Keys](https://clodo.ai/api-console?tab=keys). ## Minting an API key 1. Open the **API Keys** tab. 2. Click **Mint new key**. 3. Copy the raw secret. It is shown once. The table shows the key prefix and last four characters after creation. Allow up to 60 seconds after minting before first use. ## Revoking a key Click the trash icon next to a key. Revocation is immediate. In-flight requests with that key fail at the next request boundary with `403 Forbidden` or `401 unauthorized`. Other keys on the account keep working. ## Header rules - Header name is `x-api-key`. - Send the raw secret value. No `Bearer` prefix, no base64 wrapping. - Do not put the key in the URL or request body. ## Errors - `403 Forbidden` — `x-api-key` header missing, revoked, or not recognized. Returned by the edge with body `{"message":"Forbidden"}`. - `402 insufficient_credits` — balance hit zero. Top up in the **Credits** tab. - `403 permission_denied` — key is valid but the account is not eligible for the public API. See [Error Envelope](https://docs.clodo.ai/api-reference/errors) for the full schema and code list. Source: https://docs.clodo.ai/api-reference/authentication --- # API Base URL ``` https://api-public.clodo.ai/api/public/v1/ ``` Full URLs: | Endpoint | URL | |---|---| | Email enrichment | `https://api-public.clodo.ai/api/public/v1/enrich/email/` | | People Search | `https://api-public.clodo.ai/api/public/v1/search/` | | Deep Search | `https://api-public.clodo.ai/api/public/v1/deep-search/` | | Agentic Search | `https://api-public.clodo.ai/api/public/v1/agentic-search/` | | Phone enrichment | `https://api-public.clodo.ai/api/public/v1/enrich/phone/` | | Professional URL | `https://api-public.clodo.ai/api/public/v1/enrich/professional-url/` | | Professional profile | `https://api-public.clodo.ai/api/public/v1/enrich/professional-profile/` | | Outreach Emails | `https://api-public.clodo.ai/api/public/v1/emails/` | | Sequences | `https://api-public.clodo.ai/api/public/v1/sequences/` | | Sending Accounts | `https://api-public.clodo.ai/api/public/v1/email/accounts/` | ## Trailing slashes Always send the trailing slash. Endpoint paths are registered at the edge with a trailing slash (`/enrich/email/`, not `/enrich/email`). ## TLS HTTPS only. Plain HTTP is rejected at the edge. ## Versioning Version lives in the path: `/api/public/v1/`. Breaking changes ship as `/v2/`. `/v1/` runs for at least 12 months after `/v2/` ships, with `Sunset` response headers starting 6 months out. Source: https://docs.clodo.ai/api-reference/base-url --- # Agentic Search ## Use case Use Agentic Search to find people whose fit depends on a specific combination of experience, expertise, and accomplishments. Describe your ideal persona in detail, including what matters most and who to exclude. The agent researches potential matches and returns evidence explaining why each person fits. - **Find B2B leads for a specific customer profile.** Look for engineering leaders at robotics companies who have taken autonomous warehouse systems into production, or finance executives with experience expanding a business internationally. Explain the experience that makes someone relevant to your product. - **Source candidates for hard-to-fill roles.** Search for founding engineers who have built developer tools, operations leaders who have scaled manufacturing, or researchers with both academic and industry experience. Spell out the background your hiring team needs beyond a job title. - **Find experts for interviews, advisory work, and research.** Identify practitioners who have implemented a particular technology or solved a specific operational problem. Use the returned fit summaries, proof points, and source evidence to assess whom to contact. For complete workflows, see [Recruiting & Candidate Sourcing](https://docs.clodo.ai/guides/recruiting-candidate-sourcing), [Expert Discovery](https://docs.clodo.ai/guides/expert-discovery), and [GTM Lead Generation](https://docs.clodo.ai/guides/gtm-lead-generation). ## Endpoint ```http POST https://api-public.clodo.ai/api/public/v1/agentic-search/ ``` Send your API key in the `x-api-key` header. See [Authentication](https://docs.clodo.ai/api-reference/authentication). > This endpoint returns `202 Accepted`. Create a [webhook signing secret](https://docs.clodo.ai/guides/webhook-secrets) and supply a public HTTPS `webhook_url` before making your first request. See [Async Polling](https://docs.clodo.ai/guides/async-polling) for retrieving results. ## Pricing See [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing) for credit costs and charging behavior. ## Errors For error responses and retry guidance, see [Handling Errors](https://docs.clodo.ai/api-reference/errors). Clodo's canonical agentic people search endpoint. Describe your target persona in plain language and get as specific as you want: their current role, past experience, technical expertise, location, the kind of company they work at, or something particular they have done. Combine criteria, explain what matters most, and include exclusions or examples to clarify who you are looking for. For example: “Find engineering leaders at US robotics companies who have personally worked on autonomous warehouse systems. Prioritize people who previously took a robotics product from prototype to production, and exclude consultants and recruiting firms.” Clodo searches people data and the open web, researches potential matches against your criteria, and returns people with fit summaries, proof points, and source evidence. The `query` field supports up to 2,000 characters. ## Availability Agentic Search is enabled per account. Once enrolled, all your API keys can use it, including newly created keys. Accounts that are not enrolled receive `404`. [Contact support](mailto:hello@clodo.ai) to request access. ## Credit budget Set `max_credits` to control the maximum charge for a job: **300–2,000 credits**, with a default of **500**. The cap is reserved when the job is accepted. At completion, only the final charge is retained; the unused reservation is released or refunded. The final charge depends on the research performed and leads delivered, and never exceeds your cap. A completed search that performs work but finds no qualifying leads costs **100 credits**. Internal failures and cancellation before paid work starts are not charged. Partial deliveries remain billable within the same cap. A webhook delivery failure does not undo the charge; retrieve the result by polling. ## Request body | Field | Type | Required | Description | |---|---|---|---| | `query` | string | Yes | Describe the people you want to find in plain language, up to 2,000 characters. Be as specific as you like about their role, background, expertise, company, location, or experiences. Include priorities, exclusions, and any evidence you want checked. Must not be empty. | | `target_results` | integer | No | Desired number of leads, from 1 to 50. Default: `50`. This is a target, not a guaranteed result count. | | `max_credits` | integer | No | Maximum charge, from 300 to 2,000 credits. Default: `500`. | | `webhook_url` | string | Yes | Public HTTPS receiver URL, up to 2,048 characters. Must resolve to a public IP address. | Create an active [webhook signing secret](https://docs.clodo.ai/guides/webhook-secrets) before submitting a request. Without one, the endpoint returns `409`. ## Request examples ```bash curl --request POST 'https://api-public.clodo.ai/api/public/v1/agentic-search/' \ --header "x-api-key: $CLODO_API_KEY" \ --header 'Content-Type: application/json' \ --header 'Idempotency-Key: agentic-search-example-001' \ --data '{ "query": "Find engineering leaders at US robotics companies with evidence of deploying autonomous warehouse systems.", "target_results": 25, "max_credits": 500, "webhook_url": "https://your-app.example/webhooks/clodo" }' ``` ```python import os import requests response = requests.post( "https://api-public.clodo.ai/api/public/v1/agentic-search/", headers={ "x-api-key": os.environ["CLODO_API_KEY"], "Idempotency-Key": "agentic-search-example-001", }, json={ "query": "Find engineering leaders at US robotics companies with evidence of deploying autonomous warehouse systems.", "target_results": 25, "max_credits": 500, "webhook_url": "https://your-app.example/webhooks/clodo", }, timeout=30, ) response.raise_for_status() job = response.json() print(job["id"]) ``` ```javascript const response = await fetch("https://api-public.clodo.ai/api/public/v1/agentic-search/", { method: "POST", headers: { "x-api-key": process.env.CLODO_API_KEY, "Content-Type": "application/json", "Idempotency-Key": "agentic-search-example-001", }, body: JSON.stringify({ query: "Find engineering leaders at US robotics companies with evidence of deploying autonomous warehouse systems.", target_results: 25, max_credits: 500, webhook_url: "https://your-app.example/webhooks/clodo", }), }); if (!response.ok) throw new Error(`Clodo returned ${response.status}: ${await response.text()}`); const job = await response.json(); console.log(job.id); ``` Run these examples on your server to keep your API key private. Replace the example webhook URL with your receiver and use a new idempotency key for each new search. ## Accepted response HTTP `202 Accepted`: ```json { "id": "dj_a1b2c3d4e5f6478890abcdef123456789", "status": "pending", "created_at": "2026-09-07T12:00:00Z" } ``` ## Results and webhook events The `agentic_search.completed` event contains the final result, including successful searches with zero leads: ```json { "id": "dj_a1b2c3d4e5f6478890abcdef123456789", "event_type": "agentic_search.completed", "partial": false, "completion_reason": "", "total_returned": 1, "results": [ { "full_name": "Alex Morgan", "current_job_title": "VP of Engineering", "headline": "Building warehouse robotics", "location": "Boston, Massachusetts, United States", "professional_url": "https://www.linkedin.com/in/example-alex-morgan", "company": { "name": "Example Robotics", "domain": "robotics.example", "industry": "Robotics", "employee_count_range": "51-200" }, "experience": [ {"company": "Example Robotics", "title": "VP of Engineering", "date_range": "2022-present"} ], "fit_summary": "Leads engineering for a US company deploying autonomous warehouse systems.", "proof_points": ["Company team page identifies Alex as VP of Engineering."], "evidence": [ {"headline": "Leadership team", "url": "https://robotics.example/team"} ], "tier": "perfect" } ] } ``` The response above is illustrative. Individual person fields may be empty or omitted when unavailable. Evidence entries can include `headline`, `description`, and `url`. Source URLs are only included after retrieval during the job. | Field | Meaning | |---|---| | `results` | Delivered people, with professional details, company, experience, fit summary, proof points, evidence, and a fit tier when available. | | `total_returned` | Number of people delivered. A zero-result search returns `0` and an empty `results` array. | | `partial` | `true` when the job stopped early and returned the results saved so far. | | `completion_reason` | Empty on normal completion; `budget_exhausted`, `deadline_exceeded`, or `user_cancelled` when stopped early. | An internal failure emits `agentic_search.failed`: ```json { "id": "dj_a1b2c3d4e5f6478890abcdef123456789", "event_type": "agentic_search.failed", "error": {"code": "error", "message": "Internal error — you were not charged."} } ``` Verify [webhook signatures](https://docs.clodo.ai/guides/webhook-signing) before processing results. See [Webhook Events](https://docs.clodo.ai/guides/webhook-events) for additional failure codes. ## Polling Use `GET /api/public/v1/agentic-search/{job_id}/` with your API key. Poll every 30–60 seconds until the status is `completed`, `failed`, or `cancelled`. The [polling envelope](https://docs.clodo.ai/guides/async-polling) includes `status`, timestamps, `tier: "agentic_search"`, `leads_returned`, `result`, and `error`. On completion, `result` contains the same result fields as the webhook, without `id` or `event_type`. Polling consumes no credits. ## Cancellation Send `POST /api/public/v1/agentic-search/{job_id}/cancel/` with your API key. No request body is required. It returns `202` with the job's current `id`, `status`, and `created_at`. - A pending job cancels immediately and releases its entire credit reservation. - A running job stops at the next safe point and completes with `partial: true`, `completion_reason: "user_cancelled"`, and any saved results. Work already performed remains billable within your cap. - Cancelling an already terminal job returns its current state. It does not restart the job or refund completed work. Poll after requesting cancellation to confirm the terminal state. The cancellation request is idempotent. ## Idempotency The optional `Idempotency-Key` header is scoped to your API key. Repeating the same key and validated request body returns the existing job, without another dispatch or reservation. Changing the body—including `webhook_url`—while reusing the key returns `409`. Use a fresh key for every new logical request, including requests to other endpoints. ## Rate limits | Operation | Sustained requests per minute | Burst | |---|---|---| | Create search | 2 | 2 | | Poll search | 3,600 | 100 | | Cancel search | 60 | 10 | At most **2 Agentic Search jobs per API key** and **3 per account** can be pending or running. New requests beyond these concurrency limits return `429`. Replays of existing idempotent requests do not create a new job. ## Status codes | HTTP | Meaning | |---|---| | `202` | Search accepted, existing job replayed, or cancellation acknowledged. | | `400` | Invalid query, target, cap, or webhook URL. | | `402` | Billing cannot authorize the requested credit reservation. | | `403` | API key is missing, invalid, revoked, or the account is ineligible for API access. | | `404` | Account is not enrolled for Agentic Search, or the requested job is unavailable to your account. | | `409` | No active webhook signing secret, or an idempotency key was reused with a different body. | | `429` | Request rate or in-flight job limit exceeded. | | `500` | Internal admission or dispatch failure; the reservation is released or refunded. | For other platform errors and response formats, see [Handling Errors](https://docs.clodo.ai/api-reference/errors). Source: https://docs.clodo.ai/api-reference/endpoint/agentic-search --- # People Search ## Use case Use People Search to build a list of people from a natural-language description of your target audience. Search by professional criteria such as role, seniority, industry, company, and location, then retrieve matching profiles for your sales, recruiting, or research workflow. - **Build prospect lists for outbound sales.** Find heads of marketing at SaaS companies in London or procurement leaders at US manufacturers. Use their role and company details to organize leads before enriching contact information or preparing outreach. - **Create candidate pools for recruiting.** Source software engineers in Toronto, finance directors in New York, or product managers at fintech companies. Start with the professional profile you need and review the matches for your open role. - **Discover experts and professional communities.** Find renewable-energy executives for an industry interview series, healthcare operations leaders for customer discovery, or design leaders for an event. Search for the roles and sectors relevant to your project. When you need more people, continue a completed search with `search_id` to retrieve another non-overlapping page of results. ## Endpoint ```http POST https://api-public.clodo.ai/api/public/v1/search/ ``` Send your API key in the `x-api-key` header. See [Authentication](https://docs.clodo.ai/api-reference/authentication). > This endpoint returns `202 Accepted`. Create a [webhook signing secret](https://docs.clodo.ai/guides/webhook-secrets) and supply a public HTTPS `webhook_url` before making your first request. See [Async Polling](https://docs.clodo.ai/guides/async-polling) for retrieving results. ## Pricing See [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing) for credit costs and charging behavior. ## Errors For error responses and retry guidance, see [Handling Errors](https://docs.clodo.ai/api-reference/errors). `POST /api/public/v1/search/` — async (returns `202 Accepted`). 1 credit per lead returned. Light people search. Evaluates a natural-language query and returns matching leads. Wall time ~1 minute. Results delivered via signed webhook POST. Polling fallback at `GET /api/public/v1/search/{job_id}/`. To fetch more non-overlapping leads for the same prompt, pass a completed job `id` back as `search_id`. ## Request | Field | Type | Required | Notes | |---|---|---|---| | `query` | string | yes for new searches | Natural-language sourcing prompt. ≤500 chars. Omit when continuing with `search_id`. | | `search_id` | string | no | `dj_...` id from a previous completed People Search. Continues that search and returns the next non-overlapping page. | | `max_results` | int | yes | min 25 and max 100. Upper bound on lead count. | | `webhook_url` | string | yes | HTTPS URL. Must resolve to a public IP. | Pre-condition: an active webhook signing secret. See [Webhook Signing Secrets](https://docs.clodo.ai/guides/webhook-secrets). `Idempotency-Key` header is supported. Same key returns the same job's current state. ## Response (202) ```json { "id": "dj_a1b2c3d4...", "status": "pending", "created_at": "2026-05-01T12:00:00Z" } ``` Use the returned `id` as `search_id` to continue pagination after the job completes. ## Webhook events `search.completed`: ```json { "id": "dj_a1b2c3d4...", "event_type": "search.completed", "results": [ { "first_name": "Patrick", "last_name": "Collison", "full_name": "Patrick Collison", "current_job_title": "CEO", "headline": "CEO at Stripe", "location": "San Francisco, California, United States", "professional_url": "linkedin.com/in/patrickcollison", "company": { "name": "Stripe", "domain": "stripe.com", "professional_url": "linkedin.com/company/stripe", "industry": "Financial Services", "employee_count": 8000 } } ], "total_returned": 1 } ``` Person and company `professional_url` values come back in bare-host form (no scheme, no `www.`, no trailing slash). Company `professional_url` is optional and may be `null`. `search.failed`: ```json { "id": "dj_a1b2c3d4...", "event_type": "search.failed", "error": {"code": "error", "message": "People search failed; please retry."} } ``` | `error.code` | `error.message` | |---|---| | `invalid_input` | `Invalid search query.` | | `error` | `People search failed; please retry.` | A 0-result outcome ships as `search.completed` with `total_returned: 0`. ## Billing Charged per lead returned. `max_results` is the upper bound on credits. `total_returned` is the actual charge. A 0-result run charges nothing. ## Example ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/search/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "query": "VPs of Engineering at Series B SaaS in NYC", "max_results": 50, "webhook_url": "https://yourapp.com/webhooks/clodo" }' ``` Continue the same search: ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/search/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "search_id": "dj_a1b2c3d4...", "max_results": 100, "webhook_url": "https://yourapp.com/webhooks/clodo" }' ``` ## Rate limit `POST /api/public/v1/search/`: | Limit | Value | |---|---| | Sustained rate | 6 requests / minute | | Burst | 2 requests | Polling `GET /api/public/v1/search/{job_id}/`: | Limit | Value | |---|---| | Sustained rate | 3,600 requests / minute | | Burst | 100 requests | Exceeding any returns `429`. ## See also - [GTM Lead Generation](https://docs.clodo.ai/guides/gtm-lead-generation) - [Recruiting & Candidate Sourcing](https://docs.clodo.ai/guides/recruiting-candidate-sourcing) - [Expert Discovery](https://docs.clodo.ai/guides/expert-discovery) - [Webhook Signing Secrets](https://docs.clodo.ai/guides/webhook-secrets) - [Webhook Signature Verification](https://docs.clodo.ai/guides/webhook-signing) - [Async Polling](https://docs.clodo.ai/guides/async-polling) - [Idempotency](https://docs.clodo.ai/guides/idempotency) Source: https://docs.clodo.ai/api-reference/endpoint/people-search --- # Deep Search ## Use case Use Deep Search to find and qualify people against a focused set of criteria. It returns a researched shortlist with professional details, work experience, and fit scores, giving your team more context for deciding whom to approach. - **Qualify sales leads before outreach.** Search for decision-makers at companies in your target market—for example, technology leaders at regional banks or founders of climate-tech businesses in Germany. Review the returned company context and fit scores to prioritize prospects. - **Build recruiting shortlists.** Find candidates whose roles, industry backgrounds, and career experience align with a position. For a healthcare product leadership role, describe the relevant product and sector experience, then use the returned work history to guide your review. - **Identify subject-matter experts for a project.** Look for supply-chain leaders in manufacturing, executives in renewable energy, or operators in financial services. Compare their professional backgrounds when selecting potential interviewees, advisors, or research participants. Choose standard or extended mode based on the size of the shortlist you want. Each mode has a fixed price per successful run, making it straightforward to budget recurring prospecting, candidate sourcing, and expert discovery workflows. ## Endpoint ```http POST https://api-public.clodo.ai/api/public/v1/deep-search/ ``` Send your API key in the `x-api-key` header. See [Authentication](https://docs.clodo.ai/api-reference/authentication). > This endpoint returns `202 Accepted`. Create a [webhook signing secret](https://docs.clodo.ai/guides/webhook-secrets) and supply a public HTTPS `webhook_url` before making your first request. See [Async Polling](https://docs.clodo.ai/guides/async-polling) for retrieving results. ## Pricing See [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing) for credit costs and charging behavior. ## Errors For error responses and retry guidance, see [Handling Errors](https://docs.clodo.ai/api-reference/errors). `POST /api/public/v1/deep-search/` — async (returns `202 Accepted`). Fixed price per run: - `mode: "standard"` — 200 credits, ~25 qualified leads - `mode: "extended"` — 400 credits, ~100 qualified leads Wall time ~10-15 minutes. Results delivered via signed webhook POST. Polling fallback at `GET /api/public/v1/deep-search/{job_id}/`. ## Request | Field | Type | Required | Notes | |---|---|---|---| | `query` | string | yes | Natural-language query. ≤500 chars. | | `mode` | string | no | `"standard"` (default) or `"extended"`. | | `webhook_url` | string | yes | HTTPS URL. Must resolve to a public IP. | Pre-condition: an active webhook signing secret. See [Webhook Signing Secrets](https://docs.clodo.ai/guides/webhook-secrets). `Idempotency-Key` header is supported. Same key returns the same job's current state. ## Response (202) ```json { "id": "dj_a1b2c3d4...", "status": "pending", "created_at": "2026-05-01T12:00:00Z" } ``` ## Webhook events `deep_search.completed`: ```json { "id": "dj_a1b2c3d4...", "event_type": "deep_search.completed", "results": [ { "first_name": "Patrick", "last_name": "Collison", "full_name": "Patrick Collison", "current_job_title": "CEO", "headline": "CEO at Stripe", "location": "San Francisco, California, United States", "professional_url": "linkedin.com/in/patrickcollison", "company": { "name": "Stripe", "domain": "stripe.com", "professional_url": "linkedin.com/company/stripe", "industry": "Financial Services", "employee_count": 8000 }, "email": "patrick@stripe.com", "experience": [ {"company": "Stripe", "title": "CEO", "date_range": "2010–present"} ], "score": 92 } ], "total_returned": 1 } ``` Company `professional_url` is optional and may be `null`. `deep_search.failed`: ```json { "id": "dj_a1b2c3d4...", "event_type": "deep_search.failed", "error": {"code": "error", "message": "Deep search failed; please retry."} } ``` | `error.code` | `error.message` | |---|---| | `invalid_input` | `Invalid deep-search query.` | | `error` | `Deep search failed; please retry.` | A 0-result outcome ships as `deep_search.completed` with `total_returned: 0`. Person and company `professional_url` values come back in bare-host form (no scheme, no `www.`, no trailing slash). Company `professional_url` is optional and may be `null`. ## Billing Fixed charge per run. Customer pays the full mode price on a successful run regardless of `total_returned`. Pipeline errors and dispatch failures release the reservation (no charge). ## Example ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/deep-search/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "query": "Climate tech founders in Berlin", "mode": "standard", "webhook_url": "https://yourapp.com/webhooks/clodo" }' ``` ## Rate limit `POST /api/public/v1/deep-search/`: | Limit | Value | |---|---| | Sustained rate | 4 requests / minute | | Burst | 2 requests | Polling `GET /api/public/v1/deep-search/{job_id}/`: | Limit | Value | |---|---| | Sustained rate | 3,600 requests / minute | | Burst | 100 requests | Exceeding any returns `429`. ## See also - [Expert Discovery](https://docs.clodo.ai/guides/expert-discovery) - [GTM Lead Generation](https://docs.clodo.ai/guides/gtm-lead-generation) - [Recruiting & Candidate Sourcing](https://docs.clodo.ai/guides/recruiting-candidate-sourcing) - [Webhook Signing Secrets](https://docs.clodo.ai/guides/webhook-secrets) - [Webhook Signature Verification](https://docs.clodo.ai/guides/webhook-signing) - [Async Polling](https://docs.clodo.ai/guides/async-polling) - [Idempotency](https://docs.clodo.ai/guides/idempotency) Source: https://docs.clodo.ai/api-reference/endpoint/deep-search --- # Email Enrichment ## Use case Find a professional email address from a profile URL or a name and company domain. ## Endpoint ```http POST https://api-public.clodo.ai/api/public/v1/enrich/email/ ``` Send your API key in the `x-api-key` header. See [Authentication](https://docs.clodo.ai/api-reference/authentication). ## Pricing See [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing) for credit costs and charging behavior. ## Errors For error responses and retry guidance, see [Handling Errors](https://docs.clodo.ai/api-reference/errors). `POST /api/public/v1/enrich/email/` — sync. 5 credits per call. ## Request Provide at least one of: - `professional_url` - `first_name` + `last_name` + `company_name` - `first_name` + `last_name` + `domain` > Use `professional_url`, or `first_name` + `last_name` + `domain`, to resolve an email. The name + `company_name` shape is accepted for compatibility, but without a profile URL or domain it returns `404 not_found`. Completed lookups that find no match still cost 5 credits. | Field | Type | Notes | |---|---|---| | `professional_url` | string | LinkedIn URL. | | `first_name` | string | Required when not using `professional_url`. | | `last_name` | string | Required when not using `professional_url`. | | `company_name` | string | Accepted with `first_name` + `last_name`, but cannot resolve an email without `professional_url`. Mutually exclusive with `domain`. | | `domain` | string | e.g. `acme.com`. Pair with `first_name` + `last_name`. Must contain a dot. | ## Response ```json { "email": "patrick@stripe.com" } ``` A miss returns `404 not_found` (see Status mapping below). ## Status mapping | Outcome | HTTP | Charged | |---|---|---| | Email found | `200 OK` | 5 credits | | No match found | `404 not_found` | 5 credits | | Invalid input | `400 invalid_request` | 0 | | Upstream failure | `502 upstream_error` | 0 | ## Examples By LinkedIn URL: ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/enrich/email/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{"professional_url": "https://www.linkedin.com/in/patrickcollison/"}' ``` ```python import os import requests response = requests.post( "https://api-public.clodo.ai/api/public/v1/enrich/email/", headers={"x-api-key": os.environ["CLODO_API_KEY"]}, json={"professional_url": "https://www.linkedin.com/in/patrickcollison/"}, timeout=30, ) response.raise_for_status() print(response.json()) ``` ```javascript const response = await fetch( "https://api-public.clodo.ai/api/public/v1/enrich/email/", { method: "POST", headers: { "x-api-key": process.env.CLODO_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ professional_url: "https://www.linkedin.com/in/patrickcollison/", }), }, ); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } console.log(await response.json()); ``` By profile URL with name + company context: ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/enrich/email/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "professional_url": "https://www.linkedin.com/in/patrickcollison/", "first_name": "Patrick", "last_name": "Collison", "company_name": "Stripe" }' ``` By name + domain: ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/enrich/email/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "first_name": "Patrick", "last_name": "Collison", "domain": "stripe.com" }' ``` ## Rate limit | Limit | Value | |---|---| | Sustained rate | 30 requests / minute | | Burst | 5 requests | Exceeding any returns `429`. ## See also - [Authentication](https://docs.clodo.ai/api-reference/authentication) - [Error Envelope](https://docs.clodo.ai/api-reference/errors) - [Credit Semantics](https://docs.clodo.ai/guides/credits-and-pricing) Source: https://docs.clodo.ai/api-reference/endpoint/email-enrichment --- # Phone Enrichment ## Use case Find a person’s phone number from their professional identity. ## Endpoint ```http POST https://api-public.clodo.ai/api/public/v1/enrich/phone/ ``` Send your API key in the `x-api-key` header. See [Authentication](https://docs.clodo.ai/api-reference/authentication). > This endpoint returns `202 Accepted`. Create a [webhook signing secret](https://docs.clodo.ai/guides/webhook-secrets) and supply a public HTTPS `webhook_url` before making your first request. See [Async Polling](https://docs.clodo.ai/guides/async-polling) for retrieving results. ## Pricing See [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing) for credit costs and charging behavior. ## Errors For error responses and retry guidance, see [Handling Errors](https://docs.clodo.ai/api-reference/errors). `POST /api/public/v1/enrich/phone/` — async (returns `202 Accepted`). 50 credits on hit, 0 on miss. Phone-number lookup. Typical completion time ranges from a few seconds to around a minute. Always returns 202 + webhook delivery for a uniform integration shape. Polling fallback at `GET /api/public/v1/enrich/phone/{job_id}/`. ## Request Provide at least one of: - `professional_url` - `email` - `first_name` + `last_name` + `company_name` - `first_name` + `last_name` + `domain` | Field | Type | Notes | |---|---|---| | `professional_url` | string | LinkedIn URL. | | `email` | string | Person's professional email. | | `first_name` | string | Required when not using `professional_url` or `email`. | | `last_name` | string | Required when not using `professional_url` or `email`. | | `company_name` | string | Pair with `first_name` + `last_name`. Mutually exclusive with `domain`. | | `domain` | string | e.g. `acme.com`. Pair with `first_name` + `last_name`. Must contain a dot. | | `webhook_url` | string | Required. HTTPS URL. Must resolve to a public IP. | Pre-condition: an active webhook signing secret. See [Webhook Signing Secrets](https://docs.clodo.ai/guides/webhook-secrets). `Idempotency-Key` header is supported. Same key returns the same job's current state. ## Response (202) ```json { "id": "dj_a1b2c3d4...", "status": "pending", "created_at": "2026-05-01T12:00:00Z" } ``` ## Webhook events `phone_enrich.completed` (hit): ```json { "id": "dj_a1b2c3d4...", "event_type": "phone_enrich.completed", "phone": "+14155551234" } ``` `phone_enrich.completed` (miss): ```json { "id": "dj_a1b2c3d4...", "event_type": "phone_enrich.completed", "phone": null } ``` `phone_enrich.failed`: ```json { "id": "dj_a1b2c3d4...", "event_type": "phone_enrich.failed", "error": {"code": "error", "message": "Phone enrichment failed; please retry."} } ``` | `error.code` | `error.message` | |---|---| | `invalid_input` | `Invalid phone-enrichment request.` | | `error` | `Phone enrichment failed; please retry.` | Branch on `phone === null` to detect miss, not on `event_type`. A miss is a clean completion. `phone` is E.164 normalized. ## Billing 50 credits charged on hit. 0 on miss. 0 on error or invalid input. ## Examples By LinkedIn URL: ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/enrich/phone/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "professional_url": "https://www.linkedin.com/in/patrickcollison/", "webhook_url": "https://yourapp.com/webhooks/clodo" }' ``` By email: ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/enrich/phone/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "email": "patrick@stripe.com", "webhook_url": "https://yourapp.com/webhooks/clodo" }' ``` By name + company: ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/enrich/phone/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "first_name": "Patrick", "last_name": "Collison", "company_name": "Stripe", "webhook_url": "https://yourapp.com/webhooks/clodo" }' ``` By name + domain: ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/enrich/phone/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "first_name": "Patrick", "last_name": "Collison", "domain": "stripe.com", "webhook_url": "https://yourapp.com/webhooks/clodo" }' ``` ## Rate limit `POST /api/public/v1/enrich/phone/`: | Limit | Value | |---|---| | Sustained rate | 15 requests / minute | | Burst | 3 requests | Polling `GET /api/public/v1/enrich/phone/{job_id}/`: | Limit | Value | |---|---| | Sustained rate | 3,600 requests / minute | | Burst | 100 requests | Exceeding any returns `429`. ## See also - [Webhook Signing Secrets](https://docs.clodo.ai/guides/webhook-secrets) - [Webhook Signature Verification](https://docs.clodo.ai/guides/webhook-signing) - [Async Polling](https://docs.clodo.ai/guides/async-polling) - [Idempotency](https://docs.clodo.ai/guides/idempotency) Source: https://docs.clodo.ai/api-reference/endpoint/phone-enrichment --- # Professional URL ## Use case Resolve a person’s identity to a professional profile URL. ## Endpoint ```http POST https://api-public.clodo.ai/api/public/v1/enrich/professional-url/ ``` Send your API key in the `x-api-key` header. See [Authentication](https://docs.clodo.ai/api-reference/authentication). ## Pricing See [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing) for credit costs and charging behavior. ## Errors For error responses and retry guidance, see [Handling Errors](https://docs.clodo.ai/api-reference/errors). `POST /api/public/v1/enrich/professional-url/` — sync. 3 credits per call. Resolve a person to their LinkedIn URL. ## Request Provide exactly one of: - `email` - `first_name` + `last_name` + `company_name` - `first_name` + `last_name` + `domain` These shapes are mutually exclusive. Mixing returns `400 invalid_request`. | Field | Type | Notes | |---|---|---| | `email` | string | Standalone shape. | | `first_name` | string | Pair with `last_name` and (`company_name` or `domain`). | | `last_name` | string | Pair with `first_name` and (`company_name` or `domain`). | | `company_name` | string | Pair with `first_name` + `last_name`. Mutually exclusive with `domain`. | | `domain` | string | e.g. `acme.com`. Pair with `first_name` + `last_name`. Must contain a dot. | ## Response ```json { "professional_url": "linkedin.com/in/patrickcollison" } ``` A miss returns `404 not_found` (see Status mapping below). The URL comes back in bare-host form (no scheme, no `www.`, no trailing slash). ## Status mapping | Outcome | HTTP | Charged | |---|---|---| | URL found | `200 OK` | 3 credits | | No match found | `404 not_found` | 3 credits | | Invalid input | `400 invalid_request` | 0 | | Upstream failure | `502 upstream_error` | 0 | ## Examples By email: ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/enrich/professional-url/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{"email": "patrick@stripe.com"}' ``` By name + company: ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/enrich/professional-url/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "first_name": "Patrick", "last_name": "Collison", "company_name": "Stripe" }' ``` By name + domain: ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/enrich/professional-url/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "first_name": "Patrick", "last_name": "Collison", "domain": "stripe.com" }' ``` ## Rate limit | Limit | Value | |---|---| | Sustained rate | 6 requests / minute | | Burst | 2 requests | Exceeding any returns `429`. ## See also - [Authentication](https://docs.clodo.ai/api-reference/authentication) - [Error Envelope](https://docs.clodo.ai/api-reference/errors) - [Credit Semantics](https://docs.clodo.ai/guides/credits-and-pricing) Source: https://docs.clodo.ai/api-reference/endpoint/professional-url --- # Professional Profile ## Use case Retrieve a person’s work history, education, and current role from their profile URL. ## Endpoint ```http POST https://api-public.clodo.ai/api/public/v1/enrich/professional-profile/ ``` Send your API key in the `x-api-key` header. See [Authentication](https://docs.clodo.ai/api-reference/authentication). ## Pricing See [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing) for credit costs and charging behavior. ## Errors For error responses and retry guidance, see [Handling Errors](https://docs.clodo.ai/api-reference/errors). `POST /api/public/v1/enrich/professional-profile/` — sync. 2 credits per call. Fetch a structured profile snapshot for a LinkedIn URL. ## Request | Field | Type | Required | Notes | |---|---|---|---| | `professional_url` | string | yes | LinkedIn URL. `/in/` shape. | ## Response ```json { "first_name": "Satya", "last_name": "Nadella", "full_name": "Satya Nadella", "headline": "Chairman and CEO at Microsoft", "location": { "city": "Redmond, Washington", "country": "United States", "country_code": "us" }, "current_position": { "title": "Chairman and CEO", "company_name": "Microsoft", "company_industry": "Computer Software" }, "experience": [ { "title": "Chairman and CEO", "company_name": "Microsoft", "start": "2014-02", "end": null } ], "education": [ { "school_name": "University of Wisconsin-Milwaukee", "degree": "Master’s Degree", "field_of_study": "Computer Science", "start": null, "end": null } ] } ``` Field shapes: - `location`: `{city, country, country_code}`. Any sub-field may be `null`. - `current_position`: `{title, company_name, company_industry}`, or `null` when the profile has no current position. - `experience[]`: `{title, company_name, start, end}`. `start` and `end` are `YYYY-MM` strings or `null`. - `education[]`: `{school_name, degree, field_of_study, start, end}`. Same date format. Any string field may be `null` when the source profile lacks it. ## Status mapping | Outcome | HTTP | Charged | |---|---|---| | Profile found | `200 OK` | 2 credits | | No profile found | `404 not_found` | 2 credits | | Invalid URL | `400 invalid_request` | 0 | | Upstream failure | `502 upstream_error` | 0 | ## Example ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/enrich/professional-profile/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{"professional_url": "https://www.linkedin.com/in/patrickcollison/"}' ``` ## Rate limit | Limit | Value | |---|---| | Sustained rate | 18 requests / minute | | Burst | 3 requests | Exceeding any returns `429`. ## See also - [Authentication](https://docs.clodo.ai/api-reference/authentication) - [Error Envelope](https://docs.clodo.ai/api-reference/errors) - [Credit Semantics](https://docs.clodo.ai/guides/credits-and-pricing) Source: https://docs.clodo.ai/api-reference/endpoint/professional-profile --- # Outreach Emails ## Use case Draft personalized emails, review them, and send from your connected inbox. ## Endpoint ```http POST https://api-public.clodo.ai/api/public/v1/emails/ ``` Send your API key in the `x-api-key` header. See [Authentication](https://docs.clodo.ai/api-reference/authentication). > This endpoint returns `202 Accepted`. Poll the resource to follow its progress. Outreach does not require a webhook signing secret. ## Pricing See [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing) for credit costs and charging behavior. ## Errors For error responses and retry guidance, see [Handling Errors](https://docs.clodo.ai/api-reference/errors). Create AI-drafted outreach emails, review them, and send them from your own connected inbox — the full draft → review → approve → send loop over the API. **2 credits per drafted email**, charged when the draft succeeds. Reviewing, editing, approving, cancelling, and reading are free. Emails send from a mailbox you've connected in the Clodo web app (see [Sending Accounts](https://docs.clodo.ai/api-reference/endpoint/sending-accounts)), and every email you create here also appears on your Emails page in the app — same review queue, same thread history, same open and reply tracking. This is not a raw email-sending API: you supply a contact and instructions, Clodo drafts the copy. Every recipient is saved as a lead in your workspace. ## Lifecycle ``` POST /emails/ -> 202 status: drafting GET /emails/{id}/ -> poll until status: pending_review PATCH /emails/{id}/ -> optional draft edits POST /emails/{id}/approve/ -> status: queued (then sending -> sent) ``` | Status | Meaning | |---|---| | `drafting` | The draft is being written (typically 10–60s). | | `pending_review` | Draft ready — waiting for your approval. Expires after 7 days (see `review_expires_at`). | | `queued` | Approved; waiting for its send slot. | | `sending` / `sent` | In flight / delivered to your provider. | | `failed` | Drafting or sending failed (see `error`). Failed drafts are not charged. | | `cancelled` | Cancelled by you, or expired unapproved. | Pass `"review": "none"` at create to skip the review gate — the draft goes straight to `queued` with an automatically chosen send time. The default (`"required"`) is recommended: nothing sends without an explicit approve. ## Create — `POST /emails/` | Field | Type | Notes | |---|---|---| | `to.email` | string | Required. Recipient address — becomes/updates a lead in your workspace. | | `to.first_name` | string | Required. | | `to.last_name` | string | Optional. | | `to.company_name` | string | Optional but recommended — improves the draft. | | `to.professional_url` | string | Optional LinkedIn URL. | | `to.context` | string | Optional free-text grounding, ≤1000 chars (e.g. "raised Series B last month"). | | `instructions` | string | Required, ≤2000 chars. What the email should say/do. | | `from_account` | string | Optional `ea_...` id (see [Sending Accounts](https://docs.clodo.ai/api-reference/endpoint/sending-accounts)). Defaults to your least-loaded connected account. | | `reply_to` | string | Optional Reply-To address. Must be one of **your** connected email accounts; anything else is a `400`. | | `cc` | string[] | Optional CC recipients, max 3. Visible to everyone on the thread. A reply **from** a CC'd address is not treated as the lead replying. | | `bcc` | string[] | Optional BCC recipients, max 3 (e.g. a CRM logging address). Never visible to the other recipients. | | `review` | string | `"required"` (default) or `"none"`. | ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/emails/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "to": { "email": "jackie@acme.com", "first_name": "Jackie", "company_name": "Acme", "context": "spoke at SaaStr about PLG onboarding" }, "instructions": "Casual note asking if she is open to hearing about our founding PM role." }' ``` Returns `202`: ```json { "id": "em_1f0c9a...", "status": "drafting", "created_at": "2026-07-13T00:00:00Z" } ``` `409 conflict` means you have no usable sending account — connect one in the Clodo web app first. ## Read — `GET /emails/{id}/` Poll this while `drafting`; afterwards it's the live state of the email (opens, replies, schedule). ```json { "id": "em_1f0c9a...", "status": "pending_review", "to": { "email": "jackie@acme.com", "name": "Jackie" }, "from_email": "you@yourcompany.com", "subject": "Founding PM at Acme?", "body_plain": "Hi Jackie, ...", "body_html": "

Hi Jackie, ...

", "instructions": "Casual note asking...", "created_at": "2026-07-13T00:00:00Z", "scheduled_for": null, "sent_at": null, "opened_at": null, "open_count": 0, "replied_at": null, "review_expires_at": "2026-07-20T00:00:10Z", "error": null } ``` ## List — `GET /emails/` Paginated, newest first. Filters: `?status=`, `?since=`, `?page=`, `?page_size=` (default 50, max 200). Returns `{count, page, page_size, total_pages, results}`. Only emails created through the API appear here. ```bash curl "https://api-public.clodo.ai/api/public/v1/emails/?status=replied&since=2026-07-01T00:00:00Z" \ -H "x-api-key: ck_live_..." ``` ## Edit — `PATCH /emails/{id}/` Allowed while `pending_review` or `queued` (approved but not yet sent — including scheduled follow-up steps of a running sequence). Send `subject` and/or one of `body_plain` / `body_html` (not both). HTML is sanitized server-side. Editing while `pending_review` extends the 7-day review window. Once a message is `sending` or `sent` the edit returns `409`. ```bash curl -X PATCH https://api-public.clodo.ai/api/public/v1/emails/em_1f0c9a.../ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{"subject": "Quick question about Acme"}' ``` ## Approve — `POST /emails/{id}/approve/` Only while `pending_review`. Returns the updated email. | Body | Behavior | |---|---| | `{"schedule": "best_time"}` | Default. Clodo picks the optimal send time for the recipient. | | `{"schedule": "now"}` | Next available send slot. | | `{"schedule": "at", "send_at": "2026-07-14T16:00:00Z"}` | Fixed UTC time. | ## Cancel — `POST /emails/{id}/cancel/` Allowed while `drafting`, `pending_review`, or `queued`. Cancelling mid-draft costs nothing; cancelling an already-drafted email does not refund the 2 credits (the drafting work happened). ## Status mapping | Outcome | HTTP | Charged | |---|---|---| | Draft created | `202` | 2 credits when the draft succeeds | | Draft failed | — (`status: failed` on poll) | 0 | | Invalid input | `400 invalid_request` | 0 | | No usable sending account | `409 conflict` | 0 | | Edit/approve/cancel in wrong state | `409 conflict` | 0 | | Unknown `em_` id | `404 not_found` | 0 | ## Notes - Webhooks are **optional** for these endpoints — polling is first-class. If you never register a webhook secret, everything above still works. - Daily send limits on your connected accounts always apply; approving more than the day's limit queues the remainder for the following days. - Sends stop automatically when the recipient replies (visible via `replied_at`). ## See also - [Sending Accounts](https://docs.clodo.ai/api-reference/endpoint/sending-accounts) - [Credit Semantics](https://docs.clodo.ai/guides/credits-and-pricing) - [Async Polling](https://docs.clodo.ai/guides/async-polling) Source: https://docs.clodo.ai/api-reference/endpoint/outreach-emails --- # Sequences ## Use case Create, review, and run personalized email sequences with automatic follow-ups. ## Endpoint ```http POST https://api-public.clodo.ai/api/public/v1/sequences/ ``` Send your API key in the `x-api-key` header. See [Authentication](https://docs.clodo.ai/api-reference/authentication). > This endpoint returns `202 Accepted`. Poll the resource to follow its progress. Outreach does not require a webhook signing secret. ## Pricing See [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing) for credit costs and charging behavior. ## Errors For error responses and retry guidance, see [Handling Errors](https://docs.clodo.ai/api-reference/errors). Enroll up to 100 contacts in a multi-step drafted sequence — every step is drafted up front, you review the whole thing, and one approve sets it running. **2 credits per drafted email** (e.g. 5 contacts × 3 steps = 30 credits), reserved when you create the sequence and settled when drafting finishes; failed drafts are released. Sequences require a connected **Google or Microsoft** sending account (reply detection drives stop-on-reply; SMTP can't support it — you'll get a `409`). ## How follow-ups behave - Step timing is automatic: follow-ups go out 3 / 7 / 14 days after the **previous step actually sends** (tunable per-account in the web app), at a sensible hour, skipping weekends. - A step only sends after the prior step has sent. - **A reply or bounce stops everything**: remaining steps for that contact are cancelled automatically. - Each enrolled contact becomes a lead in your workspace, and the whole sequence is visible on your Emails page in the app. ## Lifecycle ``` POST /sequences/ -> 202 status: drafting GET /sequences/{id}/ -> poll until status: pending_review PATCH /emails/{em_id}/ -> optional per-step edits (steps are regular em_ emails) POST /sequences/{id}/approve/ -> status: active (steps queued on cadence) POST /sequences/{id}/contacts/-> enroll more contacts, any time after drafting settles GET /sequences/{id}/replies/ -> inbound replies as they arrive ``` | Sequence status | Meaning | |---|---| | `drafting` | Steps are being written (~30–60s per email). | | `pending_review` | All drafts ready — waiting for approve. | | `active` | Approved and running. | | `completed` / `cancelled` / `failed` | Terminal. Per-enrollment statuses carry the detail (`replied`, `bounced`, ...). | ## Create — `POST /sequences/` | Field | Type | Notes | |---|---|---| | `contacts` | array, 1–100 | Same contact shape as [Outreach Emails](https://docs.clodo.ai/api-reference/endpoint/outreach-emails) (`email` + `first_name` required; `company_name`, `professional_url`, `context` optional). | | `instructions` | string, ≤2000 | What the sequence should pitch/do. | | `name` | string, ≤200 | Optional label for your own bookkeeping (e.g. `"LegalHR1"`). Echoed on every sequence payload; not shown to recipients. | | `steps` | int, 1–4 | Total steps including the intro. Default 3. | | `from_account` | string | Optional `ea_...` sender pin. | | `reply_to` | string | Optional Reply-To address for every step. Must be one of **your** connected email accounts (any of them — not just the sender); anything else is a `400`. Replies routed there are still detected: the sequence stops on reply as usual. | | `cc` | string[] | Optional CC recipients, max 3, applied to every step. Visible to everyone on the thread. A reply **from** a CC'd address does not stop the sequence — only the lead's reply does. | | `bcc` | string[] | Optional BCC recipients, max 3, applied to every step (e.g. a CRM logging address). Never visible to the other recipients. | | `review` | string | `"required"` (default) or `"none"` (auto-approve after drafting). | ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/sequences/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "contacts": [ {"email": "x@acme.com", "first_name": "Xola", "company_name": "Acme"}, {"email": "y@globex.com", "first_name": "Yuri", "company_name": "Globex"}, {"email": "z@initech.io", "first_name": "Zadie", "company_name": "Initech"} ], "instructions": "3-step sequence pitching our outbound API. Casual tone, developer audience.", "name": "DevRel-Q3-1", "steps": 3 }' ``` Returns `202`: ```json { "id": "sq_9c41d2...", "name": "DevRel-Q3-1", "status": "drafting", "created_at": "2026-07-13T00:00:00Z", "contacts_accepted": 3, "contacts_rejected": [], "total_emails": 9, "drafted": 0 } ``` **Partial accept:** ineligible contacts are skipped, not fatal — `contacts_rejected` lists each with a reason (`already_in_automation` — the person is in another active sequence; `not_enriched`; `recently_contacted`; `invalid_contact`). Only if *no* contact is eligible does the call fail. ## Add contacts — `POST /sequences/{id}/contacts/` Enroll 1–100 additional contacts into an existing sequence — including after it's active. New enrollments inherit the sequence's **original instructions and step plan** and start at step 0 on their own cadence clock; existing enrollments are untouched. **2 credits per newly drafted email**, reserved on the call and settled when the new drafts finish. | Field | Type | Notes | |---|---|---| | `contacts` | array, 1–100 | Same contact shape as create. | | `from_account` | string | Optional `ea_...` sender pin for the new enrollments. | New enrollments also inherit the sequence's `reply_to`, `cc`, and `bcc` (if set at create time) — appended contacts' threads behave the same as everyone else's. | `review` | string | `"required"` (default) or `"none"`. `"none"` auto-approves **every** pending enrollment in the sequence once drafting finishes. | ```bash curl -X POST https://api-public.clodo.ai/api/public/v1/sequences/sq_9c41d2.../contacts/ \ -H "x-api-key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{"contacts": [{"email": "w@umbrella.co", "first_name": "Wren", "company_name": "Umbrella"}]}' ``` Returns `202`: ```json { "id": "sq_9c41d2...", "name": "DevRel-Q3-1", "status": "drafting", "contacts_accepted": 1, "contacts_rejected": [], "emails_added": 3 } ``` Behavior notes: - The sequence's top-level status returns to `drafting` while the new enrollments draft, then settles back (per-enrollment statuses stay accurate throughout — poll the detail). - With `review: "required"`, call `approve/` again after drafting — it activates only the newly drafted enrollments (already-active ones are skipped). Per-step copy edits via `PATCH /emails/{em_id}/` work on the new drafts before approval, same as at create. - `409` while a drafting pass is running (retry shortly), and for `cancelled` / `failed` sequences (create a new one instead). - Contacts already enrolled in this sequence are rejected with reason `already_enrolled`; the other rejection reasons match create. - A sequence holds at most **500** enrollments. ## Read — `GET /sequences/{id}/` ```json { "id": "sq_9c41d2...", "name": "DevRel-Q3-1", "status": "pending_review", "instructions": "3-step sequence pitching...", "total_emails": 9, "drafted": 9, "failed": 0, "enrollments": [ { "contact": {"email": "x@acme.com", "name": "Xola"}, "status": "pending_review", "emails": [ {"id": "em_...", "step": 0, "status": "pending_review", "subject": "...", "body_plain": "...", ...}, {"id": "em_...", "step": 1, ...}, {"id": "em_...", "step": 2, ...} ] } ] } ``` Each step is a regular outreach email — the `em_` ids work against `GET /emails/{id}/` and `PATCH /emails/{id}/` for per-step edits. Edits work before approving AND after — any step whose status is `pending_review` or `queued` accepts a `PATCH`, so you can rewrite upcoming follow-ups on a live sequence. Every other status (`drafting`, `sending`, `sent`, `failed`, `cancelled`) returns `409`. `GET /sequences/` lists your sequences (paginated, no enrollments — fetch the detail). ## Approve — `POST /sequences/{id}/approve/` Approves every drafted enrollment: step 0 queues at its computed best time, later steps follow the cadence after each prior send. `409` while still drafting. ## Cancel — `POST /sequences/{id}/cancel/` Cancels all non-terminal enrollments and their unsent steps. Already-sent steps are unaffected. No refunds for drafted emails (the drafting work happened). ## Replies — `GET /sequences/{id}/replies/` ```json { "replies": [ { "email_id": "em_...", "from_email": "x@acme.com", "from_name": "Xola", "subject": "Re: your outbound API", "body_plain": "Interested — got time Thursday?", "classification": "reply", "received_at": "2026-07-16T14:03:00Z" } ] } ``` `classification` is `reply`, `bounce`, or `auto_reply`. The corresponding enrollment will already show status `replied`/`bounced` with its remaining steps cancelled. ## Status mapping | Outcome | HTTP | Charged | |---|---|---| | Sequence created | `202` | 2 credits × drafts that succeed | | All contacts rejected | `400 invalid_request` | 0 | | No Google/Microsoft account | `409 conflict` | 0 | | Approve while drafting | `409 conflict` | 0 | | Unknown `sq_` id | `404 not_found` | 0 | ## See also - [Outreach Emails](https://docs.clodo.ai/api-reference/endpoint/outreach-emails) — the one-off flow and per-step edit semantics - [Sending Accounts](https://docs.clodo.ai/api-reference/endpoint/sending-accounts) - [Credit Semantics](https://docs.clodo.ai/guides/credits-and-pricing) Source: https://docs.clodo.ai/api-reference/endpoint/sequences --- # Sending Accounts ## Use case Check your connected mailboxes before creating outreach emails or sequences. ## Endpoint ```http GET https://api-public.clodo.ai/api/public/v1/email/accounts/ ``` Send your API key in the `x-api-key` header. See [Authentication](https://docs.clodo.ai/api-reference/authentication). ## Pricing See [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing) for credit costs and charging behavior. ## Errors For error responses and retry guidance, see [Handling Errors](https://docs.clodo.ai/api-reference/errors). `GET /api/public/v1/email/accounts/` — sync. Free. Outreach emails send from **your own connected mailbox** (Google, Microsoft, or SMTP) — your address, your sending reputation. Accounts are connected and managed in the Clodo web app (Settings → Email Accounts); the API surface is read-only. Use this endpoint to check that a usable inbox exists before creating emails. ## Response ```json { "accounts": [ { "id": "ea_65006cf5b9b8c2af", "email": "you@yourcompany.com", "type": "google", "connected": true, "sending_enabled": true, "daily_limit": 35, "sent_today": 12, "health": "healthy" } ] } ``` | Field | Notes | |---|---| | `id` | Stable `ea_...` handle. Pass as `from_account` when creating an email to pin the sender. | | `type` | `google`, `microsoft`, or `smtp`. | | `connected` | `false` means the account needs attention in the web app (e.g. re-auth). | | `daily_limit` | Effective sends allowed today (accounts warm up gradually and throttle on poor engagement). | | `health` | `healthy`, or a state describing what needs fixing (e.g. `reauth_required`). | If the list is empty (or nothing is `connected` + `sending_enabled`), `POST /emails/` returns `409 conflict` — connect an inbox in the web app first. ## See also - [Outreach Emails](https://docs.clodo.ai/api-reference/endpoint/outreach-emails) Source: https://docs.clodo.ai/api-reference/endpoint/sending-accounts --- # Webhook Signing Secrets Async endpoints (People Search, Deep Search, Phone Enrichment) POST results to a webhook URL you supply per request. Each delivery is signed with HMAC-SHA256 using your webhook signing secret. An active webhook signing secret is required to call any async endpoint. Calls without one fail with `409 conflict`. ## Minting 1. Open the **API Keys** tab. 2. Find the **Webhook Signing Secret** section. 3. Click **Mint webhook secret**. 4. Copy the raw secret. It is shown once. The Console displays the secret's prefix and version after creation. ## Rotating Click **Rotate** to mint a new secret and invalidate the old one immediately. ## API key vs webhook signing secret | Thing | Used for | Wire format | |---|---|---| | API key (`ck_live_...`) | Authenticating your requests to us | `x-api-key` header on outbound requests | | Webhook signing secret | Verifying webhook deliveries from us to you | `Clodo-Signature` header on inbound webhook POSTs | ## Scope One active webhook signing secret per account. All webhook deliveries to the account use it. ## Headers we send on every webhook | Header | Value | |---|---| | `User-Agent` | `Clodo-Webhook/1.0` | | `Content-Type` | `application/json` | | `Clodo-Signature` | `t=,v1=` (HMAC over `.`) | | `Clodo-Webhook-Event` | Event type (e.g. `search.completed`). Lets you route on a header without parsing the body. | | `Clodo-Webhook-Id` | Numeric delivery ID. Stable across retry attempts of the same delivery. | ## Wire format The body bytes are canonicalized: keys sorted alphabetically, no whitespace. Sign and verify the bytes you receive on the wire, not a re-serialized JSON dict (key order would diverge and HMAC would fail). ## What to read next - [Webhook Signature Verification](https://docs.clodo.ai/guides/webhook-signing) for the HMAC algorithm and verifier code. - [Webhook Events](https://docs.clodo.ai/guides/webhook-events) for event types and payload shapes. - [Webhook Retry Policy](https://docs.clodo.ai/guides/webhook-retries) for retry schedule. Source: https://docs.clodo.ai/guides/webhook-secrets --- # Webhook Signature Verification Every webhook delivery includes a `Clodo-Signature` header. Verify it against the raw request body using your webhook signing secret. ## Header format ``` Clodo-Signature: t=,v1= ``` - `t` — unix seconds when we signed the request. - `v1` — lowercase hex HMAC-SHA256 over `.`, keyed with your secret. ## Algorithm 1. Read the raw bytes of the request body. **Do not parse and re-serialize** — key order would change and the HMAC would not match. 2. Parse `t` and `v1` from the `Clodo-Signature` header. 3. Reject if `abs(now - t) > 300` (5-minute replay window). 4. Compute `expected = HMAC_SHA256(secret_utf8, f"{t}.".encode("ascii") + body_bytes).hexdigest()`. 5. Constant-time compare `expected` with `v1`. ## Python verifier ```python import hashlib import hmac import time TOLERANCE_SECONDS = 5 * 60 def verify(secret: str, body: bytes, signature_header: str) -> bool: parts = dict(p.split("=", 1) for p in signature_header.split(",")) if "t" not in parts or "v1" not in parts: return False try: t = int(parts["t"]) except ValueError: return False if abs(int(time.time()) - t) > TOLERANCE_SECONDS: return False signed = f"{t}.".encode("ascii") + body expected = hmac.new(secret.encode("utf-8"), signed, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, parts["v1"]) ``` Flask example: ```python @app.post("/webhooks/clodo") def receive(): body = request.get_data() # raw bytes if not verify(SECRET, body, request.headers.get("Clodo-Signature", "")): return "", 401 payload = json.loads(body) # ... handle payload ... return "", 200 ``` ## Node verifier ```javascript import crypto from "node:crypto"; const TOLERANCE_SECONDS = 5 * 60; export function verify(secret, body, signatureHeader) { const parts = Object.fromEntries( signatureHeader.split(",").map((p) => p.split("=", 2)), ); if (!parts.t || !parts.v1) return false; const t = parseInt(parts.t, 10); if (!Number.isFinite(t)) return false; if (Math.abs(Math.floor(Date.now() / 1000) - t) > TOLERANCE_SECONDS) return false; const signed = Buffer.concat([Buffer.from(`${t}.`, "ascii"), body]); const expected = crypto .createHmac("sha256", secret) .update(signed) .digest("hex"); const a = Buffer.from(expected, "hex"); const b = Buffer.from(parts.v1, "hex"); return a.length === b.length && crypto.timingSafeEqual(a, b); } ``` Express example (use `express.raw` so `req.body` stays as bytes): ```javascript app.post( "/webhooks/clodo", express.raw({ type: "application/json" }), (req, res) => { const ok = verify(SECRET, req.body, req.get("Clodo-Signature") || ""); if (!ok) return res.status(401).end(); const payload = JSON.parse(req.body.toString("utf8")); // ... handle payload ... res.status(200).end(); }, ); ``` ## Common mistakes - Parsing the JSON before verifying. Most frameworks consume the body as a stream; once consumed it is gone. Use raw-body middleware (`express.raw`, `request.get_data()`, etc.) and verify against the bytes. - Comparing strings with `==`. Use `hmac.compare_digest` (Python) or `crypto.timingSafeEqual` (Node). - Trusting `t` without bounds-checking. The 5-minute window is the replay defense. ## See also - [Webhook Signing Secrets](https://docs.clodo.ai/guides/webhook-secrets) for minting and rotating the secret. - [Webhook Events](https://docs.clodo.ai/guides/webhook-events) for the catalog of events and their payload shapes. - [Webhook Retry Policy](https://docs.clodo.ai/guides/webhook-retries) for retry schedule. Source: https://docs.clodo.ai/guides/webhook-signing --- # Webhook Events Catalog of every event we deliver. All bodies are alphabetically key-sorted + minified on the wire — see [Webhook Signing Secrets](https://docs.clodo.ai/guides/webhook-secrets). ## search.completed Fired when a People Search run finishes successfully (including 0-result runs). ```json { "id": "dj_a1b2c3d4...", "event_type": "search.completed", "results": [ { "first_name": "Mark", "last_name": "Wang", "full_name": "Mark Wang", "current_job_title": "Software Engineer", "headline": "Software Engineer", "location": "San Francisco, California, United States", "professional_url": "linkedin.com/in/mark-wang-8a8a2213", "company": { "name": "OpenAI", "domain": "openai.com", "professional_url": "linkedin.com/company/openai", "industry": "Research Services", "employee_count": 8626 } } ], "total_returned": 1 } ``` Company `professional_url` is optional and may be `null`. A 0-result outcome ships with `total_returned: 0` and `results: []`. ## search.failed Fired when a People Search run fails before producing results. ```json { "id": "dj_a1b2c3d4...", "event_type": "search.failed", "error": {"code": "error", "message": "People search failed; please retry."} } ``` | `error.code` | `error.message` | |---|---| | `invalid_input` | `Invalid search query.` | | `error` | `People search failed; please retry.` | ## deep_search.completed Fired when a Deep Search run finishes successfully (including 0-result runs). ```json { "id": "dj_a1b2c3d4...", "event_type": "deep_search.completed", "results": [ { "first_name": "Patrick", "last_name": "Collison", "full_name": "Patrick Collison", "current_job_title": "CEO", "headline": "CEO at Stripe", "location": "San Francisco, California, United States", "professional_url": "linkedin.com/in/patrickcollison", "company": { "name": "Stripe", "domain": "stripe.com", "professional_url": "linkedin.com/company/stripe", "industry": "Financial Services", "employee_count": 8000 }, "email": "patrick@stripe.com", "experience": [ {"company": "Stripe", "title": "CEO", "date_range": "2010–present"} ], "score": 92 } ], "total_returned": 1 } ``` `email`, `headline`, `experience`, and `company.professional_url` may be empty (`""`, `[]`, or `null`) on a per-lead basis. Person and company `professional_url` values use bare-host form. ## deep_search.failed ```json { "id": "dj_a1b2c3d4...", "event_type": "deep_search.failed", "error": {"code": "error", "message": "Deep search failed; please retry."} } ``` | `error.code` | `error.message` | |---|---| | `invalid_input` | `Invalid deep-search query.` | | `error` | `Deep search failed; please retry.` | ## agentic_search.completed Fired when an Agentic Search finishes, including zero-result and partial deliveries. The payload contains `id`, `event_type: "agentic_search.completed"`, `results`, `total_returned`, `partial`, and `completion_reason`. Each person may include professional details, company, experience, `fit_summary`, `proof_points`, `evidence`, and `tier`. See [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search) for the complete example and field descriptions. When `partial` is `true`, inspect `completion_reason`: `budget_exhausted`, `deadline_exceeded`, or `user_cancelled`. A partial delivery can still be billable within the requested credit cap. ## agentic_search.failed ```json { "id": "dj_a1b2c3d4e5f6478890abcdef123456789", "event_type": "agentic_search.failed", "error": {"code": "error", "message": "Internal error — you were not charged."} } ``` | `error.code` | Meaning | |---|---| | `error` | Internal failure; no charge is retained. | | `stale_running_1h` | Job timed out and was force-failed; the credit hold was refunded. | | `admission_expired` | The job's billing authorization is no longer valid. Check billing and submit a new request. | | `settlement_failed` | Billing could not settle the request. No result is delivered and no charge is retained. | See [Async Polling](https://docs.clodo.ai/guides/async-polling) if webhook delivery is unavailable. ## phone_enrich.completed Fired for both hits and clean misses. Branch on `phone === null` to detect a miss. Hit: ```json { "id": "dj_a1b2c3d4...", "event_type": "phone_enrich.completed", "phone": "+14155551234" } ``` Miss: ```json { "id": "dj_a1b2c3d4...", "event_type": "phone_enrich.completed", "phone": null } ``` `phone` is E.164 normalized. ## phone_enrich.failed Fired only on transport / pipeline errors. ```json { "id": "dj_a1b2c3d4...", "event_type": "phone_enrich.failed", "error": {"code": "error", "message": "Phone enrichment failed; please retry."} } ``` | `error.code` | `error.message` | |---|---| | `invalid_input` | `Invalid phone-enrichment request.` | | `error` | `Phone enrichment failed; please retry.` | ## See also - [Webhook Signature Verification](https://docs.clodo.ai/guides/webhook-signing) for HMAC verifier code. - [Webhook Retry Policy](https://docs.clodo.ai/guides/webhook-retries) for retry schedule and what we treat as transient vs permanent. - [Async Polling](https://docs.clodo.ai/guides/async-polling) for the polling-fallback envelope. Source: https://docs.clodo.ai/guides/webhook-events --- # Webhook Retry Policy ## Schedule Three attempts max. Schedule: | Attempt | When | |---|---| | 1 | At result-ready time | | 2 | 5 seconds after attempt 1 fails | | 3 | 5 minutes after attempt 2 fails | After attempt 3 fails, the delivery is marked `failed` and not retried. A 1-hour hard ceiling from the first attempt also caps total time. If a delivery is still pending past 1 hour after first attempt, it is force-failed. ## Success Any 2xx response from your endpoint marks the delivery `delivered`. We do not retry on 2xx. ## What we retry Transient failures get re-enqueued per the schedule above: - `5xx` HTTP responses - Connection errors (DNS resolution, refused, reset, peer reset) - Read or connect timeouts (5s connect / 20s read per attempt) - Other transport-level errors ## What we do NOT retry Permanent failures move straight to `failed`: - `4xx` HTTP responses (treated as "your code rejected this delivery; retrying won't help") - TLS / certificate errors (expired, hostname mismatch, custom CA) - SSRF rejections at delivery time (URL resolved to a private / loopback / IMDS address) - Your account has no active webhook signing secret ## Headers each retry sends Every attempt for a given delivery sends the **same** `Clodo-Webhook-Id` header. Use that value on your end to dedup if you process and a 2xx response is lost in flight. The `Clodo-Signature` is recomputed per attempt with a fresh `t=`. Within the 5-minute replay window the signature stays valid against the same body bytes. ## Reconciling lost deliveries If your receiver is down past the retry window, the delivery is `failed` but the underlying job result is still durable. Use [Async Polling](https://docs.clodo.ai/guides/async-polling) to fetch the result via `GET /v1//{job_id}/`. The polling response carries the same body as the webhook would have delivered (without the `id` / `event_type` wrapper). ## See also - [Webhook Signing Secrets](https://docs.clodo.ai/guides/webhook-secrets) - [Webhook Signature Verification](https://docs.clodo.ai/guides/webhook-signing) - [Webhook Events](https://docs.clodo.ai/guides/webhook-events) - [Async Polling](https://docs.clodo.ai/guides/async-polling) Source: https://docs.clodo.ai/guides/webhook-retries --- # People Search API Use Cases Build workflows for B2B lead generation, candidate sourcing, and expert discovery with the Clodo API. Start by describing the people you need, choose the search endpoint that fits the task, enrich selected profiles, and prepare outreach from a connected mailbox. ## Describe the exact person you have in mind Get specific about the combination that matters: someone at a company using a particular technology, who has solved a particular problem, worked in a particular environment, or published or presented relevant work. Include examples, exclusions, priorities, and dates. An ICP or candidate persona can describe a story about the person's experience, not just a list of profile attributes. For example: “Find fintech data leaders at companies using Snowflake who previously built banking infrastructure. Prioritize people who have given a talk about a migration since January 2025, and include supporting sources.” [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search) is the starting point for this level of detail. Review the returned evidence against your requirements and keep unverified criteria as open questions. ## Choose a workflow | Goal | Guide | What you will build | |---|---|---| | Find prospects for your product | [GTM Lead Generation](https://docs.clodo.ai/guides/gtm-lead-generation) | Turn an ideal customer profile into a prospect list, resolve email addresses, and draft relevant sales outreach. | | Source people for an open role | [Recruiting & Candidate Sourcing](https://docs.clodo.ai/guides/recruiting-candidate-sourcing) | Translate a job description into search criteria, research candidates, and prepare recruiter outreach. | | Find people with first-hand expertise | [Expert Discovery](https://docs.clodo.ai/guides/expert-discovery) | Identify practitioners for research interviews, advisory projects, and industry discussions, then review their experience. | ## Which people search endpoint should I use? | Endpoint | Best starting point | How results help | |---|---|---| | [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search) | A detailed combination of company tech stack, professional experience, accomplishments, public work, priorities, and exclusions | Researches matches and returns fit summaries, proof points, and source evidence. Set a maximum credit budget. Account enrollment is required. | | [People Search](https://docs.clodo.ai/api-reference/endpoint/people-search) | A professional audience described by role, seniority, company, industry, or geography | Returns matching professional profiles. Continue a completed search to retrieve more non-overlapping results. | | [Deep Search](https://docs.clodo.ai/api-reference/endpoint/deep-search) | A focused search that needs a researched shortlist | Returns professional context, work experience, and fit scores, with standard and extended modes priced per successful run. | Use the endpoint that fits the information you need. You do not need to call all three for every workflow. If a broad list answers your question, start with People Search. If the distinction between a good and a poor match depends on specific experience, describe it explicitly in Agentic Search or Deep Search. ## Before you start 1. [Create your account and get an API key](https://docs.clodo.ai/get-api-key). Run API calls on your server and send the key in the `x-api-key` header. 2. Create a [webhook signing secret](https://docs.clodo.ai/guides/webhook-secrets) and provide a public HTTPS receiver for search jobs. Verify [webhook signatures](https://docs.clodo.ai/guides/webhook-signing) before processing results. 3. Store each accepted job ID. Use [Async Polling](https://docs.clodo.ai/guides/async-polling) if you need to retrieve its status or results directly. 4. Check [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing) before choosing result counts, search modes, or an Agentic Search cap. 5. If you plan to draft outreach, connect a mailbox and check [Sending Accounts](https://docs.clodo.ai/api-reference/endpoint/sending-accounts). Search alone does not require a sending account. ## Connect the steps in your application **Search → review matches → enrich selected people → draft outreach → approve sending.** Save the search criteria alongside each job ID so you can explain why a person was included. On completion, retain the professional URL, company, and relevant search context. Use those fields to deduplicate your own records and decide who needs further research. [Professional Profile Enrichment](https://docs.clodo.ai/api-reference/endpoint/professional-profile) retrieves structured work history and education. [Email Enrichment](https://docs.clodo.ai/api-reference/endpoint/email-enrichment) resolves an email from a professional URL or a name and company domain. Enrich only the people you intend to review or contact; search results do not guarantee a usable email address. [Outreach Emails](https://docs.clodo.ai/api-reference/endpoint/outreach-emails) turns recipient details and instructions into a draft. Set `review: "required"`, inspect the draft, and approve it when ready. For follow-up workflows, see [Sequences](https://docs.clodo.ai/api-reference/endpoint/sequences). ## Questions about building people discovery workflows ### Can I use one API for lead generation and recruiting? Yes. The search endpoints accept descriptions of people rather than a fixed sales-only or recruiting-only request format. Change the query and your downstream review criteria to suit the workflow. The guides show different examples for each audience. ### Does finding someone automatically contact them? No. Search and enrichment do not send emails. Outreach requires a separate request and a connected sending account. With `review: "required"`, a draft waits for an explicit approval before entering the send queue. ### Can I connect results to my CRM, ATS, or research database? Your application can map returned JSON into its own records and write to those systems through their supported APIs. These guides describe that application step; the search endpoints do not automatically create CRM records, ATS candidates, or research projects. Source: https://docs.clodo.ai/guides/use-cases --- # GTM Lead Generation with the Clodo API Turn an ideal customer profile into a list of people to contact. This guide connects natural-language people search, lead qualification, email enrichment, and personalized outreach for sales prospecting and go-to-market workflows. Your ICP can be as specific as the problem your product solves. Combine the person's responsibilities, their company's technology stack, past experience, and relevant public work. For example, look for an engineering leader at a fintech using Snowflake who previously built data infrastructure at a bank and has spoken publicly about data-platform migrations. ## 1. Translate your ICP into a people search Describe the people you would want to meet if you could write the exact criteria yourself. Start with who owns the problem, then explain the combination of company context, technology, experience, and public evidence that would make them a strong fit. Include must-haves, preferences, exclusions, and a time window when recency matters. You do not need to reduce a nuanced persona to a job title and location. “People who have done X, at companies using Y, with evidence of Z” is a useful way to write an [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search) query. Its `query` field supports up to 2,000 characters, so use that space to explain what a good match looks like. | Starting point | More useful search | |---|---| | Manufacturing leads | Operations leaders at US manufacturers who have taken warehouse automation from pilot to production; prioritize people who have described the implementation in a public case study | | Prospects for data infrastructure | Data-platform leaders at fintech companies using Snowflake, with prior experience building data infrastructure at a bank | | Prospects for developer tooling | Engineering leaders at B2B software companies using Kubernetes who have given a conference talk about platform engineering; exclude agencies and consultants | | Security decision-makers | Security leaders at UK financial-services companies who previously led a cloud-security program and have published an article or spoken about its implementation | Make the evidence requirement explicit: identify the technology, experience, publication, or event that matters, and ask for sources supporting the match. A query expresses what you want found; review the returned evidence to establish which criteria were actually verified. Missing evidence is an unanswered qualification question. Publicly accessible posts can also provide context: ask for people who have written about a particular implementation problem, and include a known post URL when you have one. A person's own public writing and the list of people who liked a post are different sources; the latter requires social-engagement access beyond the current public API. ### Example: combine tech-stack signals, career history, and public work For an enrolled account, send this body to `POST /api/public/v1/agentic-search/` after completing the [setup checklist](https://docs.clodo.ai/guides/use-cases). Replace the example webhook URL with your public HTTPS receiver. ```json { "query": "Find data-platform leaders at US or UK fintech companies using Snowflake. Prioritize people who previously built data infrastructure at a bank and have written an engineering article or given a conference talk about a data-platform migration since January 2025. Exclude consultants and agencies. Company use of Snowflake is required; prior banking experience and public migration work are preferences. Include sources supporting the technology and experience matches, and distinguish missing evidence from confirmed facts.", "target_results": 25, "max_credits": 500, "webhook_url": "https://your-app.example/webhooks/clodo" } ``` Wait for `agentic_search.completed`, or poll `GET /api/public/v1/agentic-search/{job_id}/`. Review `fit_summary`, `proof_points`, and `evidence`, together with `partial` and `completion_reason`. Carry the supported context into qualification and outreach. You can proceed directly to step 3 with these results. ## 2. Build the initial prospect list For a broader first pass, use [People Search](https://docs.clodo.ai/api-reference/endpoint/people-search). This is an alternative starting point to the detailed Agentic Search above, useful when a role and company description is enough to build your initial pool. After completing the [setup checklist](https://docs.clodo.ai/guides/use-cases), send this JSON body to `POST /api/public/v1/search/` with your `x-api-key` header. Replace the example receiver with your public HTTPS webhook URL. ```json { "query": "Plant managers and operations directors at industrial manufacturing companies in the United States with 200 to 2000 employees.", "max_results": 50, "webhook_url": "https://your-app.example/webhooks/clodo" } ``` Store the `id` from the `202` response. Wait for `search.completed`, or poll `GET /api/public/v1/search/{job_id}/`. A completed webhook contains `results`; a completed polling response places them in `result.results`. Inspect failures and terminal status before treating a job as ready. Need a researched shortlist? [Deep Search](https://docs.clodo.ai/api-reference/endpoint/deep-search) adds professional context and fit scores. For the detailed combination of technology, experience, and public work shown above, use [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search) and assess the evidence for each criterion. ## 3. Qualify leads before enriching contact details Keep each prospect's name, professional URL, current role, and company in your application. Compare them with your ICP and remove people outside your target audience. Your own CRM can hold the review status and campaign membership; Clodo search does not write those records automatically. Prioritize people whose responsibilities relate to the problem your product solves. A matching title is a starting point for qualification, not proof of budget or purchase intent. When more background would change your decision, call [Professional Profile Enrichment](https://docs.clodo.ai/api-reference/endpoint/professional-profile) to inspect work history. ## 4. Find an email for a selected prospect Send the person's returned `professional_url` to `POST /api/public/v1/enrich/email/`. This is a synchronous request. The value below is a placeholder; use the actual profile URL you selected. ```json { "professional_url": "https://www.linkedin.com/in/selected-prospect" } ``` A `200` returns `email`. A `404` means no email was resolved; leave the contact unresolved instead of inventing an address. Completed email lookups cost 5 credits even when there is no match, so enrich after qualification. See [Email Enrichment](https://docs.clodo.ai/api-reference/endpoint/email-enrichment) for accepted input combinations and other errors. ## 5. Draft a relevant first touch Connect a mailbox and verify it appears in [Sending Accounts](https://docs.clodo.ai/api-reference/endpoint/sending-accounts). Send a separate request to `POST /api/public/v1/emails/`, using the resolved email and accurate recipient details. The example below shows the structure, not a real recipient. ```json { "to": { "email": "alex@manufacturer.example", "first_name": "Alex", "company_name": "Example Manufacturing", "context": "Operations director at an industrial manufacturer." }, "instructions": "Write a short introduction to our production-planning software. Connect it to the recipient's operations role without assuming they have a particular problem. Ask whether reducing manual scheduling work is a current priority. Do not claim we have met or invent company news.", "review": "required" } ``` Poll `GET /api/public/v1/emails/{id}/` until the email reaches `pending_review` or another terminal outcome. Review the subject and body, edit if needed, then call `POST /api/public/v1/emails/{id}/approve/` to queue it for sending. Approval is a separate action; creating this draft does not send it. See [Outreach Emails](https://docs.clodo.ai/api-reference/endpoint/outreach-emails) for the full lifecycle and [Sequences](https://docs.clodo.ai/api-reference/endpoint/sequences) for follow-ups. ## 6. Expand the campaign without repeating the same page To continue a completed People Search, send its job ID as `search_id` with `max_results` and `webhook_url`. Omit `query` for the continuation. Clodo returns the next non-overlapping page for that search. Still deduplicate records in your own application when combining different searches or campaigns. Use [Idempotency](https://docs.clodo.ai/guides/idempotency) for retrying search requests and respect [Rate Limits](https://docs.clodo.ai/api-reference/rate-limits). Keep search, enrichment, and drafting charges separate in your budget; a list of 50 prospects does not require 50 enrichment calls or 50 drafts. ## Lead generation questions ### How do I find B2B sales leads with an API? Describe the decision-maker and target company in People Search, review returned profiles against your ICP, then enrich contact details for the prospects you select. This separates audience discovery from the decision to contact someone. ### Can I search for decision-makers at target accounts? Include the company or account criteria and relevant functions in your query. Review returned company and role fields before adding someone to an account-based sales campaign. For complex qualification, use Agentic Search or Deep Search. ### How specific can my ideal customer profile be? Combine multiple criteria in plain language: company technology, current responsibilities, previous employers, implementation experience, published work, conference participation, geography, and recency. Use Agentic Search for a detailed persona and state which conditions are required versus preferred. Specificity guides the research; it does not guarantee that every condition has a publicly verifiable match. ### Does this workflow identify buying intent? A role or company match does not establish buying intent. If a particular public activity matters, describe it in an Agentic Search query and assess any supporting evidence. Do not treat an unsupported assumption as a verified signal. ## Related workflows - [Recruiting & Candidate Sourcing](https://docs.clodo.ai/guides/recruiting-candidate-sourcing): build talent pipelines rather than prospect lists. - [Expert Discovery](https://docs.clodo.ai/guides/expert-discovery): find practitioners for interviews and advisory projects. - [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing): plan the cost of each step. Source: https://docs.clodo.ai/guides/gtm-lead-generation --- # Recruiting and Candidate Sourcing with the Clodo API Build a candidate sourcing workflow that starts with a role description and ends with a reviewed shortlist and recruiter outreach. Use Clodo to find people with relevant professional experience, inspect their backgrounds, and explain why they may fit the role. This example searches for engineering leaders who have brought robotics systems into production. The same flow works for specialized individual contributors, industry operators, and leadership searches when you change the job-related criteria. ## 1. Turn a job description into a searchable persona Separate requirements from preferences. Tell the search what the person needs to have done, the environment they worked in, and which experience is optional. Avoid relying on a title alone: two engineering managers can have very different technical and operating backgrounds. Get specific enough to describe the person your team would be excited to meet: “An engineer who has operated Kubernetes in a regulated financial-services environment, previously worked at an early-stage company, and has presented a production incident case study.” Combine technical experience, career history, and public work; explain which are requirements and which would strengthen a match. Ask for evidence of the actual work, since employment at a company using a technology does not establish personal experience with it. | Hiring requirement | Search detail | |---|---| | Relevant technical experience | Deployed autonomous robotics systems in a production environment | | Leadership background | Led an engineering team through a product launch | | Location | Based in the United States | | Preference | Prior warehouse or industrial robotics experience | Keep the query focused on role-relevant evidence. Search results support sourcing and professional review; they do not establish availability, interest, or willingness to relocate. ## 2. Search for candidates with specific experience [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search) is the canonical endpoint for a detailed persona. It returns fit summaries, proof points, and evidence that help a recruiter understand the match. It requires account enrollment; see its availability section before starting. Complete the [API and webhook setup](https://docs.clodo.ai/guides/use-cases), then send this body to `POST /api/public/v1/agentic-search/` with `x-api-key`. Replace the example webhook URL with your receiver. ```json { "query": "Find US-based engineering leaders who have taken autonomous robotics systems from prototype to production and led an engineering team through a product launch. Prioritize warehouse or industrial robotics experience. Exclude recruiting firms and consultants. Explain the relevant experience with supporting evidence.", "target_results": 25, "max_credits": 500, "webhook_url": "https://your-app.example/webhooks/clodo" } ``` Save the returned job ID. Wait for `agentic_search.completed`, or poll `GET /api/public/v1/agentic-search/{job_id}/`. The requested result count is a target, not a guarantee. Check `partial` and `completion_reason` before treating the search as complete in your recruiting workflow. For a broader candidate pool defined by title, industry, and location, use [People Search](https://docs.clodo.ai/api-reference/endpoint/people-search). For a researched shortlist with fit scores and fixed pricing per successful run, use [Deep Search](https://docs.clodo.ai/api-reference/endpoint/deep-search). ## 3. Build an evidence-based candidate review For each result, retain the professional URL, current role, company, `fit_summary`, `proof_points`, and `evidence`. In your own recruiting application, show the original requirement next to the supporting information so reviewers can distinguish a demonstrated match from missing information. An empty field means information was unavailable; it does not prove that the candidate lacks the experience. Use the evidence to decide what to verify in a conversation. Keep hiring decisions with the recruiting team and evaluate candidates against the same job-related criteria. If the search stops early, review the saved candidates or refine the next query. To stop a running Agentic Search, call `POST /api/public/v1/agentic-search/{job_id}/cancel/` and poll until terminal. Work already performed may be billable within the credit cap; cancellation does not necessarily erase the charge. ## 4. Enrich shortlisted professional profiles Call `POST /api/public/v1/enrich/professional-profile/` for candidates who need further review, using the professional URL from search. ```json { "professional_url": "https://www.linkedin.com/in/selected-candidate" } ``` The placeholder above must be replaced with a real result. [Professional Profile Enrichment](https://docs.clodo.ai/api-reference/endpoint/professional-profile) returns structured current position, work history, education, and location when available. Preserve missing values rather than filling them with assumptions. Map the results into your ATS or candidate database through your application's integration. Store the source URL and search context alongside the candidate record. The Clodo search endpoint does not create an ATS record automatically. ## 5. Prepare recruiter outreach Use [Email Enrichment](https://docs.clodo.ai/api-reference/endpoint/email-enrichment) to resolve an email from a selected professional URL. A lookup can return no match; do not create an outreach request until you have a usable address. Connect a [sending account](https://docs.clodo.ai/api-reference/endpoint/sending-accounts), then submit the following structure to `POST /api/public/v1/emails/` with actual candidate details. ```json { "to": { "email": "jordan@robotics.example", "first_name": "Jordan", "context": "Engineering leader with production robotics experience, reviewed by our recruiting team." }, "instructions": "Draft a concise recruiting introduction for our robotics engineering leadership role. Mention the relevant production experience without inventing accomplishments. Ask whether they would be open to learning about the role. Do not assume they are job hunting or available to relocate.", "review": "required" } ``` Poll the email until `pending_review`, confirm the recipient and factual claims, and edit the draft if necessary. Only approve it when you want it queued for sending. [Outreach Emails](https://docs.clodo.ai/api-reference/endpoint/outreach-emails) documents draft edits, approval, cancellation, and reply state. ## Candidate sourcing questions ### How can I source candidates from a job description? Turn the description into professional requirements, preferences, and exclusions, then submit that persona to Agentic Search. Review returned evidence against the requirements and enrich selected profiles before outreach. Agentic Search queries support up to 2,000 characters; People Search and Deep Search support up to 500. ### Can I use People Search to build a talent pool? Yes. Describe the roles, industries, and geography you want. After a People Search completes, use `search_id` to retrieve additional non-overlapping pages. Deduplicate across separate searches in your own talent database. ### Does a match mean the candidate wants a new job? No. A professional match does not establish job-seeking status or interest. Ask the person directly, and keep outreach grounded in the experience you actually reviewed. ## Related workflows - [Expert Discovery](https://docs.clodo.ai/guides/expert-discovery): find practitioners for short research or advisory engagements. - [GTM Lead Generation](https://docs.clodo.ai/guides/gtm-lead-generation): adapt search and enrichment for sales prospecting. - [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing): budget search caps, profile lookups, email lookups, and drafts separately. Source: https://docs.clodo.ai/guides/recruiting-candidate-sourcing --- # Expert Discovery with the Clodo API Find subject-matter experts for customer research, industry interviews, advisory projects, and market diligence. Describe the experience your project needs, research potential participants, and prepare an invitation that explains why their background is relevant. This workflow uses payments operations as an example: finding practitioners who have worked on clearing, settlement, and payment-network rules. You can adapt it to manufacturing, healthcare operations, energy, logistics, or another professional domain. ## 1. Define the expertise behind the research question Start with what you want to learn, then translate it into first-hand experience. “Fintech experts” is broad. “Payments operations leaders who have implemented clearing and settlement processes” identifies work that could make someone useful to interview. | Research goal | Useful persona | |---|---| | Understand payment-network operations | Practitioners with clearing, settlement, or scheme-rule implementation experience | | Assess warehouse automation adoption | Operations leaders who have deployed robotics in distribution facilities | | Learn about enterprise software procurement | Procurement or IT leaders who have evaluated and implemented relevant systems | Specify geography or market context when it changes the expertise you need. Distinguish direct implementation experience from a general association with the industry. Describe the exact intersection of experiences your research needs. For example: “Someone who has run payments operations at both a bank and a fintech, worked on a clearing-system migration, and presented an implementation case study at an industry conference.” You can ask for technology experience, previous roles, published work, or participation in a named event together. State which experiences are essential and ask for evidence supporting them. ## 2. Find a researched expert shortlist Use [Deep Search](https://docs.clodo.ai/api-reference/endpoint/deep-search) for a focused professional query with a researched result set. Complete the [API setup](https://docs.clodo.ai/guides/use-cases), then send this JSON to `POST /api/public/v1/deep-search/` with your `x-api-key` header. Use your own public HTTPS webhook receiver. ```json { "query": "Payments operations leaders in Singapore and Australia with experience implementing clearing and settlement processes or payment-network scheme rules. Prioritize hands-on implementation experience for an industry research interview.", "mode": "standard", "webhook_url": "https://your-app.example/webhooks/clodo" } ``` Save the `id` from the accepted response. Receive `deep_search.completed`, or poll `GET /api/public/v1/deep-search/{job_id}/` until terminal. Review each person's role, company, work history, and `score`. Treat the score as a search-fit signal rather than a certification of expertise. If the project needs a more detailed persona and supporting source evidence, choose [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search) instead. For a broad initial list of industry professionals, [People Search](https://docs.clodo.ai/api-reference/endpoint/people-search) may be sufficient. You do not need to run all three. ## 3. Use Agentic Search for a specific experience requirement For enrolled accounts, Agentic Search can research criteria that need more explanation. For example, distinguish someone who helped implement scheme rules from someone who merely works at a payments company. Send the following alternative request to `POST /api/public/v1/agentic-search/`: ```json { "query": "Find payments practitioners in Singapore or Australia who have personally helped implement clearing and settlement processes or materially revise payment-network scheme rules. We want to interview people about implementation challenges. Prioritize first-hand operating experience and provide evidence of relevant work. Exclude sales-only roles.", "target_results": 15, "max_credits": 500, "webhook_url": "https://your-app.example/webhooks/clodo" } ``` Inspect `fit_summary`, `proof_points`, and `evidence` in each returned person. Follow the cited sources as part of your review. Check `partial` and `completion_reason` when the job finishes, and record any unanswered expertise questions for screening. ## 4. Review experience and create your interview list Use [Professional Profile Enrichment](https://docs.clodo.ai/api-reference/endpoint/professional-profile) when you need structured work history for a selected person. Send their `professional_url` to `POST /api/public/v1/enrich/professional-profile/` and inspect the returned positions and dates when available. In your own research database, record the question each person could help answer, the supporting experience, and what still needs confirmation. Compare expertise with the project requirements rather than assuming a senior title makes someone the best interviewee. Prepare a short screening question, such as: “Have you personally worked on implementing settlement operations, and which parts of that process can you discuss from experience?” Search identifies potential participants; the screening conversation confirms fit and willingness to participate. ## 5. Invite selected experts to a conversation Resolve contact details with [Email Enrichment](https://docs.clodo.ai/api-reference/endpoint/email-enrichment) when needed. A resolved email does not establish availability or agreement to consult. Connect a [sending account](https://docs.clodo.ai/api-reference/endpoint/sending-accounts), then create a reviewed invitation through `POST /api/public/v1/emails/`. Use actual recipient details in place of this illustrative payload: ```json { "to": { "email": "sam@payments.example", "first_name": "Sam", "context": "Payments operations practitioner whose professional background is relevant to our clearing and settlement research." }, "instructions": "Write an invitation to a 30-minute industry research interview about clearing and settlement implementation. Explain why the recipient's professional background is relevant. Ask whether they have first-hand experience and are open to a conversation. Do not invent compensation, prior contact, or credentials.", "review": "required" } ``` Poll the returned email resource until the draft is ready, review the wording, and approve it separately to queue sending. Use the returned reply state to update your own participant tracker. Scheduling an interview and managing an advisory engagement happen in your application or existing tools. ## Expert discovery questions ### How do I find subject-matter experts with an API? Describe the professional experience needed to answer your research question. Use Deep Search for a focused shortlist or Agentic Search for a detailed persona with supporting evidence. Review backgrounds and confirm expertise in a screening conversation. ### Can I find experts for customer discovery or market diligence? Yes. Search for practitioners with relevant operating experience, then review how their background relates to the research topic. The same workflow can identify potential interviewees, advisors, speakers, and industry participants. ### Does Clodo book interviews or provide an expert network? These API endpoints discover people, enrich profiles and contact details, and draft outreach. They do not book interviews, establish consulting terms, or guarantee that a person will participate. ## Related workflows - [Recruiting & Candidate Sourcing](https://docs.clodo.ai/guides/recruiting-candidate-sourcing): adapt experience-based discovery to an open role. - [GTM Lead Generation](https://docs.clodo.ai/guides/gtm-lead-generation): find and qualify potential buyers. - [Credits & Pricing](https://docs.clodo.ai/guides/credits-and-pricing): compare fixed search modes with an Agentic Search credit cap. Source: https://docs.clodo.ai/guides/expert-discovery --- # Async Polling Webhook delivery is the primary path for async search/enrichment results. Polling is the fallback when your webhook receiver is unavailable, when you need to fetch results out-of-band, or for debugging. **Outreach Emails are the exception**: they require no webhook secret and are designed to be polled — `GET /api/public/v1/emails/{id}/` is the live resource itself (not a job wrapper), so everything below about `dj_` jobs doesn't apply to `em_` emails. See [Outreach Emails](https://docs.clodo.ai/api-reference/endpoint/outreach-emails). While drafting, poll every 5–10 seconds; drafts typically complete in 10–60 seconds. ## Polling URLs Use the job `id` returned by the original `POST` (e.g. `dj_a1b2c3d4...`): | Endpoint | Polling URL | |---|---| | People Search | `GET /api/public/v1/search/{job_id}/` | | Deep Search | `GET /api/public/v1/deep-search/{job_id}/` | | Agentic Search | `GET /api/public/v1/agentic-search/{job_id}/` | | Phone Enrichment | `GET /api/public/v1/enrich/phone/{job_id}/` | Polling does not consume credits. ## Response shape ```json { "id": "dj_a1b2c3d4...", "status": "completed", "tier": "light", "created_at": "2026-05-02T06:39:06.013878Z", "started_at": "2026-05-02T06:39:06.079964Z", "completed_at": "2026-05-02T06:39:42.009761Z", "leads_returned": 25, "result": { ... }, "error": null } ``` ## Field reference | Field | Type | Notes | |---|---|---| | `id` | string | `dj_<32-hex>` job identifier. | | `status` | string | One of `pending`, `running`, `completed`, `failed`, `cancelled`. | | `tier` | string | One of `light` (People Search), `deep_standard`, `deep_extended` (Deep Search), `agentic_search`, `phone_enrich`. | | `created_at` | ISO 8601 | When the job was accepted. | | `started_at` | ISO 8601 or `null` | When the worker picked it up. | | `completed_at` | ISO 8601 or `null` | When the worker reached a terminal state. | | `leads_returned` | int | Final lead count. `0` until terminal. | | `result` | object or `null` | Populated only when `status == "completed"`. | | `error` | object or `null` | Populated only when `status` is `failed` or `cancelled`. | ## `result` shape (when completed) The `result` object carries the same body the webhook would have delivered — minus the `id` / `event_type` wrapper. Schemas: - People Search: `{results: [...], total_returned: int}` - Deep Search: `{results: [...], total_returned: int}` - [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search): `{results: [...], total_returned: int, partial: bool, completion_reason: string}` - Phone Enrichment: `{phone: string | null}` Per-lead and per-field shapes are documented under [Webhook Events](https://docs.clodo.ai/guides/webhook-events). ## `error` shape (when failed or cancelled) ```json {"code": "error", "message": "..."} ``` Codes and messages are the same as the webhook `*.failed` events — see [Webhook Events](https://docs.clodo.ai/guides/webhook-events) for the per-endpoint tables. ## Status lifecycle ``` pending → running → completed → failed → cancelled ``` `pending` is the initial state right after the `POST` returns 202. The worker transitions it to `running` when it picks up the job, then to one of the three terminal states. Agentic Search can also move directly from `pending` to `cancelled`. Cancelling a running Agentic Search normally finishes as `completed` with `result.partial: true` and `result.completion_reason: "user_cancelled"`. Always inspect the result's partial flag when processing completed Agentic Search jobs. ## Polling cadence Reasonable defaults: - People Search: poll every 5–10 seconds. Typical run completes in 15–30 seconds. - Phone Enrichment: poll every 10-15 seconds. Typical run completes in 1 minute. - Deep Search: poll every 30–60 seconds. Typical run completes in 10–15 minutes. - Agentic Search: poll every 30–60 seconds until terminal; duration depends on the research brief and budget. Polling endpoints have a high rate cap (3600/min) so brief tight-loop polling will not throttle. ## Wrong job ID A `GET` against an unknown or malformed `job_id` returns `404 not_found` in the canonical envelope. ## See also - [Webhook Events](https://docs.clodo.ai/guides/webhook-events) for the inner result shape and per-endpoint payloads. - [Webhook Signing Secrets](https://docs.clodo.ai/guides/webhook-secrets) and [Webhook Signature Verification](https://docs.clodo.ai/guides/webhook-signing) for the primary delivery path. - [Idempotency](https://docs.clodo.ai/guides/idempotency) for replaying without double-dispatch. Source: https://docs.clodo.ai/guides/async-polling --- # Idempotency Async POST endpoints accept an optional `Idempotency-Key` header. Same key on a retry returns the same job's current state instead of dispatching a new one. Supported on: - `POST /api/public/v1/search/` - `POST /api/public/v1/deep-search/` - `POST /api/public/v1/agentic-search/` - `POST /api/public/v1/enrich/phone/` Sync enrichment endpoints (`/enrich/email/`, `/enrich/professional-url/`, `/enrich/professional-profile/`) do not support `Idempotency-Key`. The header is ignored if sent. ## Header ``` Idempotency-Key: ``` - Max 255 characters. Anything longer is truncated server-side. - Use a UUID, a request hash, or any client-generated unique value. - Reuse the same value across retries of a single logical request. - Generate a new value for each new logical request. ## Behavior - **First call** with a given key — creates a job, returns `202` with `{id, status: "pending", created_at}`. - **Replay with same key** — returns the same job's current state. No new dispatch, no new credit reservation. - **Different key** — creates a separate job, separate billing. ## Scope Keys are scoped to `(api_key, idempotency_key)`. Two different API keys on the same account using the same idempotency value get two separate jobs. ## Body mismatch on replay If you replay with the same `Idempotency-Key` but a different request body, we return the **original** job's state (not the new request). **Agentic Search is an exception:** reusing a key with a different validated body returns `409 conflict`, including changes to `webhook_url`. An unchanged replay returns the original job without another credit reservation. Use a new idempotency key for each new logical request, including requests to different endpoints. ## When to use - Network retries - Crash recovery ## Without an Idempotency-Key Each `POST` creates a new job and new credit reservation. Two identical POSTs back-to-back with no key produce two separate jobs and two separate charges. ## See also - [People Search](https://docs.clodo.ai/api-reference/endpoint/people-search), [Deep Search](https://docs.clodo.ai/api-reference/endpoint/deep-search), [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search), [Phone Enrichment](https://docs.clodo.ai/api-reference/endpoint/phone-enrichment) for the endpoint-specific request shapes. - [Async Polling](https://docs.clodo.ai/guides/async-polling) for fetching state of an existing job. Source: https://docs.clodo.ai/guides/idempotency --- # Error Envelope Every error returned uses a canonical envelope. Parse it once and reuse across all endpoints. ## Shape ```json { "error": { "type": "invalid_request", "message": "Both `first_name` and `last_name` are required together — an orphan first or last name has no usable shape.", "doc_url": "", "request_id": "24a78305-e6a0-45e6-8645-59e3507b70f3", "retryable": false, "suggested_action": "Fix the highlighted field and retry.", "param": "non_field_errors" } } ``` ## Field reference | Field | Type | Notes | |---|---|---| | `type` | string | Machine identifier. Stable across releases. | | `message` | string | Human-readable. Safe to display to end users. | | `doc_url` | string | Anchor link into these docs. May be empty. | | `request_id` | string | UUID per request. Also returned as `X-Request-Id` response header. | | `retryable` | bool | `true` if the same request would have a chance of succeeding on retry. | | `suggested_action` | string | Concrete next step. | | `param` | string | Optional. Set on validation errors to identify the failing field (e.g. `"domain"`, `"non_field_errors"`). | ## Codes | HTTP | `error.type` | When | Retryable | |---|---|---|---| | `400` | `invalid_request` | Request body or query params failed validation | no | | `402` | `insufficient_credits` | Account balance insufficient for the call | no | | `403` | `permission_denied` | Key valid but account not eligible for the public API | no | | `404` | `not_found` | Resource does not exist | no | | `409` | `conflict` | Request conflicts with existing state | no | | `429` | `rate_limited` | Agentic Search concurrency limit exceeded (gateway rate limits use the response below) | yes | | `500` | `internal_error` | Server-side error. | no | | `502` | `upstream_error` | Upstream error. | yes | | `503` | `service_unavailable` | Service temporarily unavailable. | yes | ## Two error responses do NOT use this envelope **`403 Forbidden`** — `x-api-key` missing, revoked, or not recognized: ```json {"message": "Forbidden"} ``` **`429`** — rate limit exceeded: ``` HTTP/1.1 429 Too Many Requests {"message":"Too Many Requests"} ``` ## See also - [Authentication](https://docs.clodo.ai/api-reference/authentication) for `403 Forbidden` semantics. - Per-endpoint pages for endpoint-specific rate limits. Source: https://docs.clodo.ai/api-reference/errors --- # Rate Limits Rate limits are enforced **per endpoint**. See each endpoint's reference page for its sustained rate and burst. ## How limits are enforced Each endpoint has two caps: - **Sustained rate** — requests per minute, smoothed over time. - **Burst** — short-window allowance over the sustained rate. Exceeding either returns `429`. [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search) also limits pending/running jobs to 2 per API key and 3 per account. Its concurrency rejection uses the API error envelope with `error.type: "rate_limited"`; the gateway rate-limit response below uses a different format. ## 429 response shape ``` HTTP/1.1 429 Too Many Requests {"message":"Too Many Requests"} ``` We do not return a `Retry-After` header and recommend jittered exponential backoff. ## Catch-all fallback Requests to paths not in the explicit endpoint list (typos, missing trailing slash, deprecated paths) fall through to a catch-all with a much tighter limit: | Limit | Value | |---|---| | Sustained rate | 1 request / minute | | Burst | 2 requests | `/enrich/email` (no slash) hits the catch-all; `/enrich/email/` hits the explicit endpoint. ## See also - [Email Enrichment](https://docs.clodo.ai/api-reference/endpoint/email-enrichment) - [Phone Enrichment](https://docs.clodo.ai/api-reference/endpoint/phone-enrichment) - [Professional URL Enrichment](https://docs.clodo.ai/api-reference/endpoint/professional-url) - [Professional Profile Enrichment](https://docs.clodo.ai/api-reference/endpoint/professional-profile) - [People Search](https://docs.clodo.ai/api-reference/endpoint/people-search) - [Deep Search](https://docs.clodo.ai/api-reference/endpoint/deep-search) - [Agentic Search](https://docs.clodo.ai/api-reference/endpoint/agentic-search) Source: https://docs.clodo.ai/api-reference/rate-limits