VIEWPORT
For teams

Webhook delivery

Subscribe your own consumer (Linear, PagerDuty, custom dashboard, anything) to inbox and audit events with signed delivery.

If Slack isn't enough. Or you want events in your own system. Set up a webhook endpoint. Viewport POSTs a signed JSON payload to your URL on every subscribed event.

What you can subscribe to

Event familyExamples
inbox_item.*created, resolved, dismissed, timed_out
plan.*submitted, approved, denied, revised
approval_gate.*requested, approved, denied
context.*candidate_proposed, candidate_approved, candidate_discarded
workflow_run.*started, step_started, step_completed, failed, completed
audit.*All audit events. Pick when you want a SIEM-style firehose.
member.*invited / removed / role_changed
machine.*paired / revoked

You can subscribe per-event-family or to a wildcard (audit.*).

Create an endpoint

Settings → Integrations → Webhooks → New endpoint.

Name:       Linear bridge
URL:        https://linear-bridge.acme.dev/viewport
Events:     inbox_item.created, plan.approved, plan.denied
Secret:     auto-generated (rotate any time)

Save. The platform POSTs a webhook.test event immediately to confirm reachability.

Payload shape

POST /viewport HTTP/1.1
Content-Type: application/json
X-Viewport-Event: inbox_item.created
X-Viewport-Delivery: dlv_01J7
X-Viewport-Signature: t=1715000000,v1=sha256_hex_signature
X-Viewport-Workspace: 01J7M8N9P0Q1R2S3T4U5V6W7X8

{
  "type": "inbox_item.created",
  "id": "evt_01J7…",
  "occurred_at": "2026-05-11T14:42:00Z",
  "workspace": {
    "id": "01J7M8N9P0Q1R2S3T4U5V6W7X8",
    "slug": "acme",
    "name": "Acme Engineering"
  },
  "actor": {
    "type": "system",
    "kind": "agent_request"
  },
  "subject": {
    "type": "inbox_item",
    "id": "ibx_01J7…",
    "kind": "plan_review",
    "session_id": "ses_01J7…",
    "preview": "Refactor auth middleware to bearer tokens.",
    "expires_at": "2026-05-11T15:42:00Z",
    "decision_url": "https://app.getviewport.com/inbox/ibx_01J7…"
  }
}

Signature verification

Every payload is signed. Compute the expected signature:

signed_payload = "{timestamp}.{raw_body}"
expected = HMAC-SHA256(secret, signed_payload)

Compare against v1=… in X-Viewport-Signature. Reject if mismatch, or if t is older than 5 minutes (clock skew + replay protection).

Example (Node):

import crypto from 'node:crypto';

function verify(req) {
  const sig = req.headers['x-viewport-signature'];
  const [tPart, v1Part] = sig.split(',');
  const t = tPart.replace('t=', '');
  const v1 = v1Part.replace('v1=', '');

  const expected = crypto
    .createHmac('sha256', process.env.VIEWPORT_WEBHOOK_SECRET)
    .update(`${t}.${req.rawBody}`)
    .digest('hex');

  return (
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) &&
    Math.abs(Date.now() / 1000 - Number(t)) < 300
  );
}

Retries

If your endpoint returns non-2xx or times out (default 5s):

  • 1st retry: 5s after the initial attempt.
  • 2nd retry: 30s.
  • 3rd retry: 5 min.
  • 4th retry: 30 min.
  • 5th retry: 2 hours.

After the 5th failed retry, the delivery is parked. You'll see it in Settings → Integrations → Webhooks → Failed deliveries with the response body for debugging.

To replay a failed delivery: click Resend in the UI, or:

POST /api/workspaces/{workspace}/webhooks/{endpoint}/deliveries/{delivery}/resend

Idempotency

Every delivery has an X-Viewport-Delivery id. If you re-receive it (because of retries or replays), treat it as the same event. The consumer is expected to be idempotent.

Rate limits

Viewport limits webhook deliveries to 100 requests/second per endpoint. Bursts above that are queued. If you sustain over 100rps you'll see deliveries lag; contact us for higher limits.

Use cases

  • Linear bridge: create a Linear issue when a plan is denied with a comment.
  • PagerDuty: page on-call when an approval gate times out for a prod workflow.
  • Custom dashboard: aggregate session activity, mean-time-to-decide, by team or by repo.
  • SIEM ingest: subscribe to audit.* and forward to Splunk / Datadog.

Where to go next

On this page