Outbound Webhooks
Outbound webhooks send HTTP POST notifications for legacy API tasks (source = api) and Agent API Sessions (source = agent_api). Web UI tasks and other task sources are not delivered through this public webhook path.
Events
| Event | Fires when |
|---|---|
task.created | A new task is created |
task.running | The agent starts executing or persists an execution block |
task.completed | Task finishes successfully |
task.failed | Task fails |
task.canceled | Task is canceled by user |
Create Webhook
POST /v1/webhooks
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Webhook endpoint URL (HTTPS recommended) |
events | string[] | Yes | Events to subscribe to |
description | string | No | Human-readable label (max 500 chars) |
secret | string | No | Pre-shared secret (max 500 chars). When set, included as X-Webhook-Secret header in every delivery. |
# Webhook without secret
curl -X POST https://api.rebyte.ai/v1/webhooks \
-H "API_KEY: rbk_xxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhook",
"events": ["task.completed", "task.failed"]
}'
# Webhook with pre-shared secret
curl -X POST https://api.rebyte.ai/v1/webhooks \
-H "API_KEY: rbk_xxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhook",
"events": ["task.completed", "task.failed"],
"secret": "my-shared-secret-value"
}'
Response (201):
{
"id": "880e8400-...",
"url": "https://your-server.com/webhook",
"events": ["task.completed", "task.failed"],
"description": null,
"hasSecret": true,
"isActive": true,
"createdAt": "2026-01-28T12:00:00.000Z"
}
Behavior notes:
- Duplicate URLs: registering the same URL twice returns the existing webhook (idempotent)
- Limit: maximum 3 webhooks per organization. The 4th returns
limit_exceeded. - Secret storage: the secret value is never returned in any response. Only
hasSecret: true/falseis exposed.
List Webhooks
GET /v1/webhooks
Returns all webhooks for your organization with status info (lastTriggeredAt, failureCount, isActive). Secrets are never exposed.
Get Webhook
GET /v1/webhooks/:id
Delete Webhook
DELETE /v1/webhooks/:id
Returns 204 No Content. Deleted webhooks immediately stop receiving deliveries.
Delivery
When a task event matches a webhook's subscribed events, Rebyte sends an HTTP POST to the webhook URL.
Payload:
{
"event": "task.completed",
"taskId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": 1706443200,
"data": {
"status": "completed",
"taskUrl": "https://app.rebyte.ai/run/550e8400-...",
"result": "Created CSV file with 10 Hacker News posts..."
}
}
status— lifecycle status:created,running,completed,failed, orcanceled, matching the event nametaskUrl— direct link to the task in the Rebyte UI on lifecycle payloadsresult— final AI output text when one exists on a terminal event; absent from lifecycletask.createdandtask.runningpayloadserrorMessage— failure detail when one is available
The task.running event name is also used as the envelope for persisted execution blocks. Those deliveries have data.type: "execution_event", an eventType such as thinking, text, tool_use, or tool_result, and a structured payload instead of a lifecycle status.
For a legacy API task, call GET /v1/tasks/:id for current state. For an Agent API Session, the payload taskId is the underlying public Session ID; reconcile through GET /v1/sessions/:id and its Message transcript endpoints.
Delivery headers:
| Header | Always present | Description |
|---|---|---|
Content-Type | Yes | application/json |
X-Webhook-Signature | Yes | Base64-encoded RSA-SHA256 signature |
X-Webhook-Timestamp | Yes | Unix timestamp (seconds) |
X-Webhook-Secret | Only if secret configured | The pre-shared secret value you set on creation |
Timeout: deliveries time out after 10 seconds.
Failure handling:
- Each matching event receives one delivery attempt with no automatic retry
- A timeout, network error, or non-2xx response may be dropped permanently; there is no delivery queue or durable delivery ledger
- Delivery is fire-and-forget and never blocks or fails Agent execution
- Non-2xx responses increment
failureCount - After 10 consecutive failures, the webhook is automatically disabled (
isActive: false) - Successful deliveries reset
failureCountto 0
Signature Verification
Every delivery is signed with your organization's RSA-2048 key pair, regardless of whether a pre-shared secret is configured.
How it works:
- Rebyte concatenates
{timestamp}.{body}(e.g.,1706443200.{"event":"task.completed",...}) - Signs the string with RSA-SHA256 using your org's private key
- Sends the base64-encoded signature in
X-Webhook-Signature
Get your public key: retrieve it from the API Playground.
Verification example (Node.js):
const crypto = require('crypto');
function verifyWebhook(rawBody, timestamp, signature, publicKey) {
const payload = `${timestamp}.${rawBody}`;
const verifier = crypto.createVerify('RSA-SHA256');
verifier.update(payload);
return verifier.verify(publicKey, signature, 'base64');
}
// In your webhook handler:
app.post('/webhook', (req, res) => {
const rawBody = req.body; // must be raw string, not parsed JSON
const timestamp = req.headers['x-webhook-timestamp'];
const signature = req.headers['x-webhook-signature'];
if (!verifyWebhook(rawBody, timestamp, signature, PUBLIC_KEY)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(rawBody);
console.log(`Task ${event.taskId}: ${event.event}`);
res.status(200).send('OK');
});
Verification example (Python):
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
import base64
def verify_webhook(raw_body: str, timestamp: str, signature: str, public_key_pem: str) -> bool:
public_key = serialization.load_pem_public_key(public_key_pem.encode())
payload = f"{timestamp}.{raw_body}".encode()
try:
public_key.verify(
base64.b64decode(signature),
payload,
padding.PKCS1v15(),
hashes.SHA256()
)
return True
except Exception:
return False
Two-Layer Security
Webhooks support two independent verification methods that can be used together:
| Method | Header | How it works | Use case |
|---|---|---|---|
| RSA signature | X-Webhook-Signature | Cryptographic proof the payload came from Rebyte | Tamper-proof verification |
| Pre-shared secret | X-Webhook-Secret | Static string you set on creation, echoed back in every delivery | Simple shared-secret check |
RSA signatures are always present. The pre-shared secret is optional — set it when creating the webhook if you want a simpler verification path.