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",
    "external_urls": [
      {"url": "https://example.com/hauler-portal/4471", "name": "Hauler-ticket 4"}
     ],
    "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

If you have additional questions or need more in depth information, please feel free to send us a message using the help beacon in the lower right-hand corner.