Skip to main content
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
Webhook creation form with name, URL, and event type fields
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.
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.
Use the signing secret to verify incoming requests.

Event Types

Orq.ai sends webhooks for the following events, grouped by resource:
GroupEvent
Agentagent.created
agent.updated
agent.deleted
Deploymentdeployment.created
deployment.updated
deployment.deleted
deployment.invoked
Promptprompt.created
prompt.updated
prompt.deleted

Delivery Metrics

Webhooks dashboard showing delivery metrics, event deliveries chart, response time chart, and endpoint list
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

{
  "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>"
    }
  }
}
{
  "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>"
    }
  }
}
{
  "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>"
    }
  }
}

Deployment

{
  "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>"
    }
  }
}
{
  "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>"
    }
  }
}
{
  "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>"
    }
  }
}
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.
{
  "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
  }
}
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 is linked to the request
  • billing: omitted when the response was served from cache
  • cache_status, cache_key, cache_config: present on cached responses
  • evals: present when Evaluators are configured on the deployment
  • thread: present when the invocation is part of a thread

Prompt

{
  "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>"
    }
  }
}
{
  "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>"
    }
  }
}
{
  "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>"
    }
  }
}

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 dashboard to track failures and response times.

Security

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

Headers

HeaderDescription
X-Orq-SignatureHMAC-SHA256 hex digest of the signature payload
X-Orq-Hook-IDThe event ID
X-Orq-EventThe event type
User-AgentOrq-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:
{"id":"evt_01kkyjd5d7drqxnmyzfxpracz3","created":1773773690,"type":"agent.updated"}
deployment.invoked:
{"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

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);