Skip to content

Outbound Webhooks

Outbound webhooks send HTTP POST notifications to your server when task events occur. They fire for all tasks in your organization, whether created via API, UI, or any other channel.

EventFires when
task.createdA new task is created
task.runningThe agent starts executing
task.completedTask finishes successfully
task.failedTask fails
task.canceledTask is canceled by user
POST /v1/webhooks
FieldTypeRequiredDescription
urlstringYesHTTPS endpoint URL
eventsstring[]YesEvents to subscribe to
descriptionstringNoHuman-readable label (max 500 chars)
secretstringNoPre-shared secret (max 500 chars). When set, included as X-Webhook-Secret header in every delivery.
Terminal window
# 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",
"lastTriggeredAt": null,
"failureCount": 0
}

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/false is exposed.
GET /v1/webhooks

Returns all webhooks for your organization with status info (lastTriggeredAt, failureCount, isActive). Secrets are never exposed.

GET /v1/webhooks/:id
DELETE /v1/webhooks/:id

Returns 204 No Content. Deleted webhooks immediately stop receiving deliveries.

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": "succeeded",
"taskUrl": "https://app.rebyte.ai/run/550e8400-...",
"result": "Created CSV file with 10 Hacker News posts..."
}
}
  • status — prompt status: pending (task.created), running (task.running), succeeded (task.completed), failed (task.failed), canceled (task.canceled)
  • taskUrl — direct link to the task in the Rebyte UI (always present)
  • result — final AI output text (present on terminal events: task.completed, task.failed, task.canceled; absent on task.created and task.running)

Call GET /v1/tasks/:id for full task details including prompt history.

Delivery headers:

HeaderAlways presentDescription
Content-TypeYesapplication/json
X-Webhook-SignatureYesBase64-encoded RSA-SHA256 signature
X-Webhook-TimestampYesUnix timestamp (seconds)
X-Webhook-SecretOnly if secret configuredThe pre-shared secret value you set on creation

Timeout: deliveries time out after 10 seconds.

Failure handling:

  • Delivery is fire-and-forget — no automatic retries on failure
  • Non-2xx responses increment failureCount
  • After 10 consecutive failures, the webhook is automatically disabled (isActive: false)
  • Successful deliveries reset failureCount to 0

Every delivery is signed with your organization’s RSA-2048 key pair, regardless of whether a pre-shared secret is configured.

How it works:

  1. Rebyte concatenates {timestamp}.{body} (e.g., 1706443200.{"event":"task.completed",...})
  2. Signs the string with RSA-SHA256 using your org’s private key
  3. 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

Webhooks support two independent verification methods that can be used together:

MethodHeaderHow it worksUse case
RSA signatureX-Webhook-SignatureCryptographic proof the payload came from RebyteTamper-proof verification
Pre-shared secretX-Webhook-SecretStatic string you set on creation, echoed back in every deliverySimple 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.