Skip to main content

Introduction

Webhooks let you send structured results from your Cradl AI agent to any HTTP endpoint. Use this to push data into your own service or systems like n8n, Make, or custom APIs.

Before you begin

  • Have an HTTPS endpoint that accepts JSON requests
  • Optionally prepare a request inspector (e.g., your own dev endpoint) to verify the payload
  • Upload a test document to your agent so there’s data to send

Configure your webhook

The steps below add the webhook export in Cradl AI and verify that your endpoint receives the payload.
1

Add a Webhook export

In your agent, add a new export and select Webhook. You’ll see fields for the URL, HTTP Method, and optional HTTP Headers.
Webhook settings in Cradl AI
2

Define URL and method

Enter your endpoint in URL and choose the HTTP Method your service expects (POST is common). Cradl AI sends a JSON body with the extracted data and run context.
3

Add headers (optional)

Add custom HTTP Headers for example for authentication or routing.
4

Test the webhook

Click Test webhook to send a sample payload to your URL. Confirm that your endpoint receives the request and returns a 2xx response.

Payload format

Your endpoint receives a JSON body similar to the example below. The output section contains field values organized by group and field IDs. Each field entry includes the recognized value, original rawValue, confidence, and page. The context section includes references like runId and documentId.
{
  "output": {
    "n2379dpkjj": [
      {
        "d6xz8w5wnr": {
          "rawValue": "The Meat",
          "value": "The Meat",
          "confidence": 0.926,
          "page": 0,
          "name": "Merchant"
        },
        "2i1sspmmxw": {
          "rawValue": "2017-05-23",
          "value": "2017-05-23",
          "confidence": 0.945,
          "page": 0,
          "name": "Purchase Date"
        },
        "a8b40ix8k9": {
          "rawValue": "138,00",
          "value": "138",
          "confidence": 0.982,
          "page": 0,
          "name": "Total Amount"
        },
        "s5otx28oyk": {
          "rawValue": "SEK",
          "value": "SEK",
          "confidence": 0.244,
          "page": 0,
          "name": "Currency"
        }
      }
    ]
  },
  "context": {
    "runId": "cradl:run:eee4d54340cfe1071a98735234a66755",
    "documentId": "cradl:document:349e99a0d98046db8219b70393a3289f"
  }
}
Field and group IDs are stable identifiers. The human-friendly field name is available under name for convenience. Prefer the normalized value when mapping, and fall back to rawValue if needed.

Downloading the file (optional)

The webhook payload does not include the original file. If you need it, please refer to the API reference.

Verifying signatures

Each webhook request is signed with an HMAC SHA‑256 signature so you can verify authenticity and protect against replay attacks. The following headers are automatically added by Cradl AI:
  • X-Cradl-Timestamp — Unix timestamp (seconds) when the request was created
  • X-Cradl-Nonce — A random UUIDv4 (hex, no dashes)
  • X-Cradl-Signature — Hex-encoded HMAC SHA‑256 over the message described below

How the signature is computed

Given a shared signing secret, the signature is computed on the concatenation of these exact byte sequences:
  1. HTTP method (e.g., POST), as bytes
  2. Full request URL (including query string), as bytes
  3. All HTTP headers except X-Cradl-Signature, sorted by header name and concatenated with no delimiter as key:value pairs, then concatenated together (still no delimiter).
  4. Raw request body bytes (the JSON payload)
Reject requests where X-Cradl-Timestamp is too old (e.g., >5 minutes) or where a previously seen X-Cradl-Nonce is reused

Examples

The code examples below have been generated by an AI. Use them as a starting point and verify correctness before using in production.
import hmac, hashlib
from flask import request

def verify_cradl_webhook(signing_secret: str) -> bool:
    provided = request.headers.get('X-Cradl-Signature', '')
    method = request.method.upper()
    url = request.url  # full URL including query

    headers = [(k, v) for k, v in request.headers.items()
               if k.lower() != 'x-cradl-signature']
    headers.sort(key=lambda kv: kv[0])
    header_blob = ''.join(f'{k}:{v}' for k, v in headers)

    body = request.get_data(cache=False, as_text=False)  # raw bytes
    message = method.encode() + url.encode() + header_blob.encode() + body

    expected = hmac.new(signing_secret.encode(), message, hashlib.sha256).hexdigest()
    return hmac.compare_digest(provided, expected)

Best practices

  • Return a fast 2xx response after receiving the payload; perform longer processing asynchronously if possible
  • Use a shared secret or token in headers to authenticate requests from Cradl AI
  • Log the entire body during development to validate field mappings

Troubleshooting

  • No requests arriving: Verify the URL is publicly reachable and click Test webhook
  • 401/403 from your service: Check header names/values and tokens
  • Missing fields: Upload and validate a fresh document, then test again to refresh available data
  • Cannot download file: Include the Authorization: Bearer <ACCESS_TOKEN> header when requesting fileUrl
  • If you’re using a proxy or gateway, ensure it doesn’t modify headers or the request body