Payload Format
HTTP Request
POST /your-webhook-endpoint HTTP/1.1
Content-Type: application/json
X-Fixify-Signature: t=1718000000000,v1=5d41402abc4b2a76b9719d...
Envelope Structure
Every webhook delivery is a JSON object with a fixed envelope and an event-specific metadata field.
{
"webhookId": "whdel_abc123",
"eventType": "job_created",
"entityType": "job",
"entityId": "job-456",
"parentEntityId": null,
"createdById": "agent-789",
"createdByRole": "Agent",
"metadata": {
"jobId": "job-456",
"agencyId": "agency-123",
"jobNumber": 488,
"categoryId": "cat-xyz",
"propertyId": "prop-123",
"estateId": null,
"requestorType": null,
"requestorInfo": null,
"originalJobId": null,
"originalJobNumber": null,
"originalAgencyId": null,
"originalAgencyName": null,
"cancellationReason": null,
"propertyRecreated": null,
"transferredToFixifyMaintenance": null
},
"createdOn": 1718000000000,
"deliveredAt": 1718000001234,
"attempt": 1
}
Envelope Field Reference
| Field | Type | Description |
|---|---|---|
webhookId |
string | Unique ID for this delivery. Use as idempotency key. |
eventType |
string | The event that fired. See Event Types. |
entityType |
string | Category of the affected entity. See Glossary. |
entityId |
string | null | Firestore document ID of the affected entity. |
parentEntityId |
string | null | ID of the parent entity (e.g. job ID on a quote event). |
createdById |
string | null | ID of the user who triggered the event. |
createdByRole |
string | null | Role string: "Agent", "ServiceProvider", "System", "Operations". |
metadata |
object | Event-specific payload. Shape is fixed per event type — see below. |
createdOn |
number | Epoch milliseconds when the original event was recorded. |
deliveredAt |
number | Epoch milliseconds when this delivery attempt was made. |
attempt |
number | Attempt number. 1 = first attempt; increments on each retry. |
About metadata
Every event type has a typed, stable payload shape. The fields are always present (never omitted), with null for fields that don't apply to a particular event or path. This means consumers can reliably destructure fields without optional-chaining every access.
IDs, not snapshots
Payloads carry identifiers and key changed values — not full entity snapshots. Use the IDs to fetch current state from the REST API when you need richer data.
Example payloads
job_created (normal path)
{
"jobId": "abc123",
"agencyId": "agency-xyz",
"jobNumber": 488,
"categoryId": "cat-abc",
"propertyId": "prop-123",
"estateId": null,
"requestorType": null,
"requestorInfo": null,
"originalJobId": null,
"originalJobNumber": null,
"originalAgencyId": null,
"originalAgencyName": null,
"cancellationReason": null,
"propertyRecreated": null,
"transferredToFixifyMaintenance": null
}
job_created (Fixify Maintenance transfer path)
{
"jobId": "new-job-456",
"agencyId": "fixify-maintenance-agency",
"jobNumber": 489,
"categoryId": "cat-abc",
"propertyId": null,
"estateId": null,
"requestorType": null,
"requestorInfo": null,
"originalJobId": "abc123",
"originalJobNumber": 488,
"originalAgencyId": "agency-xyz",
"originalAgencyName": "Acme Property Management",
"cancellationReason": "Transferred to Fixify Maintenance",
"propertyRecreated": true,
"transferredToFixifyMaintenance": true
}
job_cancelled
{
"jobId": "abc123",
"agencyId": "agency-xyz",
"jobNumber": 488,
"reason": "Job resolved internally"
}
quote_approved
{
"fulfilmentId": "ful-123",
"jobId": "job-456",
"agencyId": "agency-xyz",
"jobNumber": 488,
"serviceProviderId": "sp-1",
"quoteStatus": "approved"
}
property_created
{
"propertyId": "prop-789",
"agencyId": "agency-xyz",
"address": {
"formatted": "12 Main Street, Cape Town, 8001",
"street": { "name": "Main Street", "number": "12" },
"suburb": "Gardens",
"city": "Cape Town",
"province": "Western Cape",
"postalCode": "8001",
"unitNumber": null
},
"estateId": null,
"estateName": null,
"managingAgentId": "agent-abc",
"vacant": true
}
job_fulfilment_rating_submitted
{
"fulfilmentId": "ful-123",
"jobId": "job-456",
"agencyId": "agency-xyz",
"jobNumber": 488,
"categoryId": "cat-abc",
"serviceProviderId": "sp-1",
"rating": {
"communication": 4,
"qualityOfQuote": 5,
"qualityOfWork": 4,
"reliability": 5,
"review": "Excellent service, arrived on time."
}
}
Timestamps
All timestamps are epoch milliseconds. Convert with new Date(value).
Idempotency
webhookId is unique per delivery attempt. Use it to deduplicate — the same logical delivery may arrive multiple times across retries and replays.