[{"data":1,"prerenderedAt":79},["ShallowReactive",2],{"docs:rendered-article:\u002Fdocs\u002Fpublic\u002Fguides\u002Fwebhooks":3},{"path":4,"slug":5,"title":8,"description":9,"category":6,"categoryLabel":10,"order":11,"status":12,"family":13,"subfamily":13,"content":14,"html":15,"examplesHtml":16,"excerpt":17,"readTimeMinutes":18,"headings":19,"examplesHeadings":70,"lastUpdated":13,"endpointId":13,"requiredFlag":13,"relatedArticlePaths":71,"badge":76},"\u002Fdocs\u002Fguides\u002Fwebhooks",[6,7],"guides","webhooks","Webhooks","Push delivery for async enrichment results. Use when you already run a public HTTPS receiver and prefer a push model.","Guides",31,"updated",null,"\n> [!TIP]\n> **Most teams ship faster with [Polling](\u002Fdocs\u002Fguides\u002Fpolling).**\n>\n> No public endpoint required, free to call, around 5 minutes to first result. The webhook setup below is documented for teams that already run a public HTTPS receiver and need real-time push delivery.\n>\n> - No public HTTPS endpoint required\n> - Free: zero credits per poll\n> - Off the clock: doesn't count toward your per-minute rate limit\n>\n> → **[Start with Polling](\u002Fdocs\u002Fguides\u002Fpolling)**\n\n## Introduction\n\nWebhooks deliver async enrichment results to a public HTTPS URL you operate. Use them when you already operate a public HTTPS receiver and prefer a push model.\n\nFor most use cases, polling is faster to integrate and equally cost-effective. Same endpoints, same data, same enrichment cost. See the [Polling guide](\u002Fdocs\u002Fguides\u002Fpolling) to compare both side by side.\n\n## How it works\n\nAsync endpoints work in the background so your application doesn't have to wait. The API accepts your request immediately, processes it, and delivers the result to your server as soon as it's ready.\n\nThis is what happens step by step:\n\n1. **You send a request.** Call any async endpoint (e.g. `POST \u002Fv2\u002Ffetch\u002Fpersons\u002Flive`).\n2. **The API confirms instantly.** You get back a `webhookId` that uniquely identifies this request. Use it to match incoming callbacks back to your original request.\n3. **Processing happens in the background.** The API fetches and prepares the data for you.\n4. **The result is delivered to you.** As soon as the data is ready, the API sends a POST request to your webhook URL with the full JSON payload.\n\n> [!IMPORTANT]\n> **Async by design, no synchronous SLA.** We aim to POST each result in under 15 seconds, but processing can stretch to tens of minutes depending on conditions (source availability, queue load, retries). Live endpoints are built to update databases and feed async pipelines, not to back a synchronous, real-time request a user is waiting on.\n\n## Async endpoints\n\nAll of these endpoints deliver results via webhook:\n\n|   | Endpoint |\n| - | -------- |\n| **Profiles** | |\n| Person Profile Live | [`POST \u002Fv2\u002Ffetch\u002Fpersons\u002Flive`](\u002Fdocs\u002Fendpoints\u002Flive-profile) |\n| Company Profile Live | [`POST \u002Fv2\u002Ffetch\u002Fcompanies\u002Flive`](\u002Fdocs\u002Fendpoints\u002Flive-company) |\n\n## 1. Configure your webhook URL\n\n### Workspace default (recommended)\n\nSet a single HTTPS URL at workspace level so all async results are delivered automatically. No need to include `webhookUrl` in every request.\n\n1. Go to **[Settings > Webhooks](\u002Fsettings\u002Fwebhooks)**\n2. Enter your HTTPS endpoint URL\n3. Click **Save**\n4. Click **Test** to send a test payload and verify connectivity\n\n> [!TIP]\n> The workspace default URL enables full delivery tracking in the **[Webhook Events](\u002Flogs\u002Fevents)** dashboard. Note that delivery failures are not refunded: the data was fetched, so the credit is kept and the result stays retrievable via [polling](\u002Fdocs\u002Fguides\u002Fpolling).\n\n### Per-request webhook URL override (optional)\n\nInclude a `webhookUrl` field in your request body to override the delivery target for a single request. This is the fastest way to test locally with a tunnel like ngrok:\n\n::: code-group [Local tunnel]\n```bash [Bash]\nngrok http 3000\n```\n:::\n\nThen use the forwarding URL in your request:\n\n::: code-group [Request body]\n```json [JSON]\n{\n  \"url\": \"https:\u002F\u002Fsocial.com\u002Fin\u002Fjanedoe\",\n  \"webhookUrl\": \"https:\u002F\u002Fa1b2c3d4.ngrok-free.app\u002Fwebhooks\u002Freversecontact\"\n}\n```\n:::\n\nIf no webhook URL is available (neither in the request body nor in workspace settings), the API returns `422` immediately:\n\n::: code-group [422 WEBHOOK_URL_REQUIRED]\n```json [JSON]\n{\n  \"success\": false,\n  \"data\": null,\n  \"error\": {\n    \"code\": \"WEBHOOK_URL_REQUIRED\",\n    \"message\": \"A webhook URL is required to receive results. Either provide a \\\"webhookUrl\\\" in the request body, or configure a default webhook URL in your workspace settings.\"\n  }\n}\n```\n:::\n\nNo credits are consumed. Configure a default URL in **[Settings > Webhooks](\u002Fsettings\u002Fwebhooks)** or include `webhookUrl` in your request body.\n\n## 2. Set up a receiver\n\n> [!TIP]\n> Don't already operate a public HTTPS receiver? Skip this entirely with [Polling](\u002Fdocs\u002Fguides\u002Fpolling). You'll trigger jobs the same way and pull results on demand, no server required.\n\nA webhook receiver is a standard POST endpoint that accepts JSON and returns `200`. That's it.\n\n::: code-group\n```javascript [Node.js \u002F Express]\nconst express = require(\"express\");\nconst app = express();\n\napp.use(express.json());\n\napp.post(\"\u002Fwebhooks\u002Freversecontact\", (req, res) => {\n  const event = req.body;\n  console.log(\"Webhook received:\", event.webhookId);\n\n  if (event.data) {\n    \u002F\u002F Process the result, e.g. from POST \u002Fv2\u002Ffetch\u002Fpersons\u002Flive\n    console.log(\"Profile:\", event.data.firstName, event.data.lastName);\n  }\n\n  \u002F\u002F Always respond with 200 to acknowledge receipt\n  res.status(200).json({ received: true });\n});\n\napp.listen(3000, () => console.log(\"Listening on port 3000\"));\n```\n```python [Python \u002F Flask]\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\n\n@app.route(\"\u002Fwebhooks\u002Freversecontact\", methods=[\"POST\"])\ndef webhook():\n    event = request.get_json()\n    print(\"Webhook received:\", event[\"webhookId\"])\n\n    if \"data\" in event:\n        # Process the result, e.g. from POST \u002Fv2\u002Ffetch\u002Fpersons\u002Flive\n        print(\"Profile:\", event[\"data\"][\"firstName\"], event[\"data\"][\"lastName\"])\n\n    # Always respond with 200 to acknowledge receipt\n    return jsonify({\"received\": True}), 200\n\nif __name__ == \"__main__\":\n    app.run(port=3000)\n```\n:::\n\n## Webhook payload\n\n### Error callback\n\nWhen something goes wrong, you receive a callback with an `errorCode` inside `data` describing the issue. The original input fields are echoed back alongside the error code:\n\n::: code-group [Error callback]\n```json [JSON]\n{\n  \"webhookId\": \"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d\",\n  \"errorCode\": \"...\"\n}\n```\n:::\n\n### Result callback\n\nWhen the job completes successfully, the callback carries the requested data in `data`:\n\n::: code-group [Result callback]\n```json [JSON]\n{\n  \"webhookId\": \"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d\",\n  \"data\": {\n    \"...\": \"endpoint-specific result payload\"\n  }\n}\n```\n:::\n\n> [!TIP]\n> Check for `errorCode` inside `data` to distinguish errors from successful results. If `data.errorCode` is present, the request failed. Otherwise treat it as the terminal success for that `webhookId`.\n\n### Enrichment error codes\n\nThese codes describe the enrichment outcome and arrive as `data.errorCode` in the callback:\n\n| Code | Description |\n| ---- | ----------- |\n| `invalid-request` | The request input is malformed or doesn't match the expected format |\n| `data-not-found` | No matching result was found |\n| `person-not-found` | The person profile doesn't exist |\n| `company-not-found` | The company profile doesn't exist |\n| `email-not-found` | No email could be resolved for this person |\n| `fetch-data-error` | The API failed while fetching data. Safe to retry |\n| `result-unavailable` | The enrichment completed but the result was lost on our side. Retry the job. If the request was charged, contact support to get the credit back. |\n\n### Delivery error codes\n\nThese codes mean the enrichment was fetched successfully but the result could not be pushed to your webhook URL. The job is reported as `succeeded`, the `result` is still available, and the failed push is named by a `deliveryStatus` field (set to one of the codes below) on the poll and `check_task` responses.\n\n| Code | Billed | Description |\n| ---- | ------ | ----------- |\n| `webhook-url-invalid` | Yes | Your webhook returned a `4xx` response |\n| `webhook-url-errored` | Yes | Your webhook returned a `5xx` or a network error |\n| `webhook-url-timeout` | Yes | Your webhook did not respond in time |\n| `webhook-url-unreachable` | Yes | The webhook URL could not be reached from production |\n\n> [!NOTE]\n> Delivery failures are billed, not refunded: the data was successfully fetched, so the credit is kept. The job stays `succeeded` and the `result` is populated, poll `GET \u002Fv2\u002Fwebhooks\u002F:webhookId` to retrieve it. The `deliveryStatus` field tells you the push failed (and why) without flipping the job to `errored`.\n\n## Security best practices\n\n- **Use HTTPS.** The API rejects plain HTTP URLs for webhook delivery.\n- **Validate the payload shape.** Every callback contains `webhookId` + `data`. Check for `errorCode` inside `data` to distinguish errors from results. Ignore anything that doesn't match.\n- **Verify the webhookId.** Store the `webhookId` from the initial response and check it against incoming callbacks. This is your correlation and validation key.\n- **Respond quickly.** Return a `200` status code promptly. If you need to do heavy processing, acknowledge receipt first and process asynchronously.\n\n## Dashboard monitoring\n\nThe **[Webhook Events](\u002Flogs\u002Fevents)** page in your dashboard gives you full visibility into every webhook delivery:\n\n- **Status distribution.** A summary showing succeeded, errored, and in-progress events at a glance.\n- **Event log.** Each row displays the timestamp, `webhookId`, status, credits consumed, credits refunded, and error code (if any).\n- **Filters.** Narrow down events by status, error code, or `webhookId` to quickly find specific deliveries.\n\n## Go to production\n\nBefore going live, make sure your webhook integration is solid:\n\n- **Deduplicate on webhookId.** In rare cases, a callback may be delivered more than once.\n- **Log the webhookId.** Include it in your application logs to trace requests end-to-end.\n- **Monitor your credits.** Credits are deducted when the request is accepted, before the result is delivered. Refundable errors are automatically refunded. See [Rate limits & credits](\u002Fdocs\u002Fguides\u002Frate-limits-credits) for details.\n\nFor the full hardening checklist, see [Production checklist](\u002Fdocs\u002Fguides\u002Fproduction-checklist).\n\n## Troubleshooting\n\n**Not receiving webhooks**\n- Ensure your endpoint is publicly accessible from the internet (not just `localhost`)\n- If you omitted `webhookUrl` from the request, verify that a workspace default URL is configured in **[Settings > Webhooks](\u002Fsettings\u002Fwebhooks)**\n- Verify it accepts `POST` requests with a `Content-Type: application\u002Fjson` body\n- Check your server\u002Ffirewall logs for incoming requests\n\n**Timeout errors**\n- Your webhook endpoint must respond within 10 seconds\n- If processing takes longer, return `200` immediately and handle the data asynchronously\n\n**Receiving an error or not-found callback instead of data**\n- This is expected. The API sends a callback with an `errorCode` in `data` when the result is not available (see [Error codes](#error-codes))\n- Verify the URL does not require authentication or return redirects\n\n**Duplicate deliveries**\n- Use the `webhookId` to deduplicate on your side\n\n**Transient errors**\n- `fetch-data-error` is usually transient and safe to retry after a short delay\n- `result-unavailable` means the enrichment completed but the result was lost on our side. Retry the job. If the request was charged, contact support to get the credit back\n","\u003Cdiv class=\"markdown-alert markdown-alert-tip\">\u003Cp class=\"markdown-alert-title\">\u003Csvg class=\"octicon octicon-light-bulb mr-2\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\">\u003Cpath d=\"M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z\">\u003C\u002Fpath>\u003C\u002Fsvg>Tip\u003C\u002Fp>\u003Cp>\u003Cstrong>Most teams ship faster with \u003Ca href=\"\u002Fdocs\u002Fguides\u002Fpolling\">Polling\u003C\u002Fa>.\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>No public endpoint required, free to call, around 5 minutes to first result. The webhook setup below is documented for teams that already run a public HTTPS receiver and need real-time push delivery.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>No public HTTPS endpoint required\u003C\u002Fli>\n\u003Cli>Free: zero credits per poll\u003C\u002Fli>\n\u003Cli>Off the clock: doesn’t count toward your per-minute rate limit\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>→ \u003Cstrong>\u003Ca href=\"\u002Fdocs\u002Fguides\u002Fpolling\">Start with Polling\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>\n\u003C\u002Fdiv>\n\u003Ch2 id=\"introduction\">Introduction\u003C\u002Fh2>\n\u003Cp>Webhooks deliver async enrichment results to a public HTTPS URL you operate. Use them when you already operate a public HTTPS receiver and prefer a push model.\u003C\u002Fp>\n\u003Cp>For most use cases, polling is faster to integrate and equally cost-effective. Same endpoints, same data, same enrichment cost. See the \u003Ca href=\"\u002Fdocs\u002Fguides\u002Fpolling\">Polling guide\u003C\u002Fa> to compare both side by side.\u003C\u002Fp>\n\u003Ch2 id=\"how-it-works\">How it works\u003C\u002Fh2>\n\u003Cp>Async endpoints work in the background so your application doesn’t have to wait. The API accepts your request immediately, processes it, and delivers the result to your server as soon as it’s ready.\u003C\u002Fp>\n\u003Cp>This is what happens step by step:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>You send a request.\u003C\u002Fstrong> Call any async endpoint (e.g. \u003Ccode>POST \u002Fv2\u002Ffetch\u002Fpersons\u002Flive\u003C\u002Fcode>).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>The API confirms instantly.\u003C\u002Fstrong> You get back a \u003Ccode>webhookId\u003C\u002Fcode> that uniquely identifies this request. Use it to match incoming callbacks back to your original request.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Processing happens in the background.\u003C\u002Fstrong> The API fetches and prepares the data for you.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>The result is delivered to you.\u003C\u002Fstrong> As soon as the data is ready, the API sends a POST request to your webhook URL with the full JSON payload.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cdiv class=\"markdown-alert markdown-alert-important\">\u003Cp class=\"markdown-alert-title\">\u003Csvg class=\"octicon octicon-report mr-2\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\">\u003Cpath d=\"M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\">\u003C\u002Fpath>\u003C\u002Fsvg>Important\u003C\u002Fp>\u003Cp>\u003Cstrong>Async by design, no synchronous SLA.\u003C\u002Fstrong> We aim to POST each result in under 15 seconds, but processing can stretch to tens of minutes depending on conditions (source availability, queue load, retries). Live endpoints are built to update databases and feed async pipelines, not to back a synchronous, real-time request a user is waiting on.\u003C\u002Fp>\n\u003C\u002Fdiv>\n\u003Ch2 id=\"async-endpoints\">Async endpoints\u003C\u002Fh2>\n\u003Cp>All of these endpoints deliver results via webhook:\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003C\u002Fth>\n\u003Cth>Endpoint\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Profiles\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Person Profile Live\u003C\u002Ftd>\n\u003Ctd>\u003Ca href=\"\u002Fdocs\u002Fendpoints\u002Flive-profile\">\u003Ccode>POST \u002Fv2\u002Ffetch\u002Fpersons\u002Flive\u003C\u002Fcode>\u003C\u002Fa>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Company Profile Live\u003C\u002Ftd>\n\u003Ctd>\u003Ca href=\"\u002Fdocs\u002Fendpoints\u002Flive-company\">\u003Ccode>POST \u002Fv2\u002Ffetch\u002Fcompanies\u002Flive\u003C\u002Fcode>\u003C\u002Fa>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch2 id=\"1-configure-your-webhook-url\">1. Configure your webhook URL\u003C\u002Fh2>\n\u003Ch3 id=\"workspace-default-recommended\">Workspace default (recommended)\u003C\u002Fh3>\n\u003Cp>Set a single HTTPS URL at workspace level so all async results are delivered automatically. No need to include \u003Ccode>webhookUrl\u003C\u002Fcode> in every request.\u003C\u002Fp>\n\u003Col>\n\u003Cli>Go to \u003Cstrong>\u003Ca href=\"\u002Fsettings\u002Fwebhooks\" target=\"_blank\" rel=\"noopener\">Settings &gt; Webhooks\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>Enter your HTTPS endpoint URL\u003C\u002Fli>\n\u003Cli>Click \u003Cstrong>Save\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>Click \u003Cstrong>Test\u003C\u002Fstrong> to send a test payload and verify connectivity\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cdiv class=\"markdown-alert markdown-alert-tip\">\u003Cp class=\"markdown-alert-title\">\u003Csvg class=\"octicon octicon-light-bulb mr-2\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\">\u003Cpath d=\"M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z\">\u003C\u002Fpath>\u003C\u002Fsvg>Tip\u003C\u002Fp>\u003Cp>The workspace default URL enables full delivery tracking in the \u003Cstrong>\u003Ca href=\"\u002Flogs\u002Fevents\" target=\"_blank\" rel=\"noopener\">Webhook Events\u003C\u002Fa>\u003C\u002Fstrong> dashboard. Note that delivery failures are not refunded: the data was fetched, so the credit is kept and the result stays retrievable via \u003Ca href=\"\u002Fdocs\u002Fguides\u002Fpolling\">polling\u003C\u002Fa>.\u003C\u002Fp>\n\u003C\u002Fdiv>\n\u003Ch3 id=\"per-request-webhook-url-override-optional\">Per-request webhook URL override (optional)\u003C\u002Fh3>\n\u003Cp>Include a \u003Ccode>webhookUrl\u003C\u002Fcode> field in your request body to override the delivery target for a single request. This is the fastest way to test locally with a tunnel like ngrok:\u003C\u002Fp>\n\u003Cdiv class=\"docs-code-group\">\u003Cdiv class=\"docs-code-group-header\">\u003Cspan class=\"docs-code-group-title\" data-static-title>Local tunnel\u003C\u002Fspan>\u003Cdiv class=\"docs-code-group-actions\">\u003Cspan class=\"docs-code-group-lang\">Bash\u003C\u002Fspan>\u003Cbutton type=\"button\" class=\"docs-code-group-copy\" data-copy-code=\"true\">\u003Csvg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\u003Crect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"\u002F>\u003Cpath d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"docs-code-group-panel active\" data-tab=\"0\" role=\"tabpanel\">\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\" data-language=\"bash\">\u003Ccode class=\"language-bash\">\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">ngrok\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> http\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> 3000\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fdiv>\u003C\u002Fdiv>\n\u003Cp>Then use the forwarding URL in your request:\u003C\u002Fp>\n\u003Cdiv class=\"docs-code-group\">\u003Cdiv class=\"docs-code-group-header\">\u003Cspan class=\"docs-code-group-title\" data-static-title>Request body\u003C\u002Fspan>\u003Cdiv class=\"docs-code-group-actions\">\u003Cspan class=\"docs-code-group-lang\">JSON\u003C\u002Fspan>\u003Cbutton type=\"button\" class=\"docs-code-group-copy\" data-copy-code=\"true\">\u003Csvg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\u003Crect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"\u002F>\u003Cpath d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"docs-code-group-panel active\" data-tab=\"0\" role=\"tabpanel\">\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\" data-language=\"json\">\u003Ccode class=\"language-json\">\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">{\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">  \"url\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"https:\u002F\u002Fsocial.com\u002Fin\u002Fjanedoe\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">  \"webhookUrl\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"https:\u002F\u002Fa1b2c3d4.ngrok-free.app\u002Fwebhooks\u002Freversecontact\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fdiv>\u003C\u002Fdiv>\n\u003Cp>If no webhook URL is available (neither in the request body nor in workspace settings), the API returns \u003Ccode>422\u003C\u002Fcode> immediately:\u003C\u002Fp>\n\u003Cdiv class=\"docs-code-group\">\u003Cdiv class=\"docs-code-group-header\">\u003Cspan class=\"docs-code-group-title\" data-static-title>422 WEBHOOK_URL_REQUIRED\u003C\u002Fspan>\u003Cdiv class=\"docs-code-group-actions\">\u003Cspan class=\"docs-code-group-lang\">JSON\u003C\u002Fspan>\u003Cbutton type=\"button\" class=\"docs-code-group-copy\" data-copy-code=\"true\">\u003Csvg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\u003Crect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"\u002F>\u003Cpath d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"docs-code-group-panel active\" data-tab=\"0\" role=\"tabpanel\">\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\" data-language=\"json\">\u003Ccode class=\"language-json\">\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">{\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">  \"success\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">false\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">  \"data\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">null\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">  \"error\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">    \"code\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"WEBHOOK_URL_REQUIRED\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">    \"message\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"A webhook URL is required to receive results. Either provide a \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">webhookUrl\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">\\\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> in the request body, or configure a default webhook URL in your workspace settings.\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">  }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fdiv>\u003C\u002Fdiv>\n\u003Cp>No credits are consumed. Configure a default URL in \u003Cstrong>\u003Ca href=\"\u002Fsettings\u002Fwebhooks\" target=\"_blank\" rel=\"noopener\">Settings &gt; Webhooks\u003C\u002Fa>\u003C\u002Fstrong> or include \u003Ccode>webhookUrl\u003C\u002Fcode> in your request body.\u003C\u002Fp>\n\u003Ch2 id=\"2-set-up-a-receiver\">2. Set up a receiver\u003C\u002Fh2>\n\u003Cdiv class=\"markdown-alert markdown-alert-tip\">\u003Cp class=\"markdown-alert-title\">\u003Csvg class=\"octicon octicon-light-bulb mr-2\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\">\u003Cpath d=\"M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z\">\u003C\u002Fpath>\u003C\u002Fsvg>Tip\u003C\u002Fp>\u003Cp>Don’t already operate a public HTTPS receiver? Skip this entirely with \u003Ca href=\"\u002Fdocs\u002Fguides\u002Fpolling\">Polling\u003C\u002Fa>. You’ll trigger jobs the same way and pull results on demand, no server required.\u003C\u002Fp>\n\u003C\u002Fdiv>\n\u003Cp>A webhook receiver is a standard POST endpoint that accepts JSON and returns \u003Ccode>200\u003C\u002Fcode>. That’s it.\u003C\u002Fp>\n\u003Cdiv class=\"docs-code-group\">\u003Cdiv class=\"docs-code-group-header\">\u003Cspan class=\"docs-code-group-title\">Node.js \u002F Express\u003C\u002Fspan>\u003Cdiv class=\"docs-code-group-actions\">\u003Cdiv class=\"docs-code-group-selector\">\u003Cbutton type=\"button\" class=\"docs-code-group-trigger\">\u003Cspan class=\"docs-code-group-lang\">Node.js \u002F Express\u003C\u002Fspan>\u003Csvg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\u003Cpath d=\"m7 15 5 5 5-5\"\u002F>\u003Cpath d=\"m7 9 5-5 5 5\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003Cdiv class=\"docs-code-group-menu\">\u003Cbutton type=\"button\" class=\"docs-code-group-option active\" data-tab=\"0\">Node.js \u002F Express\u003C\u002Fbutton>\u003Cbutton type=\"button\" class=\"docs-code-group-option\" data-tab=\"1\">Python \u002F Flask\u003C\u002Fbutton>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cbutton type=\"button\" class=\"docs-code-group-copy\" data-copy-code=\"true\">\u003Csvg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\u003Crect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"\u002F>\u003Cpath d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"docs-code-group-panel active\" data-tab=\"0\" role=\"tabpanel\">\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\" data-language=\"javascript\">\u003Ccode class=\"language-javascript\">\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">const\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> express\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> =\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> require\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"express\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">const\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> app\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> =\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> express\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">app.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">use\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(express.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">json\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">());\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">app.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">post\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"\u002Fwebhooks\u002Freversecontact\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, (\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#E36209;--shiki-dark:#FFAB70\">req\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#E36209;--shiki-dark:#FFAB70\">res\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">) \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">=>\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">  const\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> event\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> =\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> req.body;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">  console.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">log\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"Webhook received:\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, event.webhookId);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">  if\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> (event.data) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6A737D\">    \u002F\u002F Process the result, e.g. from POST \u002Fv2\u002Ffetch\u002Fpersons\u002Flive\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">    console.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">log\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"Profile:\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, event.data.firstName, event.data.lastName);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">  }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6A737D\">  \u002F\u002F Always respond with 200 to acknowledge receipt\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">  res.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">status\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">200\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">).\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">json\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">({ received: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">true\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> });\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">});\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">app.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">listen\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">3000\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, () \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">=>\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> console.\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">log\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"Listening on port 3000\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">));\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fdiv>\u003Cdiv class=\"docs-code-group-panel\" data-tab=\"1\" role=\"tabpanel\">\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\" data-language=\"python\">\u003Ccode class=\"language-python\">\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">from\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> flask \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> Flask, request, jsonify\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">app \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> Flask(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">__name__\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">@app.route\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"\u002Fwebhooks\u002Freversecontact\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#E36209;--shiki-dark:#FFAB70\">methods\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">[\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"POST\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">])\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">def\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> webhook\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">():\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">    event \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> request.get_json()\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">    print\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"Webhook received:\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, event[\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"webhookId\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">])\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">    if\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> \"data\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> in\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> event:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6A737D\">        # Process the result, e.g. from POST \u002Fv2\u002Ffetch\u002Fpersons\u002Flive\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">        print\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"Profile:\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">, event[\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"data\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">][\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"firstName\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">], event[\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"data\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">][\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"lastName\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">])\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6A737D\">    # Always respond with 200 to acknowledge receipt\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">    return\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> jsonify({\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"received\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">True\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">}), \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">200\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">if\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> __name__\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> ==\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\"> \"__main__\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">    app.run(\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#E36209;--shiki-dark:#FFAB70\">port\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">3000\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fdiv>\u003C\u002Fdiv>\n\u003Ch2 id=\"webhook-payload\">Webhook payload\u003C\u002Fh2>\n\u003Ch3 id=\"error-callback\">Error callback\u003C\u002Fh3>\n\u003Cp>When something goes wrong, you receive a callback with an \u003Ccode>errorCode\u003C\u002Fcode> inside \u003Ccode>data\u003C\u002Fcode> describing the issue. The original input fields are echoed back alongside the error code:\u003C\u002Fp>\n\u003Cdiv class=\"docs-code-group\">\u003Cdiv class=\"docs-code-group-header\">\u003Cspan class=\"docs-code-group-title\" data-static-title>Error callback\u003C\u002Fspan>\u003Cdiv class=\"docs-code-group-actions\">\u003Cspan class=\"docs-code-group-lang\">JSON\u003C\u002Fspan>\u003Cbutton type=\"button\" class=\"docs-code-group-copy\" data-copy-code=\"true\">\u003Csvg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\u003Crect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"\u002F>\u003Cpath d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"docs-code-group-panel active\" data-tab=\"0\" role=\"tabpanel\">\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\" data-language=\"json\">\u003Ccode class=\"language-json\">\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">{\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">  \"webhookId\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">  \"errorCode\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"...\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fdiv>\u003C\u002Fdiv>\n\u003Ch3 id=\"result-callback\">Result callback\u003C\u002Fh3>\n\u003Cp>When the job completes successfully, the callback carries the requested data in \u003Ccode>data\u003C\u002Fcode>:\u003C\u002Fp>\n\u003Cdiv class=\"docs-code-group\">\u003Cdiv class=\"docs-code-group-header\">\u003Cspan class=\"docs-code-group-title\" data-static-title>Result callback\u003C\u002Fspan>\u003Cdiv class=\"docs-code-group-actions\">\u003Cspan class=\"docs-code-group-lang\">JSON\u003C\u002Fspan>\u003Cbutton type=\"button\" class=\"docs-code-group-copy\" data-copy-code=\"true\">\u003Csvg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\u003Crect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"\u002F>\u003Cpath d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"\u002F>\u003C\u002Fsvg>\u003C\u002Fbutton>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"docs-code-group-panel active\" data-tab=\"0\" role=\"tabpanel\">\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\" data-language=\"json\">\u003Ccode class=\"language-json\">\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">{\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">  \"webhookId\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">  \"data\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">    \"...\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"endpoint-specific result payload\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">  }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fdiv>\u003C\u002Fdiv>\n\u003Cdiv class=\"markdown-alert markdown-alert-tip\">\u003Cp class=\"markdown-alert-title\">\u003Csvg class=\"octicon octicon-light-bulb mr-2\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\">\u003Cpath d=\"M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z\">\u003C\u002Fpath>\u003C\u002Fsvg>Tip\u003C\u002Fp>\u003Cp>Check for \u003Ccode>errorCode\u003C\u002Fcode> inside \u003Ccode>data\u003C\u002Fcode> to distinguish errors from successful results. If \u003Ccode>data.errorCode\u003C\u002Fcode> is present, the request failed. Otherwise treat it as the terminal success for that \u003Ccode>webhookId\u003C\u002Fcode>.\u003C\u002Fp>\n\u003C\u002Fdiv>\n\u003Ch3 id=\"enrichment-error-codes\">Enrichment error codes\u003C\u002Fh3>\n\u003Cp>These codes describe the enrichment outcome and arrive as \u003Ccode>data.errorCode\u003C\u002Fcode> in the callback:\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Code\u003C\u002Fth>\n\u003Cth>Description\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>invalid-request\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The request input is malformed or doesn’t match the expected format\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>data-not-found\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>No matching result was found\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>person-not-found\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The person profile doesn’t exist\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>company-not-found\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The company profile doesn’t exist\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>email-not-found\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>No email could be resolved for this person\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>fetch-data-error\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The API failed while fetching data. Safe to retry\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>result-unavailable\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The enrichment completed but the result was lost on our side. Retry the job. If the request was charged, contact support to get the credit back.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch3 id=\"delivery-error-codes\">Delivery error codes\u003C\u002Fh3>\n\u003Cp>These codes mean the enrichment was fetched successfully but the result could not be pushed to your webhook URL. The job is reported as \u003Ccode>succeeded\u003C\u002Fcode>, the \u003Ccode>result\u003C\u002Fcode> is still available, and the failed push is named by a \u003Ccode>deliveryStatus\u003C\u002Fcode> field (set to one of the codes below) on the poll and \u003Ccode>check_task\u003C\u002Fcode> responses.\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Code\u003C\u002Fth>\n\u003Cth>Billed\u003C\u002Fth>\n\u003Cth>Description\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>webhook-url-invalid\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Yes\u003C\u002Ftd>\n\u003Ctd>Your webhook returned a \u003Ccode>4xx\u003C\u002Fcode> response\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>webhook-url-errored\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Yes\u003C\u002Ftd>\n\u003Ctd>Your webhook returned a \u003Ccode>5xx\u003C\u002Fcode> or a network error\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>webhook-url-timeout\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Yes\u003C\u002Ftd>\n\u003Ctd>Your webhook did not respond in time\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>webhook-url-unreachable\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Yes\u003C\u002Ftd>\n\u003Ctd>The webhook URL could not be reached from production\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cdiv class=\"markdown-alert markdown-alert-note\">\u003Cp class=\"markdown-alert-title\">\u003Csvg class=\"octicon octicon-info mr-2\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\">\u003Cpath d=\"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\">\u003C\u002Fpath>\u003C\u002Fsvg>Note\u003C\u002Fp>\u003Cp>Delivery failures are billed, not refunded: the data was successfully fetched, so the credit is kept. The job stays \u003Ccode>succeeded\u003C\u002Fcode> and the \u003Ccode>result\u003C\u002Fcode> is populated, poll \u003Ccode>GET \u002Fv2\u002Fwebhooks\u002F:webhookId\u003C\u002Fcode> to retrieve it. The \u003Ccode>deliveryStatus\u003C\u002Fcode> field tells you the push failed (and why) without flipping the job to \u003Ccode>errored\u003C\u002Fcode>.\u003C\u002Fp>\n\u003C\u002Fdiv>\n\u003Ch2 id=\"security-best-practices\">Security best practices\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Use HTTPS.\u003C\u002Fstrong> The API rejects plain HTTP URLs for webhook delivery.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Validate the payload shape.\u003C\u002Fstrong> Every callback contains \u003Ccode>webhookId\u003C\u002Fcode> + \u003Ccode>data\u003C\u002Fcode>. Check for \u003Ccode>errorCode\u003C\u002Fcode> inside \u003Ccode>data\u003C\u002Fcode> to distinguish errors from results. Ignore anything that doesn’t match.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Verify the webhookId.\u003C\u002Fstrong> Store the \u003Ccode>webhookId\u003C\u002Fcode> from the initial response and check it against incoming callbacks. This is your correlation and validation key.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Respond quickly.\u003C\u002Fstrong> Return a \u003Ccode>200\u003C\u002Fcode> status code promptly. If you need to do heavy processing, acknowledge receipt first and process asynchronously.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"dashboard-monitoring\">Dashboard monitoring\u003C\u002Fh2>\n\u003Cp>The \u003Cstrong>\u003Ca href=\"\u002Flogs\u002Fevents\" target=\"_blank\" rel=\"noopener\">Webhook Events\u003C\u002Fa>\u003C\u002Fstrong> page in your dashboard gives you full visibility into every webhook delivery:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Status distribution.\u003C\u002Fstrong> A summary showing succeeded, errored, and in-progress events at a glance.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Event log.\u003C\u002Fstrong> Each row displays the timestamp, \u003Ccode>webhookId\u003C\u002Fcode>, status, credits consumed, credits refunded, and error code (if any).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Filters.\u003C\u002Fstrong> Narrow down events by status, error code, or \u003Ccode>webhookId\u003C\u002Fcode> to quickly find specific deliveries.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"go-to-production\">Go to production\u003C\u002Fh2>\n\u003Cp>Before going live, make sure your webhook integration is solid:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Deduplicate on webhookId.\u003C\u002Fstrong> In rare cases, a callback may be delivered more than once.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Log the webhookId.\u003C\u002Fstrong> Include it in your application logs to trace requests end-to-end.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Monitor your credits.\u003C\u002Fstrong> Credits are deducted when the request is accepted, before the result is delivered. Refundable errors are automatically refunded. See \u003Ca href=\"\u002Fdocs\u002Fguides\u002Frate-limits-credits\">Rate limits &amp; credits\u003C\u002Fa> for details.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>For the full hardening checklist, see \u003Ca href=\"\u002Fdocs\u002Fguides\u002Fproduction-checklist\">Production checklist\u003C\u002Fa>.\u003C\u002Fp>\n\u003Ch2 id=\"troubleshooting\">Troubleshooting\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Not receiving webhooks\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Ensure your endpoint is publicly accessible from the internet (not just \u003Ccode>localhost\u003C\u002Fcode>)\u003C\u002Fli>\n\u003Cli>If you omitted \u003Ccode>webhookUrl\u003C\u002Fcode> from the request, verify that a workspace default URL is configured in \u003Cstrong>\u003Ca href=\"\u002Fsettings\u002Fwebhooks\" target=\"_blank\" rel=\"noopener\">Settings &gt; Webhooks\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>Verify it accepts \u003Ccode>POST\u003C\u002Fcode> requests with a \u003Ccode>Content-Type: application\u002Fjson\u003C\u002Fcode> body\u003C\u002Fli>\n\u003Cli>Check your server\u002Ffirewall logs for incoming requests\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>Timeout errors\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Your webhook endpoint must respond within 10 seconds\u003C\u002Fli>\n\u003Cli>If processing takes longer, return \u003Ccode>200\u003C\u002Fcode> immediately and handle the data asynchronously\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>Receiving an error or not-found callback instead of data\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul>\n\u003Cli>This is expected. The API sends a callback with an \u003Ccode>errorCode\u003C\u002Fcode> in \u003Ccode>data\u003C\u002Fcode> when the result is not available (see \u003Ca href=\"#error-codes\">Error codes\u003C\u002Fa>)\u003C\u002Fli>\n\u003Cli>Verify the URL does not require authentication or return redirects\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>Duplicate deliveries\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Use the \u003Ccode>webhookId\u003C\u002Fcode> to deduplicate on your side\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>Transient errors\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>fetch-data-error\u003C\u002Fcode> is usually transient and safe to retry after a short delay\u003C\u002Fli>\n\u003Cli>\u003Ccode>result-unavailable\u003C\u002Fcode> means the enrichment completed but the result was lost on our side. Retry the job. If the request was charged, contact support to get the credit back\u003C\u002Fli>\n\u003C\u002Ful>\n","","[!TIP] Most teams ship faster with Polling. No public endpoint required, free to call, around 5 minutes to first result. The webhook setup below is documented for teams that alr...",7,[20,24,27,30,33,37,40,43,46,49,52,55,58,61,64,67],{"id":21,"title":22,"level":23},"introduction","Introduction",2,{"id":25,"title":26,"level":23},"how-it-works","How it works",{"id":28,"title":29,"level":23},"async-endpoints","Async endpoints",{"id":31,"title":32,"level":23},"1-configure-your-webhook-url","1. Configure your webhook URL",{"id":34,"title":35,"level":36},"workspace-default-recommended","Workspace default (recommended)",3,{"id":38,"title":39,"level":36},"per-request-webhook-url-override-optional","Per-request webhook URL override (optional)",{"id":41,"title":42,"level":23},"2-set-up-a-receiver","2. Set up a receiver",{"id":44,"title":45,"level":23},"webhook-payload","Webhook payload",{"id":47,"title":48,"level":36},"error-callback","Error callback",{"id":50,"title":51,"level":36},"result-callback","Result callback",{"id":53,"title":54,"level":36},"enrichment-error-codes","Enrichment error codes",{"id":56,"title":57,"level":36},"delivery-error-codes","Delivery error codes",{"id":59,"title":60,"level":23},"security-best-practices","Security best practices",{"id":62,"title":63,"level":23},"dashboard-monitoring","Dashboard monitoring",{"id":65,"title":66,"level":23},"go-to-production","Go to production",{"id":68,"title":69,"level":23},"troubleshooting","Troubleshooting",[],[72,73,74,75],"\u002Fdocs\u002Fguides\u002Fpolling","\u002Fdocs\u002Fguides\u002Fproduction-checklist","\u002Fdocs\u002Fguides\u002Ferrors-retries","\u002Fdocs\u002Fguides\u002Fwhich-endpoint-should-i-use",{"label":77,"color":78},"Advanced","neutral",1785860237274]