Webhooks - Expected Payloads

Overview

This documentation describes the webhook payload structure and events available from the Discovery application. All webhooks deliver HTTP POST requests to configured endpoints with a Content-Type: application/json    header.


Your endpoint must return a 2xx    response or Discovery will retry delivery.


Verifying Webhook Authenticity

Every webhook request includes a Signature    header containing an HMAC-SHA256 hash of the raw request body, signed with your tenant's webhook secret. You can verify the request came from Discovery by recomputing the hash and comparing it to the header value.


Payload Envelope

Every webhook payload uses the following top-level structure:

  • type — Name of the triggered event (e.g., WorkOrderWasCreated   )
  • data — Event-specific payload object

{
  "type": "WorkOrderWasCreated",
  "data": {
    "id": "...",
    ...
  }
}

Note: There is no event    or occurred_at    field at the top level. The event name is always under the key type   .


Event Categories

Gathered Vendor Invoice File Events

Two events track raw invoice file activity in the Gather - Vendor Invoices section:

  1. GatheredVendorInvoiceFileWasCreated — Fires when an invoice file is gathered
  2. GatheredVendorInvoiceFileWasUpdated — Fires when a gathered file is modified

Both payloads include a document_url   field — a temporary signed URL providing direct access to the file:

  • On WasCreated: valid for 1 week
  • On WasUpdated: valid for 4 hours

Consumers should download the file promptly after receiving the webhook, as these URLs expire.


Example — GatheredVendorInvoiceFileWasCreated:

Additional internal fields are omitted for brevity.

{
  "type": "GatheredVendorInvoiceFileWasCreated",
  "data": {
    "id": "gvif-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "tenant_id": "ten-11112222-3333-4444-5555-666677778888",
    "status": "Gathered",
    "source": "Email",
    "batch_name": "April 2026 Invoices",
    "document_name": "invoice-april-2026.pdf",
    "document_mime": "application/pdf",
    "document_size": 245678,
    "document_md5": "d41d8cd98f00b204e9800998ecf8427e",
    "document_path": "gathered-vendor-invoice-files/2026-04-15/gvif-a1b2c3d4.pdf",
    "document_url": "https://app.discoveryapp.io/api/gathered-vendor-invoice-files/gvif-a1b2c3d4.../pdf?expires=...&signature=...",
    "extracted_at": null,
    "uploaded_by_user_id": "usr-aaaabbbb-cccc-dddd-eeee-ffff00001111",
    "bulk_import_id": null,
    "archived_at": null,
    "archived_by_user_id": null,
    "deleted_at": null,
    "created_at": "2026-04-15T10:30:00+00:00",
    "updated_at": "2026-04-15T10:30:00+00:00"
  }
}

OCR Vendor Invoice Events

Three events track invoice extraction in the Extract - OCR Vendor Invoices section:

  1. OCRVendorInvoiceWasCreated — Extraction job initiated
  2. OCRVendorInvoiceWasExtracted — Extracxtion complete
  3. OCRVendorInvoiceWasUpdated — Manual corrections and/or updates applied

All three use the same payload shape. Note that line items are a related object and are not included in the webhook payload.


Example — OCRVendorInvoiceWasCreated:

Additional internal fields are omitted for brevity.

{
  "type": "OCRVendorInvoiceWasCreated",
  "data": {
    "id": "ocr-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "tenant_id": "ten-11112222-3333-4444-5555-666677778888",
    "gathered_vendor_invoice_file_id": "gvif-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "status": "Queued",
    "vendor_name": "Acme Waste Solutions",
    "invoice_number": "INV-2026-0042",
    "account_number": "ACCT-123456",
    "invoice_date": "2026-04-01",
    "invoice_total": { "amount": "25000", "currency": "USD" },
    "subtotal": { "amount": "25000", "currency": "USD" },
    "amount_due": { "amount": "25000", "currency": "USD" },
    "total_tax": { "amount": "0", "currency": "USD" },
    "remit_to_address": "PO Box 1000, Chicago, IL 60601",
    "service_address": "456 Client Ave, Milwaukee, WI 53202",
    "duplicate_ocr_vendor_invoice_id": null,
    "archived_at": null,
    "created_at": "2026-04-15T10:30:00+00:00",
    "updated_at": "2026-04-15T10:30:00+00:00"
  }
}

Vendor Invoice Events

Four complete lifecycle events in the Audit - Vendor Invoices section:

  1. VendorInvoiceWasCreated — Initial invoice record created
  2. VendorInvoiceWasUpdated — Invoice details modified
  3. VendorInvoiceWasApproved — Invoice approved for payment
  4. VendorInvoiceWasPosted — Invoice posted to the accounting system

All four use the same payload shape. Note that line items are a related object and are not included in the webhook payload.


Example — VendorInvoiceWasCreated:

Additional internal fields are omitted for brevity.

{
  "type": "VendorInvoiceWasCreated",
  "data": {
    "id": "vi-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "tenant_id": "ten-11112222-3333-4444-5555-666677778888",
    "ocr_vendor_invoice_id": "ocr-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "vendor_account_id": "va-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "status": "Matched",
    "invoice_number": "INV-2026-0042",
    "invoice_date": "2026-04-01",
    "fiscal_period": "2026-04-01",
    "total_current_charges": { "amount": "25000", "currency": "USD" },
    "adjusted_total_current_charges": { "amount": "25000", "currency": "USD" },
    "balance": { "amount": "0", "currency": "USD" },
    "approved_at": null,
    "posted_at": null,
    "auto_approved": false,
    "archived_at": null,
    "created_at": "2026-04-15T10:30:00+00:00",
    "updated_at": "2026-04-15T10:30:00+00:00"
  }
}

Vendor Contract Events

VendorContractStatusChanged fires whenever a contract transitions between statuses.


In addition to the full vendor contract entity attributes, the payload includes the following extra fields:

Field Type Description
old_status    string | null The previous status (null when the contract is first created)
new_status    string The status the contract transitioned to
triggered_by    string | null The user ID of the employee who made the change, or null if system-triggered
status_change_timestamp    ISO 8601 string When the status change occurred

Valid status values: Pre Term, Initial Term, Initial Term (Can Cancel Auto Renewal), Initial Term (Auto Renewal Cancelled), Standard Renewal Term, Standard Renewal Term (Can Cancel Auto Renewal), Standard Renewal Term (Auto Renewal Cancelled), Rolling Month To Month, Pending Termination, Terminated


Example — VendorContractStatusChanged:

Additional internal fields are omitted for brevity.

{
  "type": "VendorContractStatusChanged",
  "data": {
    "id": "vc-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "tenant_id": "ten-11112222-3333-4444-5555-666677778888",
    "vendor_id": "vnd-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "contract_number": 12345678,
    "display_id": "12345678",
    "status": "Initial Term",
    "effective_date": "2024-01-01",
    "expiration_date": "2027-01-01",
    "term_months": 36,
    "auto_renewal_term_months": 12,
    "old_status": "Pre Term",
    "new_status": "Initial Term",
    "triggered_by": "usr-aaaabbbb-cccc-dddd-eeee-ffff00001111",
    "status_change_timestamp": "2026-04-15T10:30:00+00:00",
    "created_at": "2024-01-01T09:00:00+00:00",
    "updated_at": "2026-04-15T10:30:00+00:00"
  }
}

Work Order Events

Five work order lifecycle events are available:

  1. WorkOrderWasCreated — Work order created
  2. WorkOrderWasUpdated — Work order details modified
  3. WorkOrderWasCancelled — Work order cancelled
  4. WorkOrderWasCompleted — Work order marked as complete
  5. WorkOrderStatusWasChanged — Status transition occurred

Events 1–4 all use the same payload shape. WorkOrderStatusWasChanged includes all the same fields plus five additional ones shown below.


Example — WorkOrderWasCreated:

Additional internal fields are omitted for brevity.

{
  "type": "WorkOrderWasCreated",
  "data": {
    "id": "wo-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "tenant_id": "ten-11112222-3333-4444-5555-666677778888",
    "service_id": "svc-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "vendor_id": "vnd-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "source": "Phone",
    "requested_action": "Extra Pickup",
    "status": 9,
    "decorated_status": "New",
    "display_id": "00012345",
    "order_number": 12345,
    "requested_completion_date": "2026-04-22",
    "on_site_contact_name": "Jane Smith",
    "on_site_contact_number": "555-867-5309",
    "is_complete": false,
    "is_over_due": false,
    "incident_id": null,
    "deleted_at": null,
    "created_at": "2026-04-15T10:30:00+00:00",
    "updated_at": "2026-04-15T10:30:00+00:00"
  }
}

Example — WorkOrderStatusWasChanged:

Includes all work order fields above, plus the following additional fields. Additional internal fields are omitted for brevity.

{
  "type": "WorkOrderStatusWasChanged",
  "data": {
    "id": "wo-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "...all work order fields...",
    "old_status": 9,
    "old_status_name": "New",
    "new_status": 1,
    "new_status_name": "Requested",
    "status_change_timestamp": "2026-04-15T10:31:00+00:00"
  }
}

Incident Events

Four incident lifecycle events are available:

  1. IncidentWasCreated — Initial incident logged
  2. IncidentWasUpdated — Field modifications applied
  3. IncidentWasEscalated — Status changed to Escalated
  4. IncidentWasClosed — Status changed to Closed

All four use the same payload shape. See the Incident Fields Reference at the bottom of this article for field descriptions.


Example — IncidentWasCreated:

Additional internal fields are omitted for brevity.

{
  "type": "IncidentWasCreated",
  "data": {
    "id": "inc-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "tenant_id": "ten-11112222-3333-4444-5555-666677778888",
    "incident_number": 42,
    "display_id": "000042",
    "status": "New",
    "priority": "Low",
    "source": "Web",
    "type_id": 3,
    "reason_id": 7,
    "location_id": "loc-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "service_id": null,
    "assigned_to_id": null,
    "description": "Container was missed during scheduled pickup.",
    "filed_by": {
      "first_name": "Jane",
      "last_name": "Smith",
      "email": "[email protected]",
      "type": "Employee"
    },
    "money_saved": { "amount": "0", "currency": "USD" },
    "minutes_saved": 0,
    "reported_at": "2026-04-15T10:30:00+00:00",
    "occurred_at": null,
    "created_at": "2026-04-15T10:30:00+00:00",
    "updated_at": "2026-04-15T10:30:00+00:00"
  }
}

Client Invoice Events

Four lifecycle events fire across the client invoice lifecycle:

  1. ClientInvoiceWasCreated – Fired when a new client invoice is created
  2. ClientInvoiceWasUpdated – Fired when a client invoice's details are updated (e.g. notes, line items rebuilt, totals recalculated)
  3. ClientInvoiceWasSent – Fired when a client invoice is emailed to the client
  4. ClientInvoiceStatusWasChanged – Fired whenever a client invoice transitions between statuses

Note: Line items are a related object and are not included in the webhook payload. Use the Discovery API to retrieve line item details.

Example - ClientInvoiceWasSent

This event fires when a client invoice is emailed to the client. The payload is intentionally minimal — use the id   to fetch full invoice details via the API if needed.


{
  "type": "ClientInvoiceWasSent",
  "data": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "timestamp": "2026-04-15T10:30:00+00:00"
 }
}

Incident Fields Reference

Field Type Description
id    UUID Unique identifier
tenant_id    UUID Your tenant ID
incident_number    integer Display number
display_id    string Zero-padded display ID (e.g., 000042   )
status    string New, Needs Additional Information, Awaiting Execution, In Progress, On Hold, Escalated, Closed
priority    string Low, Medium, High
source    string Email, Internal - Company, Mobile - Customer Service App, Phone, Sensor, Text Message, Vendor - Email, Vendor - Phone, Web, Web - Client Portal
type_id    integer ID of the incident type
reason_id    integer ID of the incident reason
location_id    UUID ID of the associated location
service_id    UUID | null ID of the associated service, if any
assigned_to_id    UUID | null ID of the assigned employee
client_contact_id    UUID | null ID of the client contact
closed_by_employee_id    UUID | null ID of the employee who closed the incident
description    string | null Incident description
hauler_case_number    string | null Hauler-assigned case number
money_saved    object Money saved, e.g. {"amount": "10000", "currency": "USD"}   
minutes_saved    integer Minutes saved
filed_by    object User who filed the incident: first_name   , last_name   , email   , type   
history    array Status change history
external_urls    array External reference URLs
reported_at    ISO 8601 datetime When the incident was reported
occurred_at    ISO 8601 datetime | null When the incident actually occurred
expected_completion_date    date | null Expected resolution date
due_on    date | null Due date
initial_notification_sent_at    ISO 8601 datetime | null When the initial notification was sent
next_checklist_item    object | null Next incomplete checklist item, if any
external_reference_id    string | null External reference ID