Purpose
Webhooks are signed wake-up signals
Aurora webhooks tell a receiver that supported workspace activity occurred. They are useful for waking a worker or refreshing a cache, but they are not a durable event stream and they do not grant access to Aurora.
Workspace scoped
Each endpoint belongs to one workspace and receives only its selected event types.
Signed
Every request carries an HMAC-SHA256 signature over the timestamp and exact raw body.
Separate credentials
The receiver needs its own PAT or OAuth credential before it reads or changes Aurora data.
Setup
Configure an endpoint
Workspace owners and admins create webhook endpoints from Settings -> Admin -> Integrations.
| Field | Requirement |
|---|---|
| Name | A recognizable receiver or workflow name |
| Delivery URL | An http or https endpoint; https is strongly recommended |
| Events | At least one supported event type |
| Signing secret | Returned once at creation; store it immediately in the receiver's secret manager |
Rotation and changes
Contract
Delivery envelope and headers
Aurora sends one JSON POST to each matching active endpoint. Treat payload fields as a trigger snapshot and re-read current state before acting.
{
"event": "issue.updated",
"payload": {
"issue": {
"id": "issue-id",
"key": "ENG-123"
}
},
"sentAt": "2026-08-06T20:15:00.000Z"
}| Header | Value |
|---|---|
| content-type | application/json |
| x-aurora-event | The event name, matching the envelope event field |
| x-aurora-signature | sha256=<hex HMAC digest> |
| x-aurora-timestamp | Unix epoch milliseconds used in the signed payload |
Security
Verify the raw-body signature
Build the signed payload from the timestamp header, a period, and the exact UTF-8 request body before JSON parsing.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyAuroraWebhook({ rawBody, timestamp, signature, secret }) {
if (!timestamp || !signature) return false;
const sentAtMs = Number(timestamp);
if (!Number.isFinite(sentAtMs) || Math.abs(Date.now() - sentAtMs) > 5 * 60_000) {
return false;
}
const expected = `sha256=${createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex")}`;
const expectedBytes = Buffer.from(expected);
const actualBytes = Buffer.from(signature);
return expectedBytes.length === actualBytes.length &&
timingSafeEqual(expectedBytes, actualBytes);
}Verification order
Catalog
Supported event types
Meeting and discussion mutations do not have their own event families today. Actions that create an issue can still produce issue.created.
| Event | Trigger | Payload intent |
|---|---|---|
| project.created | A project is created | Project summary |
| project.updated | Project metadata changes | Updated project summary |
| project.deleted | A project is deleted | Deleted project identity |
| issue.created | A mainline issue is created | Issue summary |
| issue.updated | A mainline issue changes | Updated issue summary |
| issue.deleted | A mainline issue is deleted | Deleted issue identity |
| issue.subtask.created | A sub-task is created | Parent issue and sub-task summary |
| issue.subtask.updated | A sub-task changes | Parent issue and updated sub-task summary |
| issue.subtask.deleted | A sub-task is deleted | Parent issue and deleted sub-task identity |
| issue.subtask.promoted | A sub-task becomes a mainline issue | Source sub-task and promoted issue summary |
| issue_container.milestone.created | A container milestone is created | Container and milestone summary |
| issue_container.milestone.updated | A container milestone changes | Container and updated milestone summary |
| issue_container.milestone.deleted | A container milestone is deleted | Container and deleted milestone identity |
| issue_container.milestone.commented | A milestone comment is added | Container, milestone, and comment summary |
Reliability
Current delivery behavior
The current sender favors low-latency notification over durable delivery. Design receivers around the guarantees that exist today.
| Behavior | Current contract |
|---|---|
| Dispatch | One immediate POST to every active endpoint subscribed to the event |
| Success tracking | lastTriggeredAt changes only after a successful HTTP response |
| Retries | None today for network failures or non-2xx responses |
| Delivery history | No durable delivery record, replay endpoint, or response-body log |
| Timeout | No explicit sender timeout today |
| Event id | No stable event id is included; consumers must define their own idempotency strategy |
Correctness requires reconciliation
Implementation
Recommended receiver pattern
Keep the HTTP handler small and move authorization, state checks, and agent work behind your own durable queue.
1. Verify the signature and replay window against the raw body.
2. Enqueue the envelope and return 202 Accepted.
3. Select a separately stored Aurora PAT or OAuth credential.
4. Re-read the referenced resource through /api/external/* or MCP.
5. Confirm the principal still has the required project access.
6. Re-evaluate assignment and status before acting.
7. Use Idempotency-Key on downstream create operations.
8. Write a durable Aurora comment or status result.Boundaries
Current boundaries
Plan around these limitations instead of assuming queue semantics Aurora does not yet provide.
No replay service
Aurora does not expose delivery attempts, replay controls, or test-delivery endpoints today.
Partial emitter coverage
Project and mainline issue events cover listed browser and external flows. Sub-task and milestone emitters currently live in external routes.