ServiceM822 June 2026Jayden Lee

    ServiceM8 Webhook API: Understanding Form Response Payloads

    A technical breakdown of ServiceM8's new Event Webhook for form responses, including payload structure, headers, and related object data — based on real-world testing and a line-by-line comparison against ServiceM8's official developer docs.

    ServiceM8 webhook API form responses integration real-time data automation

    ServiceM8 Webhook API: Understanding Form Response Payloads

    ServiceM8 has introduced a new Event Webhook for business events, and form.response_created is one of the events it supports. It's a significant addition for anyone building real-time integrations on top of the platform. Instead of polling for changes or relying on delayed syncs, you can now receive form response data the moment it's submitted, complete with the related job context.

    The official docs confirm the event exists and tell you how to subscribe to it. What they don't yet cover is the shape of the payload you actually receive. This article cross-references everything against ServiceM8's API Reference and Guides, so you can see exactly what's officially documented, what's documented elsewhere but not linked from the webhook docs, and what we've only been able to confirm by testing in production.

    Last updated: 22 June 2026. ServiceM8 may publish updated documentation after this date. The details below reflect what we observed when testing the form.response_created event, plus a side-by-side check against the published schemas.


    Two Webhook Systems, Easy to Confuse

    Before getting into the payload, it's worth flagging something the docs don't call out clearly: ServiceM8 currently has two separate webhook subscription mechanisms, and it's easy to wire up the wrong one.

    • Object Webhook Subscriptions (/webhook_subscriptions/object) — the original mechanism. You subscribe to specific fields on an object type (e.g. job, company). When a subscribed field changes, you get a minimal payload: {object, entry: [{changed_fields, time, uuid}], resource_url}. It does not include the record itself, you have to fetch it.
    • Event Webhook Subscriptions (/webhook_subscriptions/event) — the new mechanism this article is about. You subscribe to a named business event (job.created, job.completed, form.response_created, and over 20 others) and receive a richer payload with the record data included.

    The Webhooks Overview guide still only documents the first, older mechanism. If you've read that guide and expected entry/changed_fields, that's why the real payload looks nothing like it.

    The Event Webhook Subscription reference also notes that Event Webhooks are a new capability and were being rolled out gradually, with full availability targeted by 18 September. If you're not seeing events fire yet on an older account, that's a likely reason.


    Event Types

    form.response_created is one of more than 20 supported business events, not the only one. The full list (per the official reference) includes things like job.created, job.completed, job.quote_accepted, staff.clocked_on, and company.created. This article focuses on the one most relevant to form integrations:

    Event TypeDescription
    form.response_createdFired when a form response is submitted against a job

    Delivery Format

    Webhooks are delivered as HTTP POST requests to your configured endpoint.

    Headers

    ⚠️ Not officially documented. We could not find either of these headers mentioned anywhere in the current ServiceM8 docs. The behaviour below is based on observed traffic only, treat it as unconfirmed until ServiceM8 publishes it.

    HeaderDescription
    x-addon-unique-idA unique identifier for your add-on or webhook configuration, useful for routing
    x-account-uuidThe ServiceM8 account UUID that triggered the webhook

    The payload body is Content-Type: application/json.

    Body Structure

    ⚠️ Not officially documented. This top-level envelope doesn't appear in any current guide or reference page. It's our own description of what we received on the wire.

    FieldTypeDescription
    idstringUnique webhook event identifier (prefixed smwe_)
    typestringThe event type (e.g. form.response_created)
    dataobjectThe form response data itself
    relatedobjectRelated objects, currently includes the job the form is attached to

    The data Object (Form Response)

    This is the part where the docs and reality partially overlap. Every field below matches the officially published FormResponse object schema, used for the REST GET /formresponse/{uuid}.json endpoint. ServiceM8 just doesn't tell you, anywhere in the webhook docs, that the data object you receive on this event is the same shape:

    FieldTypeDescriptionDocumented?
    uuidstringThe form response UUIDYes (FormResponse schema)
    form_uuidstringThe UUID of the form template that was submittedYes
    regarding_objectstringWhat the form relates to, currently always jobYes
    regarding_object_uuidstringThe UUID of the related object (e.g. the job UUID)Yes
    staff_uuidstringThe staff member who submitted the form (may be empty if submitted via customer portal)Yes
    form_by_staff_uuidstringThe staff member who filled out the form on behalf of someone elseYes
    field_datastring (JSON)A JSON-encoded string containing an array of form field responsesPartially. Typed as string in the schema, but nowhere does it say you need to JSON.parse() it
    edit_datedatetimeWhen the response was last editedYes
    timestampdatetimeWhen the response was createdYes
    activeinteger1 if the response is active, 0 if deletedYes
    document_attachment_uuidstringUUID of an attached document, if anyYes
    asset_uuidstringUUID of a linked asset, if anyYes

    So the correction to make here, versus how this is often reported (including in an earlier draft of this article): these fields aren't undocumented, they're documented in the wrong place. If you only read the webhook guides, you'd never find them. If you happen to check the FormResponse REST reference, they're all there.

    The field_data Array

    field_data is a JSON-encoded string. Once parsed, it is an array of objects, each representing a single form field. None of this internal structure is documented anywhere — not in the FormResponse schema (which just says string), and not in the Form Field schemas either:

    FieldTypeDescription
    UUIDstringUnique identifier for this specific field response
    FieldTypestringThe type of field, e.g. Text, Multiple Choice, Date, Photo
    ResponsestringThe actual response value
    SortOrderintegerThe order of this field in the form (1-based)
    QuestionstringThe internal question key (prefixed form_)

    Known FieldType Values

    ⚠️ No enum exists in the docs. We checked the Create Form Field and related Form Field schemas directly: field_data_json is typed as an opaque string with no enumerated list of supported question types anywhere in the published spec. Everything below is from observed traffic only.

    From our testing, the following field types are used:

    • Text — Free text input
    • Multiple Choice — Single-select or multi-select options
    • Date — Date picker response (formatted as YYYY-MM-DD HH:mm:ss)
    • Photo — A photo upload; the response value is a UUID referencing the uploaded image

    Other types (Checkbox, Signature, Dropdown, Number, etc.) are expected to exist on the Forms feature but haven't been confirmed by us in webhook traffic yet.


    The related Object

    ⚠️ Not officially documented. We could not find this object, or any of the job fields below, on any published schema, guide, or reference page. This entire section is reconstructed from production traffic.

    The related object contains the contextual data associated with the form response. Currently, only the job is included.

    Job Object

    FieldTypeDescription
    uuidstringJob UUID
    generated_job_idstringHuman-readable job number (e.g. 778)
    statusstringCurrent job status (Work Order, Quote, Invoice, Completed, etc.)
    job_addressstringFull job address
    billing_addressstringBilling address
    job_descriptionstringDescription of the work
    datedateScheduled job date
    work_order_datedatetimeWhen the work order was created
    completion_datedatetimeWhen the job was completed (may be 0000-00-00 00:00:00 if not completed)
    company_uuidstringThe client company UUID
    geo_is_validintegerWhether geocoding was successful (1 or 0)
    latnumberLatitude
    lngnumberLongitude
    geo_countrystringCountry (e.g. Australia)
    geo_statestringState (e.g. NSW)
    geo_citystringCity/suburb (e.g. Cherrybrook)
    geo_postcodestringPostcode (e.g. 2126)
    geo_streetstringStreet name
    geo_numberstringStreet number
    activeintegerWhether the job is active
    badgesstringAny badges on the job
    total_invoice_amountstringTotal invoice amount
    payment_processedintegerPayment processed flag
    payment_receivedintegerPayment received flag
    category_uuidstringJob category UUID (may be empty)
    queue_uuidstringJob queue UUID (may be empty)
    purchase_order_numberstringPurchase order number
    quote_sentbooleanWhether the quote has been sent
    invoice_sentbooleanWhether the invoice has been sent

    If you've used the Job REST reference before, most of these fields will look familiar. It appears the related.job object is, in effect, the full Job record bundled in for free. ServiceM8 just hasn't said so anywhere.


    What We Observed During Testing

    A few things stood out once we had real webhook traffic flowing, none of which are mentioned in the docs:

    Duplicate deliveries are possible. We observed the same form response payload delivered more than once, with a different webhook event ID (smwe_*) each time. Your integration needs to be idempotent. Deduplicate by the form response uuid in the data object, not by the webhook event id. The Event Webhook Subscription reference only states that your callback must return a 2xx response within 10 seconds or risk deactivation, it says nothing about delivery guarantees.

    The field_data is a string, not an object. Easy to miss given how it's labelled in some places. You'll need to JSON.parse() it before you can iterate over the fields.

    Job data is included on every form response. You get the full job context (address, geolocation, status, amounts) alongside the form data, so you don't need a separate API call to look up the job. This is genuinely useful, it's just not written down anywhere yet.

    Zero-value timestamps. Several datetime fields use 0000-00-00 00:00:00 as a "not set" sentinel. Make sure your code handles this rather than attempting to parse it as a valid date.

    Photo responses are UUIDs, not URLs. When a Photo field type is submitted, the response value is a UUID. Resolving this to an actual image URL requires a separate API call or lookup.


    What's Actually Missing From the Documentation

    To be precise about this, rather than just saying "the docs are incomplete":

    GapStatus
    Full list of supported FieldType valuesConfirmed missing — no enum anywhere in the Form Field schemas
    data object field reference inside webhook contextFields exist on the FormResponse schema, but aren't linked from the webhook docs
    field_data requires JSON.parse()Type is documented as string, but the parsing requirement isn't called out
    related object structure (job enrichment)Confirmed missing — no schema published anywhere
    Idempotency / duplicate delivery guidanceConfirmed missing — only timeout/deactivation behaviour is documented
    x-addon-unique-id / x-account-uuid headersConfirmed missing from all guides and references
    Zero-value datetime sentinel conventionConfirmed missing
    Error response formats and retry behaviourNot found in current docs

    This is understandable. Event Webhooks are a new, staged-rollout feature, and documentation for genuinely new capabilities often lags the engineering work. If you're building on this today, test thoroughly and treat every field as potentially nullable or absent.


    Suggested Architecture for Handling Webhooks

    If you're building a ServiceM8 webhook integration for form responses, here's a recommended approach:

    1. Set up a webhook endpoint that accepts POST requests and records (but doesn't yet rely on) the x-addon-unique-id and x-account-uuid headers
    2. Parse and deduplicate — store data.uuid and skip anything you've already processed
    3. Parse field_data — decode the JSON string and map each Question key to a usable field name
    4. Enrich with job contextrelated.job has most of what you need for typical workflows, without an extra API call
    5. Handle missing values defensively — expect empty strings, zero UUIDs, and 0000-00-00 00:00:00 dates
    6. Log and monitor — since the feature is new and partially undocumented, watch for payload shapes you haven't seen before

    Conclusion

    ServiceM8's Event Webhook for form responses is a genuinely useful addition, real-time, event-driven data beats polling every time. But right now there's a real gap between what's published and what the platform actually sends. Some of that gap is fields documented in the wrong place (the FormResponse schema, disconnected from the webhook docs). Some of it is genuinely undocumented (the related object, the headers, idempotency behaviour, the sentinel date format). Knowing which is which matters if you're deciding how much to rely on unconfirmed behaviour in production.

    We've reported the confirmed gaps directly to ServiceM8 and will update this article as their documentation catches up.


    Need a Hand Building on This?

    We're Proanalytica Technologies, a Sydney-based consultancy that builds web, cloud, mobile, and AI automation solutions, including ServiceM8 integrations like this one. If you're a developer or business working with ServiceM8's API and want to talk through webhook architecture, idempotency handling, or wiring this up to your own systems, get in touch:

    J

    Jayden Lee

    Founder of Proanalytica Technologies. Machine learning engineer and software developer based in Sydney, NSW. Helping Greater Sydney small businesses build better digital infrastructure.

    Need help with your Sydney business?

    From web design and WordPress maintenance to ServiceM8 setup and AI automation — we work with Greater Sydney SMBs.

    Get in Touch