> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orq.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks for real-time notifications

> Subscribe to Orq.ai events and receive real-time HTTP POST notifications when agents, deployments, prompts, and other resources change.

Webhooks let you subscribe to events in **Orq.ai** and receive an HTTP `POST` request to your server whenever those events occur, with no polling required.

## Setting Up a Webhook

Navigate to **Organization** and select **Webhooks**. Click **Create** and configure the following:

* **Name**: a unique name to identify this webhook
* **URL**: the endpoint on your server that will receive the HTTP POST requests
* **Event types**: choose which events should trigger this webhook

<div style={{ width: "70%", margin: "0 auto" }}>
  <Frame caption="Create a new webhook endpoint with a name, URL, and event types.">
    <img
      src="https://mintcdn.com/orqai/_A_PSk641PTeogiZ/images/webhook-create.png?fit=max&auto=format&n=_A_PSk641PTeogiZ&q=85&s=a6922d9ae855e03570e6f4e3cf3f983e"
      alt="Webhook creation form with name, URL, and event type
fields"
      width="687"
      height="529"
      data-path="images/webhook-create.png"
    />
  </Frame>
</div>

When **deployment** events are selected, an additional option appears:

* **Send me everything** to receive events from all deployments
* **Let me select individual deployments** to scope the webhook to specific ones.

<Warning>
  After clicking **Create**, a **Save your signing secret** dialog appears with
  your secret. Copy and store it securely, as it will not be shown again. If you
  lose it, you will need to create a new webhook.
</Warning>

Use the signing secret to [verify incoming requests](#security).

## Event Types

**Orq.ai** sends webhooks for the following events, grouped by resource:

| Group          | Event                 |
| -------------- | --------------------- |
| **Agent**      | `agent.created`       |
|                | `agent.updated`       |
|                | `agent.deleted`       |
| **Deployment** | `deployment.created`  |
|                | `deployment.updated`  |
|                | `deployment.deleted`  |
|                | `deployment.invoked`  |
| **Prompt**     | `prompt.created`      |
|                | `prompt.updated`      |
|                | `prompt.deleted`      |
| **LLM**        | `llm.chat_completion` |
|                | `llm.response`        |
|                | `llm.embedding`       |
|                | `llm.rerank`          |

## Delivery Metrics

<Frame caption="The Webhooks page showing delivery metrics and endpoint list.">
  <img
    src="https://mintcdn.com/orqai/_A_PSk641PTeogiZ/images/webhooks-dashboard.png?fit=max&auto=format&n=_A_PSk641PTeogiZ&q=85&s=415876b4682d1752bf34371c173475e5"
    alt="Webhooks dashboard showing delivery metrics, event deliveries chart,
response time chart, and endpoint list"
    width="2464"
    height="1574"
    data-path="images/webhooks-dashboard.png"
  />
</Frame>

The **Webhooks** page shows a **Delivery Metrics** dashboard with:

* **Total Deliveries**, **Successful**, **Failed**, and **Avg Duration** over a configurable time range
* An **Event deliveries** chart showing successful vs. failed deliveries over time
* A **Response time** chart showing min, avg, and max response times

Each webhook endpoint is listed below with its URL, configured event types, and signing secret.

## Payload Structure

All CRUD events share the same envelope. `request_id` is an optional string that identifies the originating API request. It is present when the event was triggered by a traceable API call, and absent for system-initiated events.

### Agent

<AccordionGroup>
  <Accordion title="agent.created" icon="plus">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "evt_<ulid>",
      "object": "event",
      "type": "agent.created",
      "created": 1742123456,
      "api_version": "1.0",
      "workspace": "<workspace_id>",
      "project": "<project_id>",
      "correlation_id": "<correlation_id>",
      "data": {
        "object": {
          "id": "<agent_id>",
          "key": "<agent_name>",
          "version": "<version>"
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="agent.updated" icon="pen">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "evt_<ulid>",
      "object": "event",
      "type": "agent.updated",
      "created": 1742123456,
      "api_version": "1.0",
      "workspace": "<workspace_id>",
      "project": "<project_id>",
      "correlation_id": "<correlation_id>",
      "data": {
        "object": {
          "id": "<agent_id>",
          "key": "<agent_name>",
          "version": "<version>"
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="agent.deleted" icon="trash">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "evt_<ulid>",
      "object": "event",
      "type": "agent.deleted",
      "created": 1742123456,
      "api_version": "1.0",
      "workspace": "<workspace_id>",
      "project": "<project_id>",
      "correlation_id": "<correlation_id>",
      "data": {
        "object": {
          "id": "<agent_id>",
          "key": "<agent_name>",
          "version": "<version>"
        }
      }
    }
    ```
  </Accordion>
</AccordionGroup>

### Deployment

<AccordionGroup>
  <Accordion title="deployment.created" icon="plus">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "evt_<ulid>",
      "object": "event",
      "type": "deployment.created",
      "created": 1742123456,
      "api_version": "1.0",
      "workspace": "<workspace_id>",
      "project": "<project_id>",
      "correlation_id": "<correlation_id>",
      "data": {
        "object": {
          "id": "<deployment_id>",
          "key": "<deployment_name>",
          "version": "<version>"
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="deployment.updated" icon="pen">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "evt_<ulid>",
      "object": "event",
      "type": "deployment.updated",
      "created": 1742123456,
      "api_version": "1.0",
      "workspace": "<workspace_id>",
      "project": "<project_id>",
      "correlation_id": "<correlation_id>",
      "data": {
        "object": {
          "id": "<deployment_id>",
          "key": "<deployment_name>",
          "version": "<version>"
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="deployment.deleted" icon="trash">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "evt_<ulid>",
      "object": "event",
      "type": "deployment.deleted",
      "created": 1742123456,
      "api_version": "1.0",
      "workspace": "<workspace_id>",
      "project": "<project_id>",
      "correlation_id": "<correlation_id>",
      "data": {
        "object": {
          "id": "<deployment_id>",
          "key": "<deployment_name>",
          "version": "<version>"
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="deployment.invoked" icon="bolt">
    <Info>
      Unlike CRUD events, `deployment.invoked` does not include `workspace`, `project`, `api_version`, or `correlation_id`. Deployment metadata is provided in a top-level `metadata` block instead of a nested `object`.
    </Info>

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "wk_log_01kkypj82sqrzg1s9t9v9p13jv",
      "created": "2026-03-17T20:07:31.161517094Z",
      "type": "deployment.invoked",
      "metadata": {
        "deployment_id": "<deployment_id>",
        "deployment_key": "<deployment_name>",
        "deployment_variant_id": "<variant_id>",
        "deployment_variant_version": "<variant_version>",
        "deployment_log_id": "<log_id>",
        "deployment_url": "<deployment_url>",
        "deployment_variant_url": "<variant_url>",
        "deployment_log_url": "<log_url>"
      },
      "data": {
        "contact_id": "<identity_ulid>",
        "prompt_config": {
          "messages": [
            { "role": "system", "content": "<system_message>" },
            { "role": "user", "content": "<user_message>", "id": "<message_id>" }
          ],
          "model": "gpt-4o-mini",
          "model_db_id": "<model_db_id>",
          "model_parameters": {
            "temperature": 0.2,
            "maxTokens": 1000,
            "topK": 5,
            "topP": 0,
            "frequencyPenalty": 0,
            "presencePenalty": 0
          },
          "model_type": "chat",
          "provider": "openai",
          "stream": false
        },
        "choices": [
          {
            "finish_reason": "stop",
            "index": 0,
            "message": { "role": "assistant", "content": "<response>" }
          }
        ],
        "variables": [
          { "is_pii": false, "key": "<variable_key>", "value": "<variable_value>" }
        ],
        "performance": {
          "first_time_to_token": 0,
          "latency": 1125.2,
          "tokens_per_second": 8.89
        },
        "usage": {
          "completion_tokens": 10,
          "completion_tokens_details": { "reasoning_tokens": 0 },
          "prompt_tokens": 122,
          "prompt_tokens_details": { "cached_tokens": 0 },
          "total_tokens": 132
        },
        "billing": {
          "billable": true,
          "input_cost": 0.0000183,
          "output_cost": 0.000006,
          "total_cost": 0.0000243
        },
        "tools": [],
        "metadata": {},
        "thread": {
          "id": "<thread_id>",
          "tags": ["<tag1>", "<tag2>"]
        },
        "status": 200
      }
    }
    ```

    <Info>
      `status` is always present. It contains the HTTP status code returned by the upstream model provider (e.g. `200`, `429`, `500`), useful for detecting partial failures without inspecting the full response.

      Several other fields are conditional and may be absent depending on the invocation:

      * `contact_id`: present when an [Identity](/docs/analytics/identity) is linked to the request
      * `billing`: omitted when the response was served from [cache](/docs/proxy/cache)
      * `cache_status`, `cache_key`, `cache_config`: present on cached responses
      * `evals`: present when [Evaluators](/docs/evaluators/overview) are configured on the deployment
      * `thread`: present when the invocation is part of a [thread](/docs/proxy/thread-management)
    </Info>
  </Accordion>
</AccordionGroup>

### Prompt

<AccordionGroup>
  <Accordion title="prompt.created" icon="plus">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "evt_<ulid>",
      "object": "event",
      "type": "prompt.created",
      "created": 1742123456,
      "api_version": "1.0",
      "workspace": "<workspace_id>",
      "project": "<project_id>",
      "correlation_id": "<correlation_id>",
      "data": {
        "object": {
          "id": "<prompt_id>",
          "key": "<prompt_name>",
          "version": "<version>"
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="prompt.updated" icon="pen">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "evt_<ulid>",
      "object": "event",
      "type": "prompt.updated",
      "created": 1742123456,
      "api_version": "1.0",
      "workspace": "<workspace_id>",
      "project": "<project_id>",
      "correlation_id": "<correlation_id>",
      "data": {
        "object": {
          "id": "<prompt_id>",
          "key": "<prompt_name>",
          "version": "<version>"
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="prompt.deleted" icon="trash">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "evt_<ulid>",
      "object": "event",
      "type": "prompt.deleted",
      "created": 1742123456,
      "api_version": "1.0",
      "workspace": "<workspace_id>",
      "project": "<project_id>",
      "correlation_id": "<correlation_id>",
      "data": {
        "object": {
          "id": "<prompt_id>",
          "key": "<prompt_name>",
          "version": "<version>"
        }
      }
    }
    ```
  </Accordion>
</AccordionGroup>

### LLM

LLM events fire after the gateway processes a chat completion, response, embedding, or rerank call. The `data` block carries the OpenTelemetry GenAI semantic-convention span attributes so subscribers receive the full request and response payload inline.

The wire shape of `gen_ai.input` and `gen_ai.output` depends on which producer emitted the span:

* For Responses API spans (`llm.response`), the canonical input/output is on `openresponses.input` / `openresponses.output` and is delivered as a JSON-encoded string.
* For chat completion / embedding / rerank spans, the canonical fields follow OTEL GenAI semconv: `gen_ai.input.messages` / `gen_ai.output.messages` (chat), `gen_ai.rerank.query` / `gen_ai.rerank.results` (rerank).
* The envelope's top-level `project` field is omitted when the span has no associated project.

<AccordionGroup>
  <Accordion title="llm.chat_completion" icon="message">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "evt_a5a771765f4959ddf615093e39",
      "object": "event",
      "type": "llm.chat_completion",
      "created": 1778437602,
      "api_version": "1.0",
      "workspace": "391ff8f8-95cd-49bd-9e28-eddba2d570b8",
      "correlation_id": "e98f5b18ddb209743950528b1f3a41a0",
      "data": {
        "span_id": "f3313eaecc8b24bc",
        "trace_id": "e98f5b18ddb209743950528b1f3a41a0",
        "parent_span_id": "f99eb9200abf32e6",
        "span_type": "span.chat_completion",
        "name": "chat gpt-4o-mini",
        "start_time": "2026-05-10T18:26:41.325945Z",
        "end_time": "2026-05-10T18:26:41.327398792Z",
        "duration_ns": 1453792,
        "status": "OK",
        "attributes": {
          "gen_ai.input.messages": "[{\"role\":\"user\",\"parts\":[{\"_id\":\"text_01kr9j8e1dm0t92b2y2fxttc8w\",\"kind\":\"text\",\"text\":\"Hello\"}]}]",
          "gen_ai.operation.name": "chat",
          "gen_ai.output.messages": "[{\"role\":\"agent\",\"parts\":[{\"_id\":\"text_01kr9j8e1ft8xm3sstfh2wzbsm\",\"kind\":\"text\",\"text\":\"Hello! How can I assist you today?\"}]}]",
          "gen_ai.provider.name": "openai",
          "gen_ai.request.model": "gpt-4o-mini",
          "gen_ai.response.finish_reasons": ["stop"],
          "gen_ai.response.model": "gpt-4o-mini",
          "gen_ai.system_instructions": "{\"role\":\"user\",\"parts\":[{\"_id\":\"text_01kr9j8e1dm0t92b2y2fxttc8w\",\"kind\":\"text\",\"text\":\"Hello\"}]}",
          "gen_ai.usage.input_tokens": 8,
          "gen_ai.usage.output_tokens": 9,
          "gen_ai.usage.total_tokens": 17,
          "orq.billing.billable": false,
          "orq.billing.cache_read_cost": 0,
          "orq.billing.cache_write_cost": 0,
          "orq.billing.input_cost": 0.0000012,
          "orq.billing.integration_id": "01KQ9Y9MS8ZHV99ZG60Q32C36E",
          "orq.billing.output_cost": 0.0000054,
          "orq.billing.pricing_tier": "standard",
          "orq.billing.total_cost": 0.0000066,
          "orq.model.id": "85b18e5b-76a0-48dc-99b5-5738c35c9c85",
          "orq.model.is_private": false,
          "orq.model.provider": "openai",
          "orq.model.type": "chat",
          "orq.organization_id": "3d4c4097-3ad7-48f8-965e-71d8d25ddb1b",
          "orq.product": "router",
          "orq.span_type": "span.chat_completion",
          "orq.workspace_id": "391ff8f8-95cd-49bd-9e28-eddba2d570b8",
          "service.instance.id": "01KR9HSA5JCBQRN8ZFN9KJZ23G",
          "service.name": "responses-api",
          "service.version": "0.1.0",
          "telemetry.sdk.language": "go",
          "telemetry.sdk.name": "opentelemetry",
          "telemetry.sdk.version": "1.43.0"
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="llm.response" icon="reply">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "evt_23c9e52b6b1a38d64b9d034791",
      "object": "event",
      "type": "llm.response",
      "created": 1778437600,
      "api_version": "1.0",
      "workspace": "391ff8f8-95cd-49bd-9e28-eddba2d570b8",
      "project": "<project_ulid>",
      "correlation_id": "b12335e85a37ed3ce191a5268fa87111",
      "data": {
        "span_id": "6b2508f591d1b112",
        "trace_id": "b12335e85a37ed3ce191a5268fa87111",
        "parent_span_id": "f7fdfb8e41c2d9e1",
        "span_type": "span.responses",
        "name": "chat gpt-4o-mini",
        "start_time": "2026-05-10T18:26:39.414839Z",
        "end_time": "2026-05-10T18:26:39.422535042Z",
        "duration_ns": 7696042,
        "status": "OK",
        "attributes": {
          "gen_ai.operation.name": "responses",
          "gen_ai.provider.name": "openai",
          "gen_ai.request.model": "gpt-4o-mini",
          "gen_ai.response.finish_reasons": ["stop"],
          "gen_ai.response.id": "resp_0608254bd32f043b016a00b1642dfc81a4a1f3c41bec5cf70a",
          "gen_ai.response.model": "gpt-4o-mini-2024-07-18",
          "gen_ai.usage.input_tokens": 15,
          "gen_ai.usage.output_tokens": 15,
          "gen_ai.usage.total_tokens": 30,
          "openresponses.background": false,
          "openresponses.completed_at": "1778430310",
          "openresponses.created_at": 1778430308,
          "openresponses.frequency_penalty": 0,
          "openresponses.input": "[{\"content\":[{\"type\":\"input_text\",\"text\":\"Count from 1 to 5.\"}],\"id\":\"msg_01kr9j8c5p9v4y4xyhdqhg117n\",\"role\":\"user\",\"status\":\"completed\",\"type\":\"message\"}]",
          "openresponses.input.items.count": 1,
          "openresponses.object": "response",
          "openresponses.output": "[{\"content\":[{\"type\":\"output_text\",\"text\":\"1, 2, 3, 4, 5.\",\"annotations\":[],\"logprobs\":[]}],\"id\":\"msg_0608254bd32f043b016a00b1668cb881a480fe34962427a227\",\"role\":\"assistant\",\"status\":\"completed\",\"type\":\"message\"}]",
          "openresponses.output.items.count": 1,
          "openresponses.parallel_tool_calls": true,
          "openresponses.presence_penalty": 0,
          "openresponses.service_tier": "default",
          "openresponses.status": "completed",
          "openresponses.store": true,
          "openresponses.stream": false,
          "openresponses.temperature": 1,
          "openresponses.text": "{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"}",
          "openresponses.tool_choice": "\"auto\"",
          "openresponses.tools.count": 0,
          "openresponses.top_logprobs": 0,
          "openresponses.top_p": 1,
          "openresponses.truncation": "auto",
          "orq.billing.billable": false,
          "orq.billing.cache_read_cost": 0,
          "orq.billing.cache_write_cost": 0,
          "orq.billing.input_cost": 0.00000225,
          "orq.billing.integration_id": "01KQ9Y9MS8ZHV99ZG60Q32C36E",
          "orq.billing.output_cost": 0.000009,
          "orq.billing.pricing_tier": "standard",
          "orq.billing.total_cost": 0.00001125,
          "orq.model.id": "85b18e5b-76a0-48dc-99b5-5738c35c9c85",
          "orq.model.is_private": false,
          "orq.model.provider": "openai",
          "orq.model.type": "chat",
          "orq.organization_id": "3d4c4097-3ad7-48f8-965e-71d8d25ddb1b",
          "orq.product": "router",
          "orq.span_type": "span.responses",
          "orq.workspace_id": "391ff8f8-95cd-49bd-9e28-eddba2d570b8",
          "service.instance.id": "01KR9HSA5JCBQRN8ZFN9KJZ23G",
          "service.name": "responses-api",
          "service.version": "0.1.0",
          "telemetry.sdk.language": "go",
          "telemetry.sdk.name": "opentelemetry",
          "telemetry.sdk.version": "1.43.0"
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="llm.embedding" icon="vector-square">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "evt_48eeb9fad80359ed8a52342235",
      "object": "event",
      "type": "llm.embedding",
      "created": 1778437604,
      "api_version": "1.0",
      "workspace": "391ff8f8-95cd-49bd-9e28-eddba2d570b8",
      "correlation_id": "8d8bad94ac9a97d14fe783daf34cb3e5",
      "data": {
        "span_id": "ac6e9de774652b62",
        "trace_id": "8d8bad94ac9a97d14fe783daf34cb3e5",
        "parent_span_id": "8ac42a0d3556cd44",
        "span_type": "span.embedding",
        "name": "embeddings text-embedding-3-small",
        "start_time": "2026-05-10T18:26:43.638991Z",
        "end_time": "2026-05-10T18:26:43.6414215Z",
        "duration_ns": 2430500,
        "status": "OK",
        "attributes": {
          "gen_ai.input.messages": "[{\"role\":\"user\",\"parts\":[{\"type\":\"text\",\"content\":\"Hello\"}]}]",
          "gen_ai.operation.name": "embeddings",
          "gen_ai.output.count": 1,
          "gen_ai.output.dimensions": 1536,
          "gen_ai.output.object": "list",
          "gen_ai.provider.name": "openai",
          "gen_ai.request.model": "text-embedding-3-small",
          "gen_ai.response.model": "text-embedding-3-small",
          "gen_ai.usage.input_tokens": 1,
          "gen_ai.usage.total_tokens": 1,
          "http.response.status_code": 200,
          "orq.billing.billable": false,
          "orq.billing.input_cost": 2e-8,
          "orq.billing.integration_id": "01KQ9Y9MS8ZHV99ZG60Q32C36E",
          "orq.billing.pricing_tier": "standard",
          "orq.billing.total_cost": 2e-8,
          "orq.model.id": "679a4486-1936-4287-bcdd-f6dc0df8389c",
          "orq.model.is_private": false,
          "orq.model.provider": "openai",
          "orq.model.type": "embedding",
          "orq.organization_id": "3d4c4097-3ad7-48f8-965e-71d8d25ddb1b",
          "orq.product": "router",
          "orq.span_type": "span.embedding",
          "orq.workspace_id": "391ff8f8-95cd-49bd-9e28-eddba2d570b8",
          "service.instance.id": "01KR9HSA5JCBQRN8ZFN9KJZ23G",
          "service.name": "responses-api",
          "service.version": "0.1.0",
          "telemetry.sdk.language": "go",
          "telemetry.sdk.name": "opentelemetry",
          "telemetry.sdk.version": "1.43.0"
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="llm.rerank" icon="arrow-down-wide-short">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "evt_194c87ee3cee419a20ccd730a7",
      "object": "event",
      "type": "llm.rerank",
      "created": 1778437606,
      "api_version": "1.0",
      "workspace": "391ff8f8-95cd-49bd-9e28-eddba2d570b8",
      "correlation_id": "1c1aa12dad4675fbdf673dfcd41c53d1",
      "data": {
        "span_id": "919ab2e33d8e838e",
        "trace_id": "1c1aa12dad4675fbdf673dfcd41c53d1",
        "parent_span_id": "c40e7f287a267c29",
        "span_type": "span.rerank",
        "name": "rerank jina-reranker-v2-base-multilingual",
        "start_time": "2026-05-10T18:26:45.708812Z",
        "end_time": "2026-05-10T18:26:45.710460167Z",
        "duration_ns": 1648167,
        "status": "OK",
        "attributes": {
          "gen_ai.operation.name": "rerank",
          "gen_ai.output.count": 3,
          "gen_ai.provider.name": "jina",
          "gen_ai.request.model": "jina-reranker-v2-base-multilingual",
          "gen_ai.request.top_n": 3,
          "gen_ai.rerank.documents": "[\"Organic skincare for sensitive skin with aloe vera and chamomile: Imagine the soothing embrace of nature with our organic skincare range, crafted specifically for sensitive skin. Infused with the calming properties of aloe vera and chamomile, each product provides gentle nourishment and protection. Say goodbye to irritation and hello to a glowing, healthy complexion.\",\"New makeup trends focus on bold colors and innovative techniques: Step into the world of cutting-edge beauty with this seasons makeup trends. Bold, vibrant colors and groundbreaking techniques are redefining the art of makeup. From neon eyeliners to holographic highlighters, unleash your creativity and make a statement with every look.\",\"Bio-Hautpflege für empfindliche Haut mit Aloe Vera und Kamille: Erleben Sie die wohltuende Wirkung unserer Bio-Hautpflege, speziell für empfindliche Haut entwickelt. Mit den beruhigenden Eigenschaften von Aloe Vera und Kamille pflegen und schützen unsere Produkte Ihre Haut auf natürliche Weise. Verabschieden Sie sich von Hautirritationen und genießen Sie einen strahlenden Teint.\",\"Neue Make-up-Trends setzen auf kräftige Farben und innovative Techniken: Tauchen Sie ein in die Welt der modernen Schönheit mit den neuesten Make-up-Trends. Kräftige, lebendige Farben und innovative Techniken setzen neue Maßstäbe. Von auffälligen Eyelinern bis hin zu holografischen Highlightern – lassen Sie Ihrer Kreativität freien Lauf und setzen Sie jedes Mal ein Statement.\",\"Cuidado de la piel orgánico para piel sensible con aloe vera y manzanilla: Descubre el poder de la naturaleza con nuestra línea de cuidado de la piel orgánico, diseñada especialmente para pieles sensibles. Enriquecidos con aloe vera y manzanilla, estos productos ofrecen una hidratación y protección suave. Despídete de las irritaciones y saluda a una piel radiante y saludable.\",\"Las nuevas tendencias de maquillaje se centran en colores vivos y técnicas innovadoras: Entra en el fascinante mundo del maquillaje con las tendencias más actuales. Colores vivos y técnicas innovadoras están revolucionando el arte del maquillaje. Desde delineadores neón hasta iluminadores holográficos, desata tu creatividad y destaca en cada look.\",\"针对敏感肌专门设计的天然有机护肤产品：体验由芦荟和洋甘菊提取物带来的自然呵护。我们的护肤产品特别为敏感肌设计，温和滋润，保护您的肌肤不受刺激。让您的肌肤告别不适，迎来健康光彩。\",\"新的化妆趋势注重鲜艳的颜色和创新的技巧：进入化妆艺术的新纪元，本季的化妆趋势以大胆的颜色和创新的技巧为主。无论是霓虹眼线还是全息高光，每一款妆容都能让您脱颖而出，展现独特魅力。\",\"敏感肌のために特別に設計された天然有機スキンケア製品: アロエベラとカモミールのやさしい力で、自然の抱擁を感じてください。敏感肌用に特別に設計された私たちのスキンケア製品は、肌に優しく栄養を与え、保護します。肌トラブルにさようなら、輝く健康な肌にこんにちは。\",\"新しいメイクのトレンドは鮮やかな色と革新的な技術に焦点を当てています: 今シーズンのメイクアップトレンドは、大胆な色彩と革新的な技術に注目しています。ネオンアイライナーからホログラフィックハイライターまで、クリエイティビティを解き放ち、毎回ユニークなルックを演出しましょう。\"]",
          "gen_ai.rerank.documents.count": 10,
          "gen_ai.rerank.query": "Organic skincare products for sensitive skin",
          "gen_ai.rerank.results": "[{\"_id\":\"rerank_01kr9j8jae2zxygmftpz9880wc\",\"kind\":\"rerank\",\"index\":0,\"relevance_score\":0.87831426,\"document\":\"Organic skincare for sensitive skin with aloe vera and chamomile: Imagine the soothing embrace of nature with our organic skincare range, crafted specifically for sensitive skin. Infused with the calming properties of aloe vera and chamomile, each product provides gentle nourishment and protection. Say goodbye to irritation and hello to a glowing, healthy complexion.\"},{\"_id\":\"rerank_01kr9j8jae611fg8mqn8dm08y5\",\"kind\":\"rerank\",\"index\":6,\"relevance_score\":0.87747681,\"document\":\"针对敏感肌专门设计的天然有机护肤产品：体验由芦荟和洋甘菊提取物带来的自然呵护。我们的护肤产品特别为敏感肌设计，温和滋润，保护您的肌肤不受刺激。让您的肌肤告别不适，迎来健康光彩。\"},{\"_id\":\"rerank_01kr9j8jaetc7rzkrqkjse3pbh\",\"kind\":\"rerank\",\"index\":4,\"relevance_score\":0.86246759,\"document\":\"Cuidado de la piel orgánico para piel sensible con aloe vera y manzanilla: Descubre el poder de la naturaleza con nuestra línea de cuidado de la piel orgánico, diseñada especialmente para pieles sensibles. Enriquecidos con aloe vera y manzanilla, estos productos ofrecen una hidratación y protección suave. Despídete de las irritaciones y saluda a una piel radiante y saludable.\"}]",
          "gen_ai.response.model": "jina-reranker-v2-base-multilingual",
          "gen_ai.usage.input_tokens": 896,
          "gen_ai.usage.total_tokens": 896,
          "orq.billing.billable": true,
          "orq.billing.input_cost": 0.00001613,
          "orq.billing.pricing_tier": "standard",
          "orq.billing.total_cost": 0.00001613,
          "orq.model.id": "4666d24e-ae9c-11ef-9414-325096b39f47",
          "orq.model.is_private": false,
          "orq.model.provider": "jina",
          "orq.model.type": "rerank",
          "orq.organization_id": "3d4c4097-3ad7-48f8-965e-71d8d25ddb1b",
          "orq.product": "router",
          "orq.span_type": "span.rerank",
          "orq.workspace_id": "391ff8f8-95cd-49bd-9e28-eddba2d570b8",
          "service.instance.id": "01KR9HSA5JCBQRN8ZFN9KJZ23G",
          "service.name": "responses-api",
          "service.version": "0.1.0",
          "telemetry.sdk.language": "go",
          "telemetry.sdk.name": "opentelemetry",
          "telemetry.sdk.version": "1.43.0"
        }
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## Best Practices

* **Use HTTPS**: always use HTTPS for your webhook URL to ensure payloads are transmitted securely.
* **Verify signatures**: validate the `X-Orq-Signature` header on every request before processing the payload.
* **Respond quickly**: acknowledge receipt immediately with a `200` response. Offload any heavy processing to a background job.
* **Handle retries**: if your endpoint is temporarily unavailable, **Orq.ai** may retry delivery. Make your handler idempotent using the event `id` to avoid processing the same event twice.
* **Monitor delivery**: use the [Delivery Metrics](#delivery-metrics) dashboard to track failures and response times.

## Security

Every webhook request includes a signature you should verify before processing the payload.

### Headers

| Header            | Description                                     |
| ----------------- | ----------------------------------------------- |
| `X-Orq-Signature` | HMAC-SHA256 hex digest of the signature payload |
| `X-Orq-Hook-ID`   | The event ID                                    |
| `X-Orq-Event`     | The event type                                  |
| `User-Agent`      | `Orq-Webhook/2.0`                               |

### How the signature is computed

The signature is computed over a JSON string containing exactly these three fields in this order. The type of `created` differs by event: an integer (Unix timestamp) for CRUD events, and an ISO-8601 string for `deployment.invoked`. The value must be preserved as-is from the original payload without type conversion.

CRUD events:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "evt_01kkyjd5d7drqxnmyzfxpracz3",
  "created": 1773773690,
  "type": "agent.updated"
}
```

`deployment.invoked`:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "wk_log_01kkyhyxwc6413gh8xfbqydge2",
  "created": "2026-03-17T18:47:03.820244586Z",
  "type": "deployment.invoked"
}
```

This is signed with your webhook secret using HMAC-SHA256 and hex-encoded. Always use constant-time string comparison to prevent timing attacks.

### Verification examples

<CodeGroup>
  ```typescript Node.js theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import express, { Request, Response } from "express";
  import crypto from "crypto";

  const app = express();
  const webhookSecret = "orq_wk_...";

  function verifySignature(
    payload: string,
    signature: string,
    secret: string,
  ): boolean {
    const expected = crypto
      .createHmac("sha256", secret)
      .update(payload)
      .digest("hex");
    const a = Buffer.from(signature);
    const b = Buffer.from(expected);
    // timingSafeEqual throws if buffers differ in length; check first
    return a.length === b.length && crypto.timingSafeEqual(a, b);
  }

  app.post("/webhooks", express.json(), (req: Request, res: Response) => {
    const signature = req.headers["x-orq-signature"] as string;
    const { id, created, type } = req.body;
    const signaturePayload = JSON.stringify({ id, created, type });

    if (!verifySignature(signaturePayload, signature, webhookSecret)) {
      return res.status(400).send("Invalid signature");
    }

    res.json({ received: true });
  });

  app.listen(3000);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import hmac
  import hashlib
  import json
  from fastapi import Request, FastAPI, HTTPException

  webhook_secret = "orq_wk_..."
  app = FastAPI()

  def verify_signature(payload: str, signature: str, secret: str) -> bool:
      expected = hmac.new(
          secret.encode("utf-8"),
          payload.encode("utf-8"),
          hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(signature, expected)

  @app.post("/webhooks")
  async def handle_webhook(request: Request):
      body = await request.json()
      signature = request.headers.get("x-orq-signature", "")
      signature_payload = json.dumps(
          {"id": body["id"], "created": body["created"], "type": body["type"]},
          separators=(",", ":")
      )
      if not verify_signature(signature_payload, signature, webhook_secret):
          raise HTTPException(status_code=400, detail="Invalid signature")
      return {"received": True}
  ```

  ```go Go theme={"theme":{"light":"github-light","dark":"github-dark"}}
  package main

  import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "io"
    "net/http"
  )

  var webhookSecret = "orq_wk_..."

  type signaturePayload struct {
    ID      string          `json:"id"`
    Created json.RawMessage `json:"created"`
    Type    string          `json:"type"`
  }

  func verifySignature(payload, signature, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(payload))
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(signature))
  }

  func webhookHandler(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    defer r.Body.Close()

    var event map[string]json.RawMessage
    json.Unmarshal(body, &event)

    sig := r.Header.Get("X-Orq-Signature")

    var id, eventType string
    json.Unmarshal(event["id"], &id)
    json.Unmarshal(event["type"], &eventType)

    sp := signaturePayload{
      ID:      id,
      Created: event["created"],
      Type:    eventType,
    }
    payloadBytes, _ := json.Marshal(sp)

    if !verifySignature(string(payloadBytes), sig, webhookSecret) {
      http.Error(w, "Invalid signature", http.StatusBadRequest)
      return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write([]byte(`{"received": true}`))
  }

  func main() {
    http.HandleFunc("/webhooks", webhookHandler)
    http.ListenAndServe(":3000", nil)
  }
  ```

  ```php PHP theme={"theme":{"light":"github-light","dark":"github-dark"}}
  <?php

  // Requires 64-bit PHP: json_decode parses large integers as floats on 32-bit builds,
  // which would corrupt the created field and produce an invalid signature.
  $webhookSecret = 'orq_wk_...';
  $body = file_get_contents('php://input');
  $event = json_decode($body, true);
  $signature = $_SERVER['HTTP_X_ORQ_SIGNATURE'] ?? '';

  $signaturePayload = json_encode([
      'id' => $event['id'],
      'created' => $event['created'],
      'type' => $event['type'],
  ], JSON_UNESCAPED_SLASHES);

  $expected = hash_hmac('sha256', $signaturePayload, $webhookSecret);

  if (!hash_equals($expected, $signature)) {
      http_response_code(400);
      echo 'Invalid signature';
      exit;
  }

  header('Content-Type: application/json');
  echo json_encode(['received' => true]);
  ```

  ```ruby Ruby theme={"theme":{"light":"github-light","dark":"github-dark"}}
  require 'sinatra'
  require 'json'
  require 'openssl'

  WEBHOOK_SECRET = 'orq_wk_...'

  def verify_signature(payload, signature, secret)
    expected = OpenSSL::HMAC.hexdigest('SHA256', secret, payload)
    Rack::Utils.secure_compare(expected, signature)
  end

  post '/webhooks' do
    body = JSON.parse(request.body.read)
    signature = request.env['HTTP_X_ORQ_SIGNATURE'] || ''

    signature_payload = JSON.generate({
      id: body['id'],
      created: body['created'],
      type: body['type']
    })

    halt 400, 'Invalid signature' unless verify_signature(signature_payload, signature, WEBHOOK_SECRET)

    content_type :json
    { received: true }.to_json
  end
  ```

  ```csharp C# theme={"theme":{"light":"github-light","dark":"github-dark"}}
  using System;
  using System.Security.Cryptography;
  using System.Text;
  using System.Text.Json;

  var builder = WebApplication.CreateBuilder(args);
  var app = builder.Build();
  var webhookSecret = "orq_wk_...";

  static bool VerifySignature(string payload, string signature, string secret)
  {
      using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
      var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
      var expected = Convert.ToHexStringLower(hash);
      return CryptographicOperations.FixedTimeEquals(
          Encoding.UTF8.GetBytes(expected),
          Encoding.UTF8.GetBytes(signature)
      );
  }

  app.MapPost("/webhooks", async (HttpContext context) =>
  {
      using var reader = new StreamReader(context.Request.Body);
      var body = await reader.ReadToEndAsync();
      var eventData = JsonSerializer.Deserialize<JsonElement>(body);
      var signature = context.Request.Headers["X-Orq-Signature"].ToString();

      // created is an integer for CRUD events and a string for deployment.invoked
      // serialize the raw JsonElement to preserve the original type
      var signaturePayload = JsonSerializer.Serialize(new
      {
          id = eventData.GetProperty("id").GetString(),
          created = eventData.GetProperty("created"),
          type = eventData.GetProperty("type").GetString()
      });

      if (!VerifySignature(signaturePayload, signature, webhookSecret))
          return Results.BadRequest("Invalid signature");

      return Results.Json(new { received = true });
  });

  app.Run("http://localhost:3000");
  ```
</CodeGroup>
