Diligent Webhooks API

## How to Secure Webhook Deliveries To ensure that webhook payloads are securely transmitted and verified. This guide explains how to configure and validate webhook deliveries using a shared secret. ### How It Works When setting up a webhook, a secret is configured on both the sender (our system) and the receiver (your endpoint). Each webhook payload is signed using this secret, allowing the receiver to verify its authenticity. #### Step 1: Configuring Your Webhook Secret 1. When creating a webhook in our system, specify a unique secret key. This secret should be a strong, randomly generated string. 2. Store this secret securely on your server; it should never be exposed publicly. #### Step 2: Receiving Webhook Payloads When your server receives a webhook event, the request will include an `X-Signature` header containing a HMAC signature of the payload. Example header: ``` X-Signature: sha256=abcdef1234567890... ``` #### Step 3: Validating the Webhook Signature To verify the webhook payload: 1. Retrieve the `X-Signature` value from the request headers. 2. Compute the HMAC SHA-256 signature of the request payload using your webhook secret. 3. Compare the computed signature with the one in the `X-Signature` header. 4. If they match, the webhook is valid. #### (Python) ```python import hashlib import hmac import json def verify_webhook_signature(secret, payload, signature): computed_signature = hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest() expected_signature = f"sha256={computed_signature}" return hmac.compare_digest(expected_signature, signature) # Example usage: secret = "your_webhook_secret" payload = json.dumps({"event": "example"}) received_signature = "sha256=abcdef1234567890..." if verify_webhook_signature(secret, payload, received_signature): print("Valid webhook received!") else: print("Invalid webhook signature!") ``` #### (JavaScript) ```javascript const crypto = require('crypto'); function verifyWebhookSignature (secret, payload, signature) { const computedSignature = `sha256=${crypto.createHmac('sha256', secret) .update(payload) .digest('hex')}`; return crypto.timingSafeEqual(Buffer.from(computedSignature), Buffer.from(signature)); } // Example usage: const secret = "your_webhook_secret"; const payload = JSON.stringify({ event: "example" }); const receivedSignature = "sha256=abcdef1234567890..."; if (verifyWebhookSignature(secret, payload, receivedSignature)) { console.log("Valid webhook received!"); } else { console.log("Invalid webhook signature!"); } ``` #### Security Considerations - Always use HTTPS to prevent interception of webhook payloads. - Reject webhook requests that fail signature validation. - Rotate secrets periodically to enhance security. By following this guide, you ensure that webhook deliveries are secure and trusted.

OpenAPI Specification

diligent-webhooks-api-openapi.yml Raw ↑
openapi: 3.0.1
info:
  version: 1.6.0
  title: Diligent CDD Webhooks API
  description: 'Download Postman collection [here](https://docs.godiligent.ai/files/postman_collection.json).

    '
servers:
- url: https://api.godiligent.ai
  description: Production
- url: https://api.sandbox.godiligent.ai
  description: Sandbox
security:
- xApiKey: []
tags:
- name: Webhooks
  description: "\n## How to Secure Webhook Deliveries\nTo ensure that webhook payloads are securely transmitted and verified. This guide explains how to configure and validate\nwebhook deliveries using a shared secret.\n\n### How It Works\n\nWhen setting up a webhook, a secret is configured on both the sender (our system) and the receiver (your endpoint). Each\nwebhook payload is signed using this secret, allowing the receiver to verify its authenticity.\n\n#### Step 1: Configuring Your Webhook Secret\n\n1. When creating a webhook in our system, specify a unique secret key. This secret should be a strong, randomly\ngenerated string.\n2. Store this secret securely on your server; it should never be exposed publicly.\n\n#### Step 2: Receiving Webhook Payloads\n\nWhen your server receives a webhook event, the request will include an `X-Signature` header containing a HMAC signature\nof the payload.\n\nExample header:\n\n```\nX-Signature: sha256=abcdef1234567890...\n```\n\n#### Step 3: Validating the Webhook Signature\n\nTo verify the webhook payload:\n\n1. Retrieve the `X-Signature` value from the request headers.\n2. Compute the HMAC SHA-256 signature of the request payload using your webhook secret.\n3. Compare the computed signature with the one in the `X-Signature` header.\n4. If they match, the webhook is valid.\n\n#### (Python)\n\n```python\nimport hashlib\nimport hmac\nimport json\n\ndef verify_webhook_signature(secret, payload, signature):\n  computed_signature = hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()\n  expected_signature = f\"sha256={computed_signature}\"\n  return hmac.compare_digest(expected_signature, signature)\n\n# Example usage:\nsecret = \"your_webhook_secret\"\npayload = json.dumps({\"event\": \"example\"})\nreceived_signature = \"sha256=abcdef1234567890...\"\n\nif verify_webhook_signature(secret, payload, received_signature):\n  print(\"Valid webhook received!\")\nelse:\n  print(\"Invalid webhook signature!\")\n```\n\n#### (JavaScript)\n\n```javascript\nconst crypto = require('crypto');\n\nfunction verifyWebhookSignature (secret, payload, signature) {\nconst computedSignature = `sha256=${crypto.createHmac('sha256', secret)\n.update(payload)\n.digest('hex')}`;\nreturn crypto.timingSafeEqual(Buffer.from(computedSignature), Buffer.from(signature));\n}\n\n// Example usage:\nconst secret = \"your_webhook_secret\";\nconst payload = JSON.stringify({ event: \"example\" });\nconst receivedSignature = \"sha256=abcdef1234567890...\";\n\nif (verifyWebhookSignature(secret, payload, receivedSignature)) {\nconsole.log(\"Valid webhook received!\");\n} else {\nconsole.log(\"Invalid webhook signature!\");\n}\n```\n\n#### Security Considerations\n\n- Always use HTTPS to prevent interception of webhook payloads.\n- Reject webhook requests that fail signature validation.\n- Rotate secrets periodically to enhance security.\n\nBy following this guide, you ensure that webhook deliveries are secure and trusted.\n"
paths:
  /webhooks:
    post:
      summary: Register or update webhook configuration
      operationId: registerWebhook
      tags:
      - Webhooks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                webhook_url:
                  type: string
                  format: uri
                  description: Must be a valid URL starting with https.
                is_active:
                  type: boolean
                  default: true
                  description: Indicates if the webhook is active.
                secret:
                  type: string
                  description: Secret key to sign the webhook payload, and will be passed in the `X-Signature` header.
                events:
                  type: array
                  description: List of events to trigger the webhook. If not provided, no events will be triggered.
                  items:
                    type: string
                    enum:
                    - cdd_state_changed
                    - monitoring_alert_fired
                    description: "__cdd_state_changed__: When the state of a CDD changes to either inconclusive or complete. \n__monitoring_alert_fired__: When a monitoring alert is fired."
              required:
              - webhook_url
      responses:
        '200':
          description: Webhook configuration created or updated.
        '400':
          description: Invalid input or validation error.
    get:
      summary: List registered webhooks
      operationId: listWebhooks
      tags:
      - Webhooks
      responses:
        '200':
          description: List of registered webhooks (limited to 1 for now).
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique ID for the webhook.
                    webhook_url:
                      type: string
                      format: uri
                    is_active:
                      type: boolean
                    events:
                      type: array
                      items:
                        type: string
                        enum:
                        - cdd_state_changed
                        - monitoring_alert_fired
  /webhooks/{webhook_id}/events:
    get:
      tags:
      - Webhooks
      summary: List recent webhook deliveries
      operationId: listWebhookDeliveries
      parameters:
      - name: webhook_id
        in: path
        required: true
        description: Webhook ID to filter deliveries.
        schema:
          type: string
      responses:
        '200':
          description: List of recent webhook delivery events.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                      format: uuid
                    webhook_id:
                      type: string
                      format: uuid
                    event_type:
                      type: string
                    timestamp:
                      type: string
                      format: date-time
                    status_code:
                      type: integer
                      example: 204
                    request:
                      type: object
                      properties:
                        headers:
                          type: object
                        payload:
                          type: object
                    response:
                      type: object
                      properties:
                        headers:
                          type: object
                        body:
                          type: object
  /webhooks/{webhook_id}/events/{event_id}/redeliver:
    post:
      summary: Redeliver a webhook event
      operationId: redeliverWebhook
      tags:
      - Webhooks
      parameters:
      - name: webhook_id
        in: path
        required: true
        description: Webhook ID for the event to redeliver.
        schema:
          type: string
      - name: event_id
        in: path
        required: true
        description: Event ID of the webhook to redeliver.
        schema:
          type: string
      responses:
        '204':
          description: Webhook redelivery initiated.
        '404':
          description: Event or Webhook not found.
        '500':
          description: Error during redelivery.
components:
  securitySchemes:
    xApiKey:
      type: apiKey
      name: X-API-KEY
      in: header