Routing API
Route a lead with a routing rule you set up in the Knock dashboard, and get the segment, owner and enrichment back in one call.
The Routing API runs one of your Knock routing rules for a single lead, from your own systems. You send an email and the ID of a routing rule, and Knock enriches the lead, walks the rule's segments and answers right away with the outcome: which segment matched, which team member gets the lead, who owns the account in your CRM, and the person and company Knock found.
You build the routing logic once, in the dashboard, with the same segments, filters and fallback you use for your forms. The API is how any other entry point (a custom form, a product sign-up, a chatbot, a marketing automation step) gets the same answer.
Endpoint
| Method | Path | What it does |
|---|---|---|
POST | /v1/route | Route one lead with one routing rule. Returns 200 with the decision. |
The call needs a key with the Enrichment API role (Admin keys work too). See Authentication.
Set up a routing rule
Create the rule in the dashboard
In the Knock dashboard, open Workflow Routing and select the Custom tab. Add a rule with +, then build it:
- If a CRM owner exists, assign them: optional. When the lead already has an owner in your CRM, they get the lead and no segment is checked.
- Priority segments: checked top to bottom, and the first segment whose filters match wins. Each segment either qualifies the lead and assigns it to its reps in rotation, or disqualifies it.
- Fallback routing: what happens when no segment matches, Qualify or Disqualify.
Copy the routing rule ID
Open the rule's ⋯ menu, next to its name, and choose Copy Routing ID. You can also use the copy icon at the top right of the rule. This ID is the routingId you send to the API.
Editing the rule later doesn't change its ID, so your integration picks up every change you make in the dashboard. If you delete the rule, the API answers 404 for its ID.
Test it
Call the API with "test": true to see the decision without assigning the lead. See Test without side effects.
Route a lead
POST https://app.knock-ai.com/v1/routeProp
Type
curl -X POST https://app.knock-ai.com/v1/route \
-H "x-api-key: $KNOCK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"routingId": "8b1f0c2e-4d6a-4c3e-9f1b-2a7d5e8c0f13",
"email": "jane@acme.com"
}'Response
{
"decisionId": "c0a8e2b4-7f3d-4a1e-9b6c-5d2f8e1a7c90",
"test": false,
"outcome": "matched",
"segment": { "id": "1f2e3d4c-5b6a-4978-8a6b-5c4d3e2f1a0b", "name": "Enterprise NA" },
"assignee": { "memberId": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "email": "dana@yourcompany.com", "name": "Dana Rep" },
"accountOwner": null,
"enrichment": {
"status": "completed",
"person": {
"firstName": "Jane",
"lastName": "Doe",
"title": "VP Sales",
"seniority": "VP",
"profession": "Sales",
"linkedinUrl": "https://www.linkedin.com/in/janedoe",
"location": "Austin, Texas, United States",
"photoUrl": null,
"currentPositionStartDate": "2024-03",
"company": { "name": "Acme", "domain": "acme.com", "size": "1001-5000", "industry": "COMPUTER_SOFTWARE", "…": "…" }
}
},
"company": { "name": "Acme", "domain": "acme.com", "size": "1001-5000", "industry": "COMPUTER_SOFTWARE", "…": "…" }
}| Field | Type | Description |
|---|---|---|
decisionId | string (UUID) | Unique ID of this decision. Include it when you contact Knock about a call. |
test | boolean | true when this was a test call: nothing was assigned or written. |
outcome | string | What the rule decided: matched, fallback, disqualified or crm_owner. See Outcomes. |
segment | object or null | The segment that matched: id and name. null when no segment matched (fallback, crm_owner, or a fallback that disqualifies). name is null if the segment has no name yet. |
assignee | object or null | The team member the rule routed the lead to: memberId, email and name. null when the lead is disqualified or the segment has no reps. |
accountOwner | object or null | The owner of the lead's account in your CRM (the Salesforce account owner or the HubSpot company owner), with the same fields as assignee. It's a CRM fact, independent of the rule. null when there's no connected CRM, no owner, or the owner isn't a member of your Knock team. |
enrichment.status | string | completed, pending, not_found or failed, with the same meanings as enrichment item statuses. pending means enrichment wasn't finished when Knock answered. |
enrichment.person | object or null | The person result, with their company. null unless status is completed. |
company | object or null | The lead's company: enrichment.person.company when it's set, otherwise the company of the email's domain, billed like an Enrichment API company result. null for a personal email address (such as Gmail) without a matched person, or when Knock doesn't know the company. |
Every key is always present, including the null ones.
Outcomes
outcome | What happened | segment | assignee |
|---|---|---|---|
crm_owner | The rule's If a CRM owner exists, assign them step found the lead's owner in your CRM. No segment was checked. | null | The CRM owner |
matched | A priority segment matched and qualifies the lead. | The segment | The next rep in the segment's rotation, or null if it has none |
fallback | No segment matched, and the fallback qualifies the lead. | null | The next rep of the fallback, or null |
disqualified | A disqualifying segment matched, or no segment matched and the fallback disqualifies. | The segment, or null for the fallback | null |
A segment filter on data Knock doesn't have for the lead (for example, company size before the company is found) doesn't match, so the walk moves on to the next segment.
Timing
Knock answers synchronously, usually within a few seconds. It waits up to about 6 seconds for the lead's enrichment, so that segments filtering on title, seniority or company data can match. When enrichment isn't finished in time, Knock routes on what it has and enrichment.status is pending. When the person was already looked up without a match, Knock doesn't wait.
Set your HTTP client timeout to at least 30 seconds. Call the API from your backend after the lead submits, not in the critical path of rendering a page.
Test without side effects
Send "test": true to run the same rule and get the same answer without acting on it:
- the lead isn't assigned, and the segment's rotation doesn't move;
- the routing result isn't saved in Knock;
- nothing is written to your CRM.
The lead is still added to your Knock contacts and enrichment still uses credits, just like the Enrichment API. Use real work emails you control, or your own, to test each segment of the rule.
{
"routingId": "8b1f0c2e-4d6a-4c3e-9f1b-2a7d5e8c0f13",
"email": "jane@acme.com",
"test": true
}What a live call does
Without test, Knock acts on the decision:
- Assigns the lead. The routing result, with its assignee, is saved on the contact in Knock under the source Routing API, and the segment's rotation moves to its next rep.
- Writes to your CRM when
syncToCrmistrue(the default): Knock creates or updates the lead in your connected CRM and sets its owner, following your CRM settings in Knock. - Adds the lead to your contacts and uses enrichment credits.
The Routing API never contacts the lead: Knock sends no LinkedIn or email outreach for API decisions. Your own system decides what happens next with the response.
Every live call is a new decision, and there is no idempotency key: calling twice for the same lead assigns it twice and moves the rotation twice. Call the API once per lead event, such as one form submission.
CRM sync
With "syncToCrm": false, this call doesn't write to your CRM. Use it when your own system, or another tool, owns the CRM write: read assignee and segment from the response and write them yourself. The routing result is still saved in Knock.
Errors
| Status | code | When | Retry? |
|---|---|---|---|
400 | validation | The body is invalid, for example routingId: routingId must be a UUID or email: email must be an email. | No. Fix the request. |
403 | permissions.denied | The key's role doesn't include the Routing API. | No. Use a key with the Enrichment API role. |
404 | external-api.routing.not-found | routingId isn't one of your routing rules, or the rule was deleted. | No. Copy the ID again from the dashboard. |
429 | external-api.rate-limit.exceeded | Your account sent more requests than its rate limit allows. | Yes, after Retry-After seconds. |
502 | external-api.routing.unavailable | Knock couldn't complete the decision. | Yes, once, after a short wait. The lead may already have been routed, so a retry can route it again. |
See Errors for the error format and the codes every endpoint shares.
Use cases
| Use case | Call it when | Use the response to |
|---|---|---|
| Custom or headless forms | Your backend receives a form submission. | Show the assigned rep's calendar to qualified leads, and a self-serve page to the rest. |
| Product sign-ups | A user signs up in your product. | Alert the assignee about enterprise sign-ups; flag sign-ups whose account already has an accountOwner. |
| Chatbots and AI agents | Your bot or agent has the visitor's email. | Decide whether to hand off, and to whom. |
| Marketing automation | A workflow step in your marketing automation or CRM runs. | Branch the workflow on outcome and segment, with syncToCrm: false if the workflow writes to the CRM. |
| Events and webinars | You import attendees after an event. | Route each attendee to the right owner for same-day follow-up. |
| Outbound checks | Before you add a prospect to a sequence. | Skip prospects whose account already has an accountOwner, or notify that owner instead. |