Skip to Content

Webhooks

Webhooks allow TrueGrade to push real-time event notifications to any external system that accepts HTTP POST requests. Use webhooks to trigger automations, update external dashboards, or sync data with your own systems.

Supported Events

CategoryEvents
Compliancedocument.submitted, document.verified, document.rejected, document.expired
Certificationscertification.advanced, certification.blocked, certification.certified
Costactual.created, actual.approved, budget.threshold_exceeded
Fielddaily_report.submitted, issue.created, issue.resolved, issue.closed
Labortimesheet.submitted, timesheet.approved, pay_application.issued, pay_application.approved
Procurementrfq.issued, bid.submitted, bid.awarded
Usersuser.invited, user.role_changed, user.deactivated

Creating a Webhook

Navigate to Administration → Integrations → Webhooks → Add Webhook:

  1. Endpoint URL — the HTTPS URL of your receiving server
  2. Secret — a random string used for HMAC signing (generate one or enter your own)
  3. Events — select which event types to send to this endpoint
  4. Active — toggle to enable/disable without deleting

Webhook endpoints must use HTTPS. HTTP endpoints are rejected.

Request Format

TrueGrade sends a JSON POST request for each event:

{ "id": "wh_01hwz2k9m3x8y4b6c7d", "event": "document.submitted", "timestamp": "2026-04-22T14:32:00Z", "organization_id": "org_01hwz2...", "project_id": "proj_01hwz2...", "payload": { "subcontractor_id": "sub_01hwz2...", "subcontractor_name": "Acme Insulation LLC", "document_type": "certificate_of_insurance", "document_id": "doc_01hwz2..." } }

Verifying Signatures

Every webhook request includes an X-TrueGrade-Signature header containing an HMAC-SHA256 signature. Verify it on your server to confirm the request came from TrueGrade:

import { createHmac, timingSafeEqual } from 'crypto' function verifyWebhookSignature( payload: string, signature: string, secret: string ): boolean { const expected = createHmac('sha256', secret) .update(payload) .digest('hex') return timingSafeEqual( Buffer.from(signature), Buffer.from(`sha256=${expected}`) ) }

Always use timingSafeEqual (or equivalent constant-time comparison) to prevent timing attacks.

Delivery and Retries

If your endpoint is temporarily unavailable, acknowledge the webhooks quickly (respond 200 immediately, process asynchronously) rather than processing synchronously during the request. This avoids timeouts and unnecessary retries.

Delivery Log

Every delivery attempt is logged with:

You can manually retry a failed delivery from the log view.