Webhook Connection

When an agent completes a run, Kolena will send an HTTP request of the run result to the webhook URL. The server listening for webhook deliveries at that URL can take action when it receives one.

Run Result Format

When making a request to your webhook, Kolena will send a JSON object of the following format:

  • agent_id: Agent id.
  • run_id: Agent run id.
  • run_url: Link to web UI of the run result.
  • created: Timestamp when the run result was created.
  • updated: Timestamp when the run result was last updated.
  • files: List of files of the run.
  • order: User specified order of the extractions for the agent. It is mainly used for UI display.
  • data: Extraction results of the run, a dictionary keyed by extraction name.

Here’s an example of the JSON object:

{
    "agent_id": 1,
    "run_id": 1,
    "run_url": "https://agents.kolena.com/my_org/agents/1/runs/1",
    "created": "2025-01-01T00:00:00",
    "updated": "2025-01-05T00:00:00",
    "files": [
      "demo1.pdf",
      "demo2.csv"
    ],
    "order": [
      "column_1",
      "column_2"
    ],
    "data": {
      "column_1": {
        "metadata": {
          "citations": [
            {
              "file": "demo1.pdf",
              "pages": [
                2,
                5
              ]
            }
          ],
          "confidence": 1,
          "reasoning": "Looking at file demo1.pdf, ..."
        },
        "value": "foo"
      },
      "column_2": {
        "metadata": {
          "citations": [
            {
              "file": "demo2.csv",
              "pages": []
            }
          ],
          "confidence": 0.9,
          "reasoning": "The order amount is listed in demo2.csv"
        },
        "value": 123
      }
    }
  }

Respond to Webhook Request

Your webhook handler should respond to the webhook request with an HTTP 2xx status within ten seconds. We will retry up to three times if it does not, with exponential back off. If the delivery still fails after that, we will mark the delivery as failed, and will schedule another delivery after a longer period, for up to five times. If these attempts still fail, the run result will be omitted from any further delivery.

Example Servers

Below are example servers demonstrating handling webhook deliveries. Since this is an example server, you can use any arbitrary value for the KOLENA_WEBHOOK_SECRET environment variable. When creating your own production server, you should use the secret generated by Kolena after creating the Webhook integration.

main.py
import logging
import os

from fastapi import FastAPI
from starlette.requests import Request

from restructured import webhook

# this is the secret you received when registering your webhook
secret = os.environ["KOLENA_WEBHOOK_SECRET"]

logger = logging.getLogger()
logger.setLevel(logging.INFO)

app = FastAPI()

@app.post("/")
async def root(request: Request) -> dict:
    body = await request.body()
    result = webhook.construct_event(request_body=body, secret=secret, request_headers=request.headers)
    logging.info(result.model_dump_json(indent=2))
    return dict(agent_id=result.agent_id, run_id=result.run_id)

Start the server:

export KOLENA_WEBHOOK_SECRET=<secret>
fastapi run --port <port> main.py

Testing Webhooks

The Python SDK provides a CLI to help test your webhook locally. Install Python SDK and set up an API key.

Forward events from an agent to your webhook by using listen command.

export KOLENA_WEBHOOK_SECRET=<secret>
restructured listen --agent-id <agent-id> --secret "$KOLENA_WEBHOOK_SECRET" --forward http://localhost:<port>

This will listen to new agent run results and forward them to your service set in the --forward option. You can use the secret from an existing webhook connection, if you already created one, or any arbitrary value for KOLENA_WEBHOOK_SECRET, as we are just locally testing. The secret passed to the CLI will be used by the restructured CLI to sign the request forwarded to your service. The --tail <N> option can be provided to fetch N most recent results of the agent. If --tail isn’t included, the command would only listen to new results.

You can test your agent with demo data without relying on the agent having active data using the sample command.

export KOLENA_WEBHOOK_SECRET=<secret>
restructured sample --agent-id <agent-id> --secret "$KOLENA_WEBHOOK_SECRET" --forward http://localhost:<port>

Register Webhook Connection

Once your service is ready to process Kolena webhook events, follow the steps in Connect Agent to set it as a destination for your agent.

Technical Details

Kolena Custom Headers

Kolena includes additional information in request headers:

  • X-Kolena-Timestamp: Timestamp of the request
  • X-Kolena-Delivery-ID: Unique id for the request
  • X-Kolena-Signature: HMAC value of request body, timestamp, and Delivery ID signed with webhook secret

You can use this information to verify that requests are coming from Kolena, detailed in the next section.

Signature Verification

When Kolena makes a request to your webhook URL, it will use the shared secret to create a signature from the timestamp the request is created, the request Delivery ID, and the body of the request. The signature, timestamp and Delivery ID are put in the request headers X-Kolena-Signature, X-Kolena-Timestamp, and X-Kolena-Delivery-ID, respectively. Upon receiving a request, use the secret to compute the signature from the X-Kolena-Timestamp, X-Kolena-Delivery-ID and request body, and verify the signature you computed matches the value in X-Kolena-Signature. It is also advised to verify that X-Kolena-Timestamp is within a short time window from the time the request is received.

Kolena provides a utility function webhook.construct_event in our Python SDK. It performs signature verification and returns the parsed JSON object.

from restructured import webhook

# `secret` is the value you receive when registering the webhook
result = webhook.construct_event(request_body, secret, request_headers)