style table td:first-child { white-space: nowrap; } # ObservePoint Webhooks ## Overview Using webhooks, you can receive an HTTPS notification when your audits and journeys finish running. To use a webhook, you’ll need a web server on the internet that can receive HTTPS requests. ObservePoint can send a JSON payload to any HTTPS URL that you configure. ## Configuring Webhooks Each audit and journey can be configured to send a webhook request on completion. Webhook configuration is optional. You have two options for configuring ObservePoint webhooks: ### Option 1: Configure webhook destination via API When you create a new audit or journey using a `POST` request, you can specify a webhook URL in the `webHookUrl` field. ``` POST https://api.observepoint.com/v2/web-audits POST https://api.observepoint.com/v2/web-journeys ``` For existing audits or journeys, you can assign a webhook URL using the `webHookUrl` field in your `PUT` payload to these API endpoints: ``` PUT https://api.observepoint.com/v2/web-audits/{auditId} PUT https://api.observepoint.com/v2/web-journeys/{journeyId} ``` ### Option 2: Configure webhook destination manually via app.observepoint.com At [app.observepoint.com](https://app.observepoint.com), log in and navigate to the **Data Sources** page. Click the **Edit** button on any audit or journey, enter the webhook URL, and click **Save**. Screenshot of the field within a datasource to add the listening endpoint. ## Webhook Payload Upon completion of an audit or journey, ObservePoint sends the following *POST* payload to the specified URL. The webhook is sent whether the run is successful or not. ```json { "itemId": 12345, "itemType": "audit", // or "web-journey" "runId": 98765 } ``` ### Field Descriptions | Field Name | Description | | --- | --- | | `itemId` | The ID of the audit or journey. | | `runId` | The run ID of the completed audit or journey. | | `itemType` | Indicates whether the run is an `"audit"` or `"web-journey"`. | When you receive a webhook request, you can query the ObservePoint API to download results using the `itemId` and `runId`. ## Example Usages The following are some common use cases for webhooks: - Check for unapproved cookies or tags and record them in an issue tracker, like Jira - Trigger a script to pull the page details report and email the results to recipients listed in the notifications field. - Ingest results into a BI system such as Tableau by triggering a data import when an audit or journey completes. - Integrate with Teams or Slack: trigger a message to your Teams or Slack channel notifying of rule failures. ## Webhook Security through Signatures To enhance webhook security, ObservePoint signs every webhook request with a unique secret key tied to your account. This allows you to verify that incoming webhook requests are genuine and have not been tampered with. ### (One Time Only) Generate Your Webhook Signing Secret Before you can verify webhook signatures, you must generate a signing secret for your account. You must have **Admin** permissions to perform this action. Please refer to [our getting started documentation](/) to learn how to make that request. **Endpoint** ``` POST https://api.observepoint.com/v3/webhooks/rotate-secret ``` **Response** ```json { "sharedSecret": "string", "accountId": number } ``` The `sharedSecret` is shown **only once**. Be sure to store it securely, as it cannot be retrieved again. You can generate a new secret at any time by calling the same endpoint. Invoking this endpoint again will immediately rotate the webhook signing secret for your entire account. Once rotated, all future webhook requests will use the new secret. Any systems or services that verify webhook signatures must be updated with the new secret right away, or verification will fail until they do. Each webhook request sent by ObservePoint includes a signature header that contains both the timestamp and the generated signature. | Header Name | Example Value | | --- | --- | | `ObservePoint-Signature` | `t=1693325764,sigv1=3eae1baf0f7f9ed8f9db7e88f27c2f3f3fbb0a73e839b6e10b0d8bb1a71a449d` | The signature is computed using an HMAC SHA-256 signature of a canonical payload that combines the timestamp and the raw webhook body. ### Verify the Signature To verify the webhook: 1. Extract the timestamp (`t`) and signature (`sigv1`) from the `ObservePoint-Signature` header. 2. Concatenate the timestamp, a period (`.`), and the raw webhook payload body. 3. Compute an [HMAC SHA-256](https://en.wikipedia.org/wiki/HMAC) signature using your stored `sharedSecret`. 4. Compare your computed signature to the value of `sigv1`. If they match, the webhook is verified. #### Sample Signature Verification Code The following code samples show how to verify the webhook signature. If you want to test these code samples, you can use the following test values: | Parameter | Example Value | Description | | --- | --- | --- | | `secret` | `epFiUaBZdFbRGn7+8v4LD4n0l3VxbTvxwnj3UMpgrlg==` | An example signing secret key returned by `POST https://api.observepoint.com/v3/webhooks/rotate-secret` | | `raw_body` | `{"runId":873230,"itemType":"web-journey","itemId":748316}` | An example webhook body used to compute the signature | | `header_value` | `t=1760064757,sigv1=00gNS2RGUZJrOBiHwgGJm9Zb1oDRPsVjt5F1NhmVIzc=` | An example `ObservePoint-Signature` header value sent with the webhook | #### Python ```python import re, hmac, hashlib, base64 def verify_observepoint_webhook(secret: str, webhook_body: str, header_value: str) -> bool: """ Verify the ObservePoint webhook signature using HMAC-SHA256. Args: secret (str): The signing secret returned by https://api.observepoint.com/v3/webhooks/rotate-secret webhook_body (str): The exact request body string. Example: '{"runId":873230,"itemType":"web-journey","itemId":748316}' header_value (str): The value of the ObservePoint-Signature header. Example: 't=1234567890,sigv1=ABC123...' Returns: bool: True if the signature is valid, False otherwise. """ header_match = re.search(r't=(\d+),sigv1=([^,$]+)', header_value) if header_match: header_timestamp = header_match.group(1) header_signature = header_match.group(2) else: raise ValueError(f'Invalid signature header: {header_value}') key = base64.b64decode(secret) to_sign = f"{header_timestamp}.{webhook_body}".encode('utf-8') calculated_signature_bytes = hmac.new(key, to_sign, hashlib.sha256).digest() header_signature_bytes = base64.b64decode(header_signature) return hmac.compare_digest(calculated_signature_bytes, header_signature_bytes) ``` #### JavaScript (Node.js) ```javascript import crypto from 'crypto'; /** * Verify the ObservePoint webhook signature using HMAC-SHA256. * * @param {string} secret - The signing secret returned by https://api.observepoint.com/v3/webhooks/rotate-secret * @param {string} webhookBody - The exact request body string. Example: '{"runId":873230,"itemType":"web-journey","itemId":748316}' * @param {string} headerValue - The value of the ObservePoint-Signature header. Example: 't=1234567890,sigv1=ABC123...' * @returns {boolean} true if the signature is valid, false otherwise */ function verifyObservepointWebhook(secret, webhookBody, headerValue) { const headerMatch = headerValue.match(/t=(\d+),sigv1=([^,$]+)/); if (!headerMatch) { throw new Error(`Invalid signature header: ${headerValue}`); } const headerTimestamp = headerMatch[1]; const headerSignature = headerMatch[2]; const key = Buffer.from(secret, 'base64'); const toSign = `${headerTimestamp}.${webhookBody}`; const calculatedSignature = crypto .createHmac('sha256', key) .update(toSign, 'utf8') .digest(); const headerSignatureBytes = Buffer.from(headerSignature, 'base64'); return calculatedSignature.length === headerSignatureBytes.length && crypto.timingSafeEqual(calculatedSignature, headerSignatureBytes); } ``` ### Handling Failed Verification If the header signature does not match your computed signature, you should reject the webhook request by responding with a 403 status code (or other error status code). ## Secret Rotation When it’s time to rotate your webhook signing secret, whether for routine security maintenance or after an incident, you can generate a new secret using the [Generate Your Webhook Signing Secret](#1.-generate-your-webhook-signing-secret) section above. Rotating the secret immediately replaces the existing signing secret for your entire account. Any subsequent webhook requests will be signed using the new secret. To safely rotate your secret: 1. **Generate and store the new secret** securely. 2. **Update all receiving services** (such as your webhook consumer, middleware, or verification scripts) to use the new secret. 3. **Verify webhook delivery** using the new signature to ensure your systems are properly configured. Once a new secret is generated, the old secret stops being used immediately. Webhooks signed with the new secret will not validate against the old one.