Receiving status webhooks
Subscribe an HTTPS endpoint to get a signed POST when a signature task changes status, instead of polling alone.
After Create Task, prefer status webhooks to learn when a task moves (for example to Pending, Completed, Rejected, Retracted, or Failed). SutureHealth POSTs to your HTTPS URL for each change. Keep Read Task / Bulk Read for downloading the signed PDF, ad-hoc checks, and reconciliation if a delivery was missed.
You manage subscriptions with the Partner API (x-api-key). Delivery is a separate outbound call from SutureHealth to your server — no API key on that POST. Authenticity comes from an HMAC signature and a per-subscription secret returned only at create time.
Reference: Register Webhook, List Webhooks, Update Webhook, Delete Webhook.
When to use webhooks vs polling
| Approach | Role |
|---|---|
| Webhooks | Preferred for status changes — near real time, no tight poll loop |
| Read by id | Fetch the signed PDF after Completed, or confirm a single task if needed |
| Bulk read | Reconciliation, catch-up after downtime, or listing many tasks |
Recommended pattern: register a webhook for status changes, then on Completed call Read Task By Id with includeOutputAttachment=true (or false if you only need metadata) and update the task with your saved-document id.
Register a subscription
POST /api/v1/signature/webhooks
POST /api/v1/signature/webhooks
Host: partner-api.demo.suturehealth.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
{
"url": "https://partner.example.com/suture/webhooks",
"description": "Production status receiver"
}
| Field | Required | Notes |
|---|---|---|
url | Yes | Absolute HTTPS URL SutureHealth will POST to |
description | No | Up to 255 characters |
201 response (shape):
{
"id": 42,
"url": "https://partner.example.com/suture/webhooks",
"secret": "base64-encoded-256-bit-secret",
"description": "Production status receiver"
}
Storesecretimmediately. It is returned only on create. List and update responses do not include it. You need it to verifyX-Suture-Signatureon every delivery.
The subscription is active as soon as it is created. It is scoped to the organization on your API key: you only receive events for that org’s tasks.
Delivery request
SutureHealth sends:
POST https://partner.example.com/suture/webhooks
Content-Type: application/json
X-Suture-Signature: sha256=<hex>
X-Suture-Timestamp: <unix-seconds>
X-Suture-Event-Id: <guid>
X-Suture-Event-Type: document.status_changed
Example body (camelCase JSON):
{
"eventId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"eventType": "document.status_changed",
"taskId": 184392,
"documentId": 77821,
"status": "Pending",
"statusReason": null,
"occurredAtUtc": "2026-08-14T15:02:48Z"
}| Field | Meaning |
|---|---|
eventId | Unique id for this delivery event (use for idempotency) |
eventType | Always document.status_changed for this release |
taskId | Partner API task id (same as create / read) |
documentId | SutureHealth inbox request id when known; may be null early |
status | Task status string (Pending, Completed, Rejected, …) — same values as tracking |
statusReason | Optional reason (for example on Failed) |
occurredAtUtc | When the status change occurred (UTC) |
The webhook body does not include the signed PDF. On Completed, read the task (or list with filters) to download output.attachment.
What your endpoint should do
- Verify the signature (below).
- Treat
eventIdas idempotent — retries can deliver the same event more than once. - Return
2xxquickly. Non-success responses are treated as delivery failures and may be retried. - Do heavy work (download PDF, write to your EMR) asynchronously after acknowledging the POST.
Verify the signature
The signed content is the UTF-8 string:
{X-Suture-Timestamp}.{raw request body}Compute HMAC-SHA256 with your subscription secret as the key. Encode the digest as lowercase hex. The X-Suture-Signature header value is:
sha256=<hex>Compare in constant time. Also reject requests whose timestamp is too far from your clock (replay window — choose a limit that fits your ops, for example a few minutes).
Pseudocode:
signed = timestamp + "." + rawBodyBytes
expected = hex_lower(hmac_sha256(secret, signed))
header = "sha256=" + expected
accept if timing_safe_equal(header, X-Suture-Signature)List, pause, and remove subscriptions
List
GET /api/v1/signature/webhooks → 200 array of subscriptions (id, url, description, isActive, createdAt, updatedAt). No secrets.
Deactivate or change URL
PATCH /api/v1/signature/webhooks/{id}
{ "active": false }or
{ "url": "https://partner.example.com/suture/webhooks-v2" }Inactive subscriptions do not receive deliveries. Reactivate with "active": true.
Delete
DELETE /api/v1/signature/webhooks/{id} → 204. Soft-deletes the subscription; it no longer receives events.
All of these require x-api-key and only affect subscriptions for your organization.
Recommended integration pattern
- Register one HTTPS endpoint per environment and store
id+secret. - On each delivery, verify the signature, then enqueue work keyed by
eventId/taskId. - When
statusisCompleted(orRejected), read the task for the PDF or rejection outcome, then update with yourexternalSavedDocumentId. - Keep periodic bulk read as a safety net for missed events.
- Deactivate or delete the subscription when rotating endpoints; create a new one if you need a new secret.
Errors (management API)
| Situation | Typical response |
|---|---|
Missing or invalid x-api-key | 401 or 403 |
Invalid create/update body (for example missing url) | 400 |
Unknown webhook id for this key | 400 |
Updated about 2 months ago
