Webhooks
Receive signed OrkStage events over HTTPS. Event types, payload, signature checks in Node.js, Python and Go, retries, testing and redelivery.
OrkStage sends events to your HTTPS endpoints as JSON POST requests signed with HMAC-SHA256. Use
them to post to chat, open tickets, start pipelines or feed your incident tools.
Events
| Event | When |
|---|---|
deployment.started | The worker picked up a deployment. |
deployment.succeeded | A deployment (apply or destroy) finished. |
deployment.failed | A deployment ended in failure. |
deployment.awaiting_approval | A high risk production deployment waits for approval. |
instance.degraded | An instance's health dropped. |
instance.drift_detected | The drift scan found changes made outside OrkStage. |
incident.opened, incident.updated, incident.resolved | An incident was opened, updated or resolved. |
cloud_target.unhealthy | A cloud target failed validation, either when someone validates it or when a deployment checks it before it starts. Sent once, when the target becomes unhealthy. |
service.unhealthy | A critical resource of an instance turned unhealthy. |
maintenance.scheduled, maintenance.started, maintenance.completed | Planned maintenance on a status page. |
domain.verified, domain.verification_failed | A custom domain's TXT record was found, or did not appear within 7 days. |
domain.certificate_issued, domain.certificate_expiring | A custom domain's certificate was issued, or expires in 30, 14, 7 or 1 days. |
quota.exceeded | Someone tried to create an instance beyond the tenant's instance limit. |
ping | Sent by the Test button in the console and by POST /webhooks/:id/test. You cannot subscribe to it. |
Creating an endpoint
In the console, open Integrations, then Webhooks, then New webhook. Through the API, use a
session or a key with webhooks:manage:
POST /api/v1/webhooks
Content-Type: application/json
{
"url": "https://hooks.example.com/orkstage",
"description": "deployments to #platform",
"events": ["deployment.failed", "deployment.awaiting_approval", "incident.opened"],
"tenantId": "8a1c…"
}format is ORKSTAGE by default. SLACK and TEAMS send a chat message to an incoming webhook
of that service instead of the signed JSON event, see
notifications.
The response contains { webhook, secret }. You see the signing secret (whsec_…) once. The
server stores it encrypted with ENCRYPTION_KEY. To rotate it, call
POST /webhooks/:id/rotate-secret, which returns the new secret.
OrkStage checks the URL when you create the endpoint and again before every delivery, because DNS can change:
- The URL must use
https://and must not contain credentials. - The host must not resolve to a private, loopback, link local or multicast address.
- Redirects are not followed, and each request times out after 10 seconds.
For local development, WEBHOOK_ALLOW_PRIVATE=true also accepts http:// URLs and private
addresses. The API refuses to start with this setting in production.
The request
POST /orkstage HTTP/1.1
Content-Type: application/json
User-Agent: OrkStage-Webhooks/1.0
OrkStage-Event: deployment.failed
OrkStage-Delivery: 0f8b6c1e-…
OrkStage-Signature: t=1791234567,v1=5f2b…c9
{
"id": "0f8b6c1e-…",
"type": "deployment.failed",
"createdAt": "2026-10-05T14:21:07.000Z",
"tenantId": "8a1c…",
"data": {
"title": "Deployment failed",
"body": "The deployment did not complete (AUTHORIZATION_ERROR). Open the deployment for details and safe retry options.",
"link": "/deployments/9b07…",
"deploymentId": "9b07…",
"instanceId": "41de…",
"kind": "APPLY"
}
}OrkStage-Delivery is unique per delivery and stays the same across retries. A manual redelivery is
a new delivery with a new OrkStage-Delivery value, but its body keeps the original id. To drop
duplicates, use the body id, which covers both retries and redeliveries.
Verifying the signature
OrkStage-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>- Read the raw request body, before any JSON parsing.
- Split the header on
,and=to gettandv1. - Reject the request if
tis more than 300 seconds away from your clock. This protects against replays. - Compute
HMAC-SHA256(secret, t + "." + rawBody)as lowercase hex and compare it withv1in constant time. - Answer with a
2xxstatus quickly, and do the work in the background.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyOrkStage(secret: string, header: string, rawBody: string, toleranceS = 300): boolean {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=', 2) as [string, string]));
const t = Number(parts.t);
if (!Number.isFinite(t) || Math.abs(Math.floor(Date.now() / 1000) - t) > toleranceS || !parts.v1) return false;
const expected = Buffer.from(createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex'));
const given = Buffer.from(parts.v1);
return expected.length === given.length && timingSafeEqual(expected, given);
}
// Express: keep the raw body for this route.
// app.post('/orkstage', express.raw({ type: 'application/json' }), (req, res) => {
// const ok = verifyOrkStage(process.env.ORKSTAGE_WEBHOOK_SECRET!, req.get('OrkStage-Signature') ?? '', req.body.toString('utf8'));
// if (!ok) return res.sendStatus(400);
// res.sendStatus(204);
// handle(JSON.parse(req.body.toString('utf8')));
// });The platform uses the same algorithm in verifyWebhook (packages/runtime/src/webhooks.ts).
Retries and failures
| Success | Any 2xx response within 10 seconds. OrkStage ignores the response body. |
| Retry schedule | After a network error or a status outside 2xx, OrkStage retries after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours. Then the delivery is FAILED. |
| Automatic disabling | After 20 failed deliveries in a row, OrkStage disables the endpoint and records a reason. Turn it back on with PATCH /webhooks/:id { "enabled": true }, which also resets the failure count. |
Testing and redelivery
| Endpoint | What it does |
|---|---|
POST /webhooks/:id/test | Sends a ping at once and returns the delivery result. |
GET /webhooks/:id/deliveries | Lists the last 50 deliveries with status, attempts, response status and error. |
POST /webhooks/:id/deliveries/:deliveryId/redeliver | Creates a new delivery with the same payload and answers 202. The worker sends it on its next pass. |
PATCH /webhooks/:id | Changes url, description, events or enabled. |
DELETE /webhooks/:id | Removes the endpoint. |
In the console, open Integrations, then Webhooks. The Deliveries button of an endpoint lists its recent deliveries, each with a Redeliver button.
API keys
Create, use, scope and revoke OrkStage API keys for CI/CD, scripts and the ork CLI. Token format, roles, the ten scopes, expiry, auditing and storage.
CI/CD intake
Connect GitHub Actions, Jenkins, GitLab CI/CD or AWS CodePipeline to OrkStage. Publish image versions by digest, validate templates, deploy and wait.