Webhook payload missing custom fields — expected?

Hey all — running into something that feels like a bug but might just be a known limitation.

I've got a webhook set up for work_order.status_changed and I'm noticing that custom field values aren't showing up in the payload. The standard fields are all there (id, status, assignee_id, customer, etc.) but the custom_fields object is either empty or just... not present? Hard to tell since the key itself is missing.

I've confirmed the custom fields have values in the UI, and they're showing up fine when I hit the REST API directly with a GET on the same work order. So the data exists, it's just not making it into the webhook payload.

Is this expected behavior? I didn't see it called out explicitly in the webhook setup docs, though I might have missed something. If this is a limitation, worth noting that it makes the webhook a lot less useful for our integration — we'd need to follow up with an API call every time just to get the full picture.

For what it's worth, I'm seeing this on both v1 and v2 webhook endpoints. YMMV but figured I'd flag it.

Parents
  • Yeah, this one tripped me up too when I first looked at it — Leo's right that it's a known limitation, though I wish we documented it more prominently (working on that).

    The short version: webhook payloads are intentionally slim for delivery reliability. Custom fields can be large nested structures with field types, validation rules, conditional logic, etc., so we strip them from the event firehose. It's a tradeoff between payload completeness and making sure your endpoint doesn't get hammered with 500KB JSON blobs every time a status flips.

    Here's the pattern I usually recommend:

    // Webhook handler — minimal, just queue the work
    app.post('/webhook', async (req, res) => {
      const { work_order } = req.body;
      
      // Ack immediately — don't block on downstream work
      res.status(200).send('OK');
      
      // Queue background job to hydrate full record
      await queue.add('enrich-work-order', {
        work_order_id: work_order.id,
        event_type: req.body.event_type
      });
    });

    Then your worker does the GET with ?expand=custom_fields,customer,assignee or whatever you need. Yeah, it's an extra round-trip, but it also means you're resilient against webhook retries and can handle backpressure gracefully.

    From a roadmap perspective — we're prototyping a "webhook enrichment" feature that would let you opt into heavier payloads per-subscription, but that's still in design. I'll update the webhooks KB when we have something to share.

    Side note: if you're seeing this on the v2 webhooks specifically, double-check you're not using the legacy v1 compatibility layer, which has slightly different field filtering. The v2 event schema should at least include an empty custom_fields object rather than omitting the key entirely.

  • Thanks Eli — that background job pattern is exactly what I ended up doing, good to have confirmation it's the recommended path. And yeah, I'm on v2, seeing the key just... missing rather than empty. Might be worth a docs tweak to clarify that behavior.

    Appreciate the transparency on the roadmap bit. An opt-in enriched payload would solve this cleanly for our use case.

Reply Children
No Data