Errors
The error format, every error code the Knock API returns, and which errors are safe to retry.
The Knock API uses standard HTTP status codes. 2xx means success, 4xx means something about the request needs to change, and 5xx means something went wrong on Knock's side.
Error format
Errors from the API have a JSON body with three fields:
{
"errors": ["Enrichment request not found"],
"code": "external-api.enrichment-request.not-found",
"metadata": {
"vendorId": "3f6a2b1c-8d4e-4f5a-9b6c-7d8e9f0a1b2c",
"enrichRequestId": "7d0f4f1e-2c3b-4a8e-9a51-0e6f3b8c2d14"
}
}| Field | Type | Description |
|---|---|---|
errors | string[] | Human-readable messages. Validation errors list one message per failed check. Don't parse them: the wording can change. |
code | string | A stable, machine-readable error code. Branch on this. |
metadata | object | Extra context about the error. Often empty; for a 429 it holds limit and retryAfterSeconds. |
A few responses are produced before a request reaches the Knock API itself and use a simpler body without a code:
{
"statusCode": 413,
"message": "request entity too large"
}This happens for an oversized body (413), malformed JSON (400), the per-IP rate limit (429), an unknown path (404) and unexpected server errors (500). Handle errors by HTTP status first, then by code when it's present.
Error codes
| Status | code | Message | Cause | Retry? |
|---|---|---|---|---|
400 | validation | One per problem, for example people.0.email: email must be an email | The body, path or query parameter is invalid. Nothing was submitted. | No. Fix the request. |
400 | – | – | The body isn't valid JSON. | No. Fix the request. |
400 | external-api.enrichment-request.rejected | The enrichment request was rejected | Knock couldn't accept the enrichment request. | No. Check the input; contact Knock if it persists. |
401 | authentication.invalid | Invalid authentication token | The x-api-key header is missing, or the key is unknown, revoked or expired. Also returned when Knock can't verify a key for a moment. | A few times with backoff, then stop and check the key. |
401 | authentication.forbidden | You do not have access to this resource | The request comes from a country Knock doesn't serve. | No. |
403 | permissions.denied | You do not have permission to perform this action | The key's role can't use this endpoint. | No. Use a key with the right role. |
403 | ip-restriction.denied | Your IP address is not allowed to access this resource | Your account only allows specific IP addresses, and this request came from another one. | No. Call from an allowed IP. |
404 | external-api.enrichment-request.not-found | Enrichment request not found | No enrichment request with this ID exists for your account. | No. |
404 | external-api.chat.not-found | Chat not found | Your account has no chat with this contactId, or the dashboard hides it (for example, spam). | No. |
404 | – | – | The path or method doesn't exist. Check the URL. | No. |
413 | – | – | The body is over 100 KB. | No. Split the batch. |
429 | external-api.rate-limit.exceeded | Rate limit exceeded | Your account sent more requests than its rate limit allows. | Yes, after Retry-After seconds. |
429 | – | – | Too many requests from one IP address. | Yes, after Retry-After seconds. |
500 | external-api.enrichment-request.not-stored | The enrichment request could not be stored | Enrichment started, but the request couldn't be saved, so you didn't get an enrichRequestId. | No. Don't resubmit automatically: the items may already be enriched and charged. Contact Knock with the X-Request-Id header. |
500 | – | – | An unexpected error. | Yes, with backoff. For a POST, see below. |
502 | external-api.enrichment.unavailable | Enrichment is temporarily unavailable, retry later | On submit: enrichment is briefly unavailable and no request was created. On read: results can't be loaded right now. | Yes, with backoff. |
502 | external-api.history.unavailable | History is temporarily unavailable, retry later | Knock can't read your chats or meetings right now. | Yes, with backoff. |
Other 5xx responses, such as 503 or 504, are temporary too: retry with backoff.
Validation errors
A 400 validation response lists every problem it found, each prefixed with the path of the field:
{
"errors": [
"people.0.email: email must be an email",
"people.2.firstName: firstName must be shorter than or equal to 320 characters"
],
"code": "validation",
"metadata": {}
}Common messages:
| Message | Fix |
|---|---|
people: people must contain at least 1 elements | Send at least one person. |
people: people must contain no more than 100 elements | Split the batch into requests of 100 or fewer. |
people.N.email: email must be an email | Fix the email at index N. Emails longer than 254 characters are rejected with this message too. |
companies.N.domain: domain should not be empty | Send a non-empty domain at index N. |
enrichRequestId: enrichRequestId must be a UUID | Pass the enrichRequestId exactly as the submit call returned it. |
contactId: contactId must be a UUID | Pass a contactId from GET /v1/chats. |
limit: limit must not be greater than 100 | Use a limit from 1 to 100. |
One invalid item rejects the whole request, and nothing is submitted or charged. Send the body as JSON with Content-Type: application/json; a body Knock can't read as JSON also gets 400, with the plain {statusCode, message, error} body and no code.
Retrying safely
| Situation | Safe to retry? |
|---|---|
429 (either kind) | Yes. Nothing was processed. Wait for Retry-After, see Rate limits. |
502 on a POST | Yes. No request was stored. |
5xx on a GET | Yes. Reading never changes anything. |
401 authentication.invalid from a key that worked moments ago | Yes, a few times with exponential backoff. Knock fails closed, so a brief verification problem looks like an invalid key. |
500 external-api.enrichment-request.not-stored | No. The items may already be enriched and charged. Contact Knock with the X-Request-Id header. |
500 (no code) or a timeout on a POST | The request may or may not have been created. Resubmitting is safe: Knock resolves the same people and companies, joins work in progress, and doesn't add duplicate contacts. You'll get a new enrichRequestId. |
400, 403, 404, 413 | No. Change the request first. |
Use exponential backoff with jitter for retries, for example 1, 2, 4, 8 and 16 seconds plus a random fraction, and give up after about five attempts. Rate limits has a ready-made retry helper.
Request IDs
Almost every response carries an X-Request-Id header with a unique ID for that request (not 413 or malformed-JSON 400 responses, which are rejected before the request is logged). Log it with your errors and include it when you contact Knock about a specific call: it lets the team find that request quickly.
HTTP/1.1 502 Bad Gateway
Content-Type: application/json; charset=utf-8
X-Request-Id: 0b4c7f9e-1d2a-4e3b-8c5d-6f7a8b9c0d1e