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
| Category | Events |
|---|---|
| Compliance | document.submitted, document.verified, document.rejected, document.expired |
| Certifications | certification.advanced, certification.blocked, certification.certified |
| Cost | actual.created, actual.approved, budget.threshold_exceeded |
| Field | daily_report.submitted, issue.created, issue.resolved, issue.closed |
| Labor | timesheet.submitted, timesheet.approved, pay_application.issued, pay_application.approved |
| Procurement | rfq.issued, bid.submitted, bid.awarded |
| Users | user.invited, user.role_changed, user.deactivated |
Creating a Webhook
Navigate to Administration → Integrations → Webhooks → Add Webhook:
- Endpoint URL — the HTTPS URL of your receiving server
- Secret — a random string used for HMAC signing (generate one or enter your own)
- Events — select which event types to send to this endpoint
- 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
- TrueGrade expects a
2xxresponse within 10 seconds - If no
2xxis received, the delivery is retried up to 5 times with exponential backoff (1m, 5m, 15m, 1h, 4h) - After 5 failed attempts, the webhook is marked Failed and no further retries occur
- Failed deliveries are visible in Administration → Integrations → Webhooks → [Webhook] → Delivery Log
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:
- Event ID and type
- HTTP status code returned by your server
- Response body (first 500 characters)
- Timestamp
- Retry number
You can manually retry a failed delivery from the log view.