Skip to content

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.

if (await alreadyProcessed(payload.webhookId)) {
  return res.status(200).send('ok');
}