Skip to main content

Webhooks

Webhooks push platform events to your systems as they happen. Every meaningful state mutation in Orunbase — a project created, a member added, an invoice paid, a scm.push from a linked GitHub repo — lands on the workspace's event log, and the webhooks pipeline fans matching events out to your HTTPS endpoints as signed POST requests.

The model has three resources:

  • Endpoint — an HTTPS URL plus a server-generated signing secret. Endpoints belong to a workspace and can optionally be scoped to a single project. An endpoint is active, or disabled (by you, or automatically after repeated failures).
  • Subscription — an event-type filter attached to an endpoint. Each subscription names one event type (or a wildcard) that should be delivered to that endpoint.
  • Delivery attempt — one record per event × subscription, tracking status, HTTP result, retries, and replay. See Retries & replay.

Endpoints and routes

All routes live under the workspace (/v1/organizations/{orgId}/…/v1/workspaces/… is an accepted alias). Endpoint creation and listing also exist project-scoped.

MethodPathDescription
GET / POST/v1/organizations/{orgId}/webhooks/endpointsList / create endpoints (workspace scope)
GET / POST/v1/organizations/{orgId}/projects/{projectId}/webhooks/endpointsList / create endpoints (project scope)
GET / PATCH / DELETE…/webhooks/endpoints/{id}Read, update (url, name, description), delete
POST…/webhooks/endpoints/{id}/disableDisable (optional reason in body)
POST…/webhooks/endpoints/{id}/enableRe-enable a disabled endpoint
POST…/webhooks/endpoints/{id}/rotate-secretRotate the signing secret — returns the new secret once
GET…/webhooks/endpoints/{id}/subscriptionsList an endpoint's subscriptions
GET…/webhooks/endpoints/{id}/delivery-attemptsList delivery attempts (cursor-paginated)
POST/v1/organizations/{orgId}/webhooks/subscriptionsCreate a subscription
GET / PATCH / DELETE…/webhooks/subscriptions/{id}Read, update, delete a subscription
GET…/webhooks/delivery-attempts/{id}Read one delivery attempt
POST…/webhooks/delivery-attempts/{id}/replayManually replay a delivery

Endpoint URLs must be HTTPS. Reads require the organization.webhook.read (or project.webhook.read) permission; writes require organization.webhook.write (or project.webhook.write). Public ids use the prefixes whe_ (endpoint), whs_ (subscription), and whd_ (delivery attempt).

Which events you can subscribe to

Subscriptions filter the platform event log — the same stream that feeds the audit log. A subscription's eventType is either an exact type, a prefix wildcard like project.* (matches project.created, project.archived, …), or * for everything. Representative types:

AreaEvent types
Workspace & membershiporganization.created, organization.updated, membership.added, membership.updated, membership.removed, invite.created, invite.accepted, invite.revoked
Teamsteam.created, team.updated, team.deleted, team.member.added, team.member.removed
Projects & environmentsproject.created, project.archived, environment.created, environment.archived
Identityapi_key.created, api_key.revoked
Configurationsettings.updated, feature.updated, secrets.updated
Billingsubscription.activated, subscription.updated, subscription.canceled, invoice.paid, payment.failed, entitlements.updated
Webhooks (management)webhook_endpoint.created, webhook_endpoint.updated, webhook_endpoint.disabled, webhook_endpoint.enabled, webhook_endpoint.secret_rotated, webhook_subscription.created
Source control (GitHub)scm.push, scm.pull_request.opened, scm.pull_request.merged, scm.check.completed, scm.release.published — see GitHub integration
note

The delivery lifecycle events webhook.delivery_succeeded, webhook.delivery_failed, and webhook.disabled are emitted onto the event log but are never fanned out to webhooks, even under a * subscription — this recursion guard prevents deliveries from generating deliveries. Read them from the audit log instead.

What a delivery looks like

Each delivery is a POST to your endpoint with Content-Type: application/json, User-Agent: Orun-Webhooks/1.0, and the signing headers described in Verify deliveries. The body carries the event:

{
"id": "5f0c9c2e-8a51-4d5f-b0a9-1f6d2f6f9a11",
"type": "project.created",
"occurred_at": "2026-07-02T09:14:03.000Z",
"data": {
"name": "storefront",
"slug": "storefront"
}
}

id is the event-log id — stable across retries and replays, so use it for consumer-side deduplication.

Create an endpoint end-to-end

1. Create the endpoint.

curl -X POST https://api.orunbase.com/v1/organizations/org_2f8a1c9e/webhooks/endpoints \
-H "Authorization: Bearer $ORUN_CLOUD_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-hooks-endpoint-001" \
-d '{"url": "https://hooks.example.com/orun-cloud", "name": "Production consumer"}'
{
"data": {
"endpoint": {
"id": "whe_7c1d2a9b3e4f5a6b7c8d9e0f1a2b3c4d",
"orgId": "org_2f8a1c9e",
"projectId": null,
"url": "https://hooks.example.com/orun-cloud",
"name": "Production consumer",
"status": "active",
"secretVersion": 1,
"createdAt": "2026-07-02T09:14:03.000Z"
}
},
"meta": { "requestId": "req_01j9x2", "cursor": null }
}

2. Fetch the signing secret. The create response never contains secret material. Call rotate-secret once — the response is the only time the plaintext (whsec_…) is revealed; store it in your secret manager.

curl -X POST https://api.orunbase.com/v1/organizations/org_2f8a1c9e/webhooks/endpoints/whe_7c1d2a9b3e4f5a6b7c8d9e0f1a2b3c4d/rotate-secret \
-H "Authorization: Bearer $ORUN_CLOUD_API_KEY"

3. Subscribe to event types.

curl -X POST https://api.orunbase.com/v1/organizations/org_2f8a1c9e/webhooks/subscriptions \
-H "Authorization: Bearer $ORUN_CLOUD_API_KEY" \
-H "Content-Type: application/json" \
-d '{"endpointId": "whe_7c1d2a9b3e4f5a6b7c8d9e0f1a2b3c4d", "eventType": "project.*"}'

The same flow with the SDK:

import { OrunCloud } from "@saas/sdk";

const client = new OrunCloud({
baseUrl: "https://api.orunbase.com",
auth: { kind: "bearer", token: process.env.ORUN_CLOUD_API_KEY! },
});

const { endpoint } = await client.webhooks.createEndpoint(
"org_2f8a1c9e",
{ url: "https://hooks.example.com/orun-cloud", name: "Production consumer" },
{ idempotencyKey: "create-hooks-endpoint-001" },
);

// Reveal-once: store `secret` immediately, it is never readable again.
const { secret } = await client.webhooks.rotateSecret("org_2f8a1c9e", endpoint.id);

await client.webhooks.createSubscription("org_2f8a1c9e", {
endpointId: endpoint.id,
eventType: "project.*",
});

Project-scoped endpoints work the same way via client.webhooks.createProjectEndpoint(orgId, projectId, …) — they only receive events for that project, and are governed by the project-level project.webhook.* permissions.