Webhooks

Tristar can deliver outbound HTTPS webhooks when customer-scoped events occur (orders, notes, invoices, and related catalog events). Receivers expose an HTTPS endpoint; Tristar POSTs a signed JSON envelope for each subscribed event.

Webhooks are outbound only. There is no public API to create or manage webhook endpoints from API v2 — configuration is done in the Admin Portal.

Configuration

In the Admin Portal: Customer → Webhooks.

For each webhook endpoint configure:

Setting Requirement
Name Display name for the endpoint
Target URL Absolute HTTPS URL (HTTP is rejected)
Secret Shared secret used to sign delivery bodies (X-Tristar-Signature)
Events One or more event codes to subscribe to
Active Inactive webhooks do not receive new deliveries

A webhook only receives events that are both subscribed on that endpoint and active in the event catalog.

Delivery history is available in Admin Portal under Webhook Logs.

Delivery Mechanics

  1. Application code publishes an event through IWebhookPublisher (or the invoice detection worker enqueues invoice.created).
  2. Matching active customer webhooks are enqueued as Pending rows in WebhookDeliveryLog (Central DB).
  3. A background delivery worker in API v2 polls about every 15 seconds, takes up to 25 due deliveries, and POSTs each payload as application/json.
  4. HTTP client timeout per attempt is 30 seconds.

Retries And Statuses

Status Meaning
Pending Queued, not yet successfully delivered
Failed Attempt failed; will retry if under the max attempt count
Succeeded Receiver returned HTTP 2xx
DeadLetter Exhausted retries (5 attempts)

Backoff after a failed attempt (before the next try):

delaySeconds = 2^attemptCount * 30

Examples after failed attempts 1–4: 60s, 120s, 240s, 480s. After the 5th failed attempt the delivery becomes DeadLetter.

Receiver Success Criteria

Treat the delivery as successful only when the receiver responds with an HTTP 2xx status. Non-2xx responses and transport errors mark the attempt failed and schedule a retry (until dead-lettered). Response bodies are stored truncated (up to 2000 characters) for troubleshooting.

Receivers should be idempotent: the same logical event may be retried, and publishers may use idempotency keys to avoid duplicate enqueue for the same webhook.

HTTP Headers

Every delivery POST includes:

Header Description
Content-Type application/json; charset=utf-8
X-Tristar-Event Event code (for example order.created)
X-Tristar-Delivery-Id Delivery log id (unique per queued delivery)
X-Tristar-Signature HMAC-SHA256 signature of the raw request body using the webhook secret (lowercase hex). Omitted only if the webhook has no secret.

Signature Verification

  1. Read the raw request body bytes as UTF-8 text (do not re-serialize JSON before verifying).
  2. Compute HMAC-SHA256(secret, body).
  3. Encode the digest as lowercase hexadecimal.
  4. Compare to X-Tristar-Signature using a constant-time comparison.

Reject the request if the signature is missing or does not match.

Envelope Payload

Bodies are JSON with camelCase property names:

{
  "id": "evt_0123456789abcdef0123456789abcdef",
  "event": "order.created",
  "occurredAt": "2026-07-22T15:30:00Z",
  "customerCode": "ABC001",
  "data": {}
}
Field Type Description
id string Event id (evt_ + 32 hex chars)
event string Event code
occurredAt string (ISO-8601 UTC) When the event was enqueued
customerCode string Customer code that owns the event
data object Event-specific payload (see catalog below)

Event Catalog

Event code Category Description Publisher status
order.created Order Draft or web order created Published
order.updated Order Order fields changed (draft save/update) Published
order.exported Order Order exported to a Winserve work order Published
order.status_changed Order Photocopy milestone set on a status note Published
note.created Note Note added to an order Published
invoice.created Invoice Work-order invoice detected as newly available Published

order.created / order.updated

Published when order save services create or update a draft/web order.

Common data fields:

Field Type Notes
orderType string Court, Process, Delivery, Investigation, FileAndServe, CountyRecording, or PhotoCopy
webOrderId string Web/draft order id

Photo copy orders also include:

Field Type Notes
finalize boolean Whether the save finalized the photo copy order

order.exported

Published after a successful export via order export (including create-with-export flows that call export).

Field Type Notes
orderType string Order type string passed into export
webOrderId string Source draft/web order id
workOrderId string Primary Winserve work order id
courtWorkOrder string or null Present for file-and-serve style exports when a court work order is created
processWorkOrders array or null Present when export creates one or more process work orders

note.created

Published when a note is added for a customer-scoped work order.

Field Type Notes
workOrder string Work order number
workOrderType string Work order type used by notes
report string Note/report text
lineItem int Created note / status identifier
facilityLineItem int or null Photocopy stop / facility line item when applicable

order.status_changed

Published from Notes/Add when a photocopy note includes an explicit milestone value.

Field Type Notes
workOrder string Photocopy work order (without facility suffix)
orderType string Currently PhotoCopy
facilityKey string {workOrder}:{facilityLineItem} when a stop id is supplied; otherwise the request work order key ({workOrder}-{facilityId})
facilityLineItem int or null Photocopy stop / facility line item when applicable
milestoneCode int New milestone code
milestone string Milestone title when available
previousMilestoneCode int or null Prior status-line milestone when available
previousMilestone string or null Prior milestone title when available

Example:

{
  "id": "evt_d4e5f60718293a4b5c6d7e8f901a2b3c",
  "event": "order.status_changed",
  "occurredAt": "2026-07-22T15:33:00Z",
  "customerCode": "ABC001",
  "data": {
    "workOrder": "W123456",
    "orderType": "PhotoCopy",
    "facilityKey": "W123456-01",
    "milestoneCode": 1,
    "milestone": "None",
    "previousMilestoneCode": 1,
    "previousMilestone": "None"
  }
}

Desktop Winserve milestone changes (SetMilestone) are not yet wired; those paths remain a follow-up.

invoice.created

Published by a background detection worker that scans tenant invoice header tables for recently created work-order invoices (photocopy, WinservePlus/court, delivery, investigation). Idempotency keys prevent duplicate deliveries for the same invoice.

Field Type Notes
workOrder string Work order number
orderType string PhotoCopy, Court, Delivery, or Investigation
invoiceNumber string Invoice number
invoiceDate string or null Invoice date when present
billToCustomerCode string or null Bill-to customer code when present
facilityLineItem int or null Photocopy facility line item when applicable
amount number or null Total when available (photocopy); otherwise null

Example:

{
  "id": "evt_e5f60718293a4b5c6d7e8f901a2b3c4d",
  "event": "invoice.created",
  "occurredAt": "2026-07-22T15:34:00Z",
  "customerCode": "ABC001",
  "data": {
    "workOrder": "DD14809",
    "orderType": "PhotoCopy",
    "invoiceNumber": "1",
    "invoiceDate": "2026-06-23T00:00:00",
    "billToCustomerCode": "TRISTAR",
    "facilityLineItem": 1,
    "amount": 59.95
  }
}

Example Delivery

POST /webhooks/tristar HTTP/1.1
Host: example.com
Content-Type: application/json; charset=utf-8
X-Tristar-Event: order.created
X-Tristar-Delivery-Id: 42
X-Tristar-Signature: 3f1a9c...lowercasehex...

{
  "id": "evt_a1b2c3d4e5f60718293a4b5c6d7e8f90",
  "event": "order.created",
  "occurredAt": "2026-07-22T15:30:00Z",
  "customerCode": "ABC001",
  "data": {
    "orderType": "Court",
    "webOrderId": "W123456"
  }
}

Verify X-Tristar-Signature against the exact body bytes shown above (or as received), using the webhook secret from Admin Portal. Respond with 2xx to acknowledge; any other status triggers retry/backoff.