External API v1

Webhooks

Webhook endpoints receive a JSON notification each time a subscribed event happens in your organization, such as a case update, a finished document job, or a completed export. Instead of polling, your server listens for these deliveries. Paths are relative to /api/v1.

Signature verification is not yet available. Deliveries are currently signed with a value that differs from the secret returned when an endpoint is created or rotated, so a verifier built from that secret will reject them. Until this is fixed, treat deliveries as notifications, confirm the resource with the API before acting on it, and do not rely on the signature headers.

Endpoints

GET/webhook-endpoints

Scope: webhooks:read · Paginated

List the webhook endpoints in your organization.

POST/webhook-endpoints

Scope: webhooks:write · Idempotency-Key required

Create an endpoint. Returns 201 Created.

FieldTypeRequiredDescription
namestringYesLabel for the endpoint, up to 255 characters.
urlstringYesPublic https:// URL that receives deliveries. Addresses on private networks are rejected.
subscriptionsarrayYesEvent types or patterns to receive. See event types.

The response includes secret, shown only in this response, along with secret_prefix for identifying it later. Store the secret when you receive it.

GET/webhook-endpoints/{endpoint_id}

Scope: webhooks:read

Read one endpoint, including its subscriptions, status, health_score, and secret_prefix. The secret is never returned here.

PATCH/webhook-endpoints/{endpoint_id}

Scope: webhooks:write · Idempotency-Key required

Update name, url, subscriptions, or status. The URL is validated again.

DELETE/webhook-endpoints/{endpoint_id}

Scope: webhooks:write

Delete an endpoint. Returns 204 No Content.

POST/webhook-endpoints/{endpoint_id}/secret/rotate

Scope: webhooks:write · Idempotency-Key required

Replace the endpoint's secret. The new secret is returned once in the response. Update your server before the old value is discarded.

Event types

Subscribe to exact event types or use a pattern:

Event typeSent when
case.createdA case is created
case.updatedA case's details change
case.workflow_changedA case moves to another workflow status
document.uploadedA document upload completes
document.processing_startedA processing job starts on a document
document.processing_completedA processing job finishes successfully
document.processing_failedA processing job fails
redaction.review_completedA document's redaction review is completed
export.completedAn export finishes and can be downloaded
export.failedAn export fails
user.invitedA user is invited
user.updatedA user's details change
user.deactivatedA user is deactivated
user.roles_updatedA user's roles change
role.createdA role is created
role.updatedA role is updated
role.deletedA role is deleted

Delivery format

Each delivery is an HTTPS POST with a CloudEvents 1.0 JSON body (Content-Type: application/cloudevents+json). Payloads are minimal: they identify the resource and never include file contents, OCR text, or secrets. Fetch the full resource through the API when you need more detail.

{
  "specversion": "1.0",
  "id": "evt_01J...",
  "source": "https://redactstripe/organizations/{organization_id}",
  "type": "export.completed",
  "subject": "exports/exp_...",
  "time": "2026-10-13T10:15:00.000Z",
  "datacontenttype": "application/json",
  "data": {
    "object": { "id": "exp_...", "object": "export" },
    "links": { "resource": "/v1/exports/exp_..." }
  }
}

Headers

HeaderValue
X-RedactStripe-Event-IdThe event ID, the same as id in the body. Use it to de-duplicate events.
X-RedactStripe-Delivery-IdThe ID of this delivery attempt.
X-RedactStripe-EventThe event type.
X-RedactStripe-TimestampWhen the delivery was signed, in ISO 8601.
X-RedactStripe-Signaturev1= followed by a hex HMAC-SHA-256. See the notice above.
User-AgentRedactStripe-Webhooks/1.0

Verifying a signature (planned scheme)

When signatures are available, the signed content is the Unix time in seconds from the timestamp, a period, and the raw request body. The expected value is v1= plus the hex HMAC-SHA-256 of that content using the endpoint secret. Reject deliveries whose timestamp is more than five minutes old or in the future, and compare signatures in constant time.

import crypto from "node:crypto";

export function verifyDelivery(rawBody, headers, secret) {
  const ts = Math.floor(Date.parse(headers["x-redactstripe-timestamp"]) / 1000);
  if (!Number.isFinite(ts) || Math.abs(Date.now() / 1000 - ts) > 300) return false;

  const expected = "v1=" + crypto
    .createHmac("sha256", secret)
    .update(`${ts}.${rawBody}`, "utf8")
    .digest("hex");
  const received = headers["x-redactstripe-signature"] || "";
  return received.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
}

Responses and retries

Deliveries

GET/webhook-endpoints/{endpoint_id}/deliveries

Scope: webhooks:read · Paginated

List the delivery attempts for an endpoint, including their status and any response code.

GET/webhook-deliveries/{delivery_id}

Scope: webhooks:read

Read one delivery.

POST/webhook-deliveries/{delivery_id}/replay

Scope: webhooks:write · Idempotency-Key required

Queue the same event for delivery again, for example after your server was down or after you fixed a bug in your handler. The replay has its own delivery ID and is returned with its status.