--- title: "Enquiry Webhook Request" slug: "webhook-request" updated: 2026-10-21T12:40:12Z published: 2026-10-21T12:40:12Z canonical: "support.carehome.co.uk/webhook-request" --- > ## Documentation Index > Fetch the complete documentation index at: https://support.carehome.co.uk/llms.txt > Use this file to discover all available pages before exploring further. # Enquiry Webhook Request Each enquiry webhook is an HTTP `POST` request to your URL with signed headers and a JSON body. This page describes the request, how to check its signature and how your system should reply. > [!NOTE] > **Who can use it:** enquiry webhooks are available to Enhanced and Platinum subscribers. See [Enquiry Webhooks Overview](https://support.carehome.co.uk/docs/webhooks-overview) and [Enquiry Webhook Setup](https://support.carehome.co.uk/docs/webhook-setup). ## Headers | Header | Description | | --- | --- | | `Content-Type` | `application/json; charset=utf-8` | | `webhook-id` | The message ID, 32 letters and numbers. It stays the same if we retry the same message, so you can use it to process each message only once. | | `webhook-timestamp` | When this attempt was sent, as a Unix timestamp in seconds, for example `1792488372`. It changes on each retry. | | `webhook-signature` | The signature of this attempt, as 64 lowercase hexadecimal characters. See [Check the signature](/docs/webhook-request#verify). | | `webhook-url-id` | The ID of the webhook this request was sent for. It matches the Webhook URL ID in the Portal. | ## Body The body is a JSON object with three properties: | Field | Type | Description | | --- | --- | --- | | `type` | string | The enquiry type, for example `care.create`. Test events end in `.test`, for example `care.create.test`. See [Enquiry Webhook Categories](https://support.carehome.co.uk/docs/webhooks-categories). | | `timestamp` | string | When the event was created, in ISO 8601 format with a time zone, for example `2026-10-20T09:26:10.344Z`. | | `data` | object | The enquiry. See [Enquiry data](/docs/webhook-request#data). | ``` {  "type": "care.create",  "timestamp": "2026-10-20T09:26:10.344Z",  "data": {    "id": 12345678,    "category": { "id": 4, "name": "Care Enquiry" },    ...  } } ``` ### Enquiry data Every enquiry type has the fields below. Fields that were not collected for an enquiry are sent as `null`. | Field | Type | Description | | --- | --- | --- | | `id` | integer | The enquiry ID. For `call.create` this is the ID of the call record, which is a separate series of numbers, so use `type` and `id` together to identify an enquiry. | | `category.id` | integer | The category ID. See [Enquiry Webhook Categories](https://support.carehome.co.uk/docs/webhooks-categories). | | `category.name` | string | The category name, for example `Care Enquiry`. | | `member.id` | integer or string | The ID of the setting the enquiry was sent to. Store it as text, because large IDs can be sent as a string. | | `member.name` | string | The setting's name. | | `member.postcode` | string | The setting's postcode. | | `group` | object or null | The setting's group, with the same `id`, `name` and `postcode` fields as `member`. `null` if the setting is not in a group. | | `emailto` | string or null | The email address the enquiry was sent to. Always `null` for `call.create`. | | `emailfrom` | string or null | The enquirer's email address. | | `entrydate` | string | When we received the enquiry, or when the call started for `call.create`, in ISO 8601 format in UTC, for example `2026-10-20T09:26:07.000Z`. For the time the event was created, use `timestamp`. | | `bodycontent` | string | The enquiry as it appears in the enquiry email. It can be plain text or HTML. For `tour.create` and `call.create` it is an HTML summary of the request or call. | | `firstname` | string or null | The enquirer's first name. | | `surname` | string or null | The enquirer's surname. | | `address1`, `address2`, `address3` | string or null | The enquirer's address, if they gave one. Usually only collected for brochure requests. | | `town` | string or null | The enquirer's town. | | `postcode` | string or null | The enquirer's postcode. | | `telephone` | string or null | The enquirer's phone number. | | `isAnonymised` | string | `Yes` if the enquirer's personal details have been removed, otherwise `No`. | | `categorySpecificData` | object | Extra fields that depend on the enquiry type. See the example page for each type, listed in [Enquiry Webhook Categories](https://support.carehome.co.uk/docs/webhooks-categories). | ## Check the signature Check the signature of every request before you use the data. You need the webhook's secret key from the Portal (**APIs & Integrations → Webhooks**, Secret column). 1. Join the `webhook-id` header, the `webhook-timestamp` header and the raw request body with full stops: `{webhook-id}.{webhook-timestamp}.{body}`. Use the body exactly as you received it, before parsing it as JSON. 2. Calculate the HMAC-SHA256 of that text, using the secret key exactly as it is shown in the Portal. 3. Write the result as lowercase hexadecimal and compare it with the `webhook-signature` header, using a constant-time comparison. 4. Reject the request if they do not match. We also recommend rejecting requests whose `webhook-timestamp` is more than 5 minutes from your current time. Our header names follow the [Standard Webhooks](https://www.standardwebhooks.com/) naming. For example, with the webhook ID `3f2c9a7b1e4d4f0a8b6c5d2e1f0a9b8c`, the timestamp `1792488372` and the body `{"type":"care.create","timestamp":"2026-10-20T09:26:10.344Z","data":{"id":12345678}}`, the text to sign is: ``` 3f2c9a7b1e4d4f0a8b6c5d2e1f0a9b8c.1792488372.{"type":"care.create","timestamp":"2026-10-20T09:26:10.344Z","data":{"id":12345678}} ``` ### Node.js ``` const crypto = require('crypto'); function isValidWebhook(headers, rawBody, secret) {  const signedText = `${headers['webhook-id']}.${headers['webhook-timestamp']}.${rawBody}`;  const expected = crypto.createHmac('sha256', secret).update(signedText, 'utf8').digest('hex');  const received = String(headers['webhook-signature'] || '');  return received.length === expected.length    && crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected)); } ``` ### PHP ``` $rawBody = file_get_contents('php://input'); $signedText = $_SERVER['HTTP_WEBHOOK_ID'] . '.' . $_SERVER['HTTP_WEBHOOK_TIMESTAMP'] . '.' . $rawBody; $expected = hash_hmac('sha256', $signedText, $secret); if (!hash_equals($expected, $_SERVER['HTTP_WEBHOOK_SIGNATURE'] ?? '')) {    http_response_code(401);    exit; } ``` ## Reply to the request Reply with any `2xx` status code, such as `200` or `204`, within 15 seconds. We do not read the response body. If you need to do slow work with the enquiry, save it first, reply, then do the work. Use your final URL in the Portal. Do not redirect it to another address. ## Retries and duplicates If your system replies with any other status code, does not reply within 15 seconds or cannot be reached, we try again a limited number of times, then stop. Each attempt is shown in **Webhook Logs** in the Portal. A retry has the same `webhook-id` and body as the first attempt, but a new `webhook-timestamp` and `webhook-signature`. Occasionally you may receive the same enquiry more than once, for example if your system saved it but replied too late. Use `type` and `data.id` together to recognise an enquiry you already have. Enquiries can also arrive in a different order from the one they were sent in.