Article Setting Up Webhooks

Setting Up Webhooks

Webhooks in FieldPulse let you push real-time event notifications to your own systems — think "hey, something happened, here's the details." No polling, no checking every five minutes to see if a work order updated. The event comes to you.

Yeah, this one tripped me up too when I first looked at it. The webhook UI is straightforward, but there are some gotchas around retries and signatures that aren't obvious until you're debugging why your endpoint isn't getting hit.

What You Can Subscribe To

Right now, webhooks fire on these events:

  • work_order.created — new work order hits the system
  • work_order.status_changed — status moves from Scheduled → In Progress → Completed, etc.
  • work_order.assigned — technician assignment changes
  • customer.created — new customer record
  • invoice.generated — invoice created from a completed job

There's been some back-and-forth on whether work_order.updated should exist as a generic catch-all. Currently it doesn't — you get specific events instead. Which honestly? Better for your webhook handler not getting blasted on every tiny field edit.

Creating a Webhook Endpoint

Head to Settings → Integrations → Webhooks and hit Add Endpoint.

You'll need:

  • Target URL — HTTPS only, must respond with 200 OK
  • Events to subscribe — check the boxes you want
  • Secret key (optional but recommended) — used to sign the payload so you can verify it came from FieldPulse

Save it and you'll see the endpoint listed with a Test button. That sends a dummy payload — super useful for confirming your URL is reachable before real events start flowing.

Verifying Webhook Signatures

If you provided a secret, FieldPulse signs each payload with HMAC-SHA256. The signature lives in the X-FieldPulse-Signature header.

Here's a quick Node.js snippet to verify:

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your express handler:
const signature = req.headers['x-fieldpulse-signature'];
const payload = JSON.stringify(req.body);

if (!verifySignature(payload, signature, process.env.WEBHOOK_SECRET)) {
  return res.status(401).send('Invalid signature');
}

res.status(200).send('OK');
// Then process the event...

Word of warning: verify the signature before you do anything else. I once saw someone process the webhook, update their database, then check the signature. Don't be that person.

Retry Behavior and Idempotency

FieldPulse retries failed deliveries with exponential backoff: 1 minute, 5 minutes, 25 minutes, then gives up. Your endpoint needs to return 200 OK fast — under 5 seconds — or the webhook system assumes failure.

Each payload includes an event_id UUID. Store these and deduplicate — retries mean you might see the same event twice. The event_id is your idempotency key.

{
  "event_id": "evt_abc123def456",
  "event_type": "work_order.status_changed",
  "timestamp": "2026-04-17T14:30:00Z",
  "data": {
    "work_order_id": "wo_789xyz",
    "previous_status": "scheduled",
    "new_status": "in_progress",
    "changed_at": "2026-04-17T14:28:45Z",
    "changed_by": "usr_technician_001"
  }
}

Testing Locally

For local development, I usually reach for ngrok or smee.io if you want something quick and webhook-specific.

Or if you're feeling lazy, webhook.site gives you a disposable URL to inspect payloads without writing any code. Good for sanity-checking the shape of events before you build your handler.

Common Gotchas

  • Payload ordering: The data object keys aren't guaranteed ordered. Don't rely on JSON key order.
  • Missing custom fields: Custom field values aren't in webhook payloads yet. There's a thread in the dev forum about this — see the discussion here. For now, you'll need to API-fetch the full record if you need custom data.
  • SSL certificate issues: Self-signed certs won't fly. FieldPulse validates the full chain.
  • Multiple events, single endpoint: Yep, you can subscribe a single URL to multiple event types. Check event_type in your handler to route accordingly.

See Also

  • Using the FieldPulse API — for fetching additional data after receiving a webhook
  • API Rate Limits and Best Practices
  • Webhook not firing on job completion — only fires on creation — troubleshooting thread
  • How do I verify the webhook signature in Node.js? — community discussion with alternate implementations

Questions? Hit me up in the dev forum. I spend way too much time in webhook internals and am happy to dig into edge cases.