Invalid Response Received Invalid JSON Response Received

Originally posted by Sam B

My addon activates just fine, and even gets the event details for the callback; however, regardless of what I return (nothing, a full HTML page, or pieces of HTML) I get the following error in the modal window/iframe:

Invalid Response Received

Invalid JSON Response Received

The iframe’s src is https://addon.go.servicem8.com/PluginPlatformExternalSDK_HandleActionEvent?&s_strAddonId=######&s_strActionType=job&s_strEventName=######&s_form_values=jobUUID-companyUUID&s_auth=######&jobUUID=######&companyUUID=######

Troubleshooting: “Invalid Response Received” in ServiceM8

The “Invalid Response Received” or “Invalid JSON Response Received” error occurs when the SM8 gateway runs JSON.parse() on your response body and fails.

SM8’s add-on gateway expects the exact same response protocol regardless of how your app is hosted. Both Lambda-hosted and Web Service-hosted add-ons must send back the same JSON envelope. While the “Web Service Hosted” documentation states that the output is “rendered in an iframe,” it is slightly misleading—the gateway actually parses your response as JSON first, and only then renders the HTML found inside the eventResponse field into the iframe.


:scroll: The Required Contract

Your endpoint must return the following:

  • HTTP Status: 200 OK

  • Content-Type: application/json

  • Body: {"eventResponse": "<full HTML document>"}

HTML Requirements: The HTML string inside eventResponse must be a complete document (i.e., it must include <!DOCTYPE html>, <html>, <head>, and <body>).

Note on the SDK: The SDK script (https://platform.servicem8.com/sdk/1.0/sdk.js) is optional. You only need it if you plan to call SMClient.init() for functions like resizeWindow, closeWindow, or invoke(). Plain HTML works perfectly fine for static pages or for using <script>window.location.replace(...)</script> to navigate the iframe directly to your app.


:warning: Common Mistakes

If you are seeing the JSON parse error, check for these frequent pitfalls:

  1. Returning an HTTP 302 redirect: This results in an empty body, causing JSON.parse() to fail.

  2. Returning Content-Type: text/html with raw HTML: JSON.parse() will immediately fail when it hits <!DOCTYPE....

  3. Using the wrong JSON key: For example, returning {"html": "..."} instead of the required {"eventResponse": "..."}.

  4. Omitting the Content-Type header entirely: Without it, the gateway might guess the wrong type.

Reference: You can check any sample in the servicem8/addon-sdk-samples GitHub repository. They all wrap HTML in {eventResponse: htmlString}. This is the strict wire protocol, even though Lambda samples often show it simply as a callback return value.


:laptop: Minimal Code Examples

Node.js (Express)

JavaScript

// Express / any Node web framework
app.post('/sm8/launch', (req, res) => {
  // (Verify the JWT body first — it's signed with your client_secret)
  
  const html = `<!DOCTYPE html>
<html>
  <head><title>My Add-on</title></head>
  <body><h1>Hello, ServiceM8!</h1></body>
</html>`;

  res.json({ eventResponse: html });
});

Python (Flask)

Python

from flask import jsonify

@app.route('/sm8/launch', methods=['POST'])
def sm8_launch():
    # (Verify the JWT body first)
    
    html = """<!DOCTYPE html>
<html>
  <head><title>My Add-on</title></head>
  <body><h1>Hello, ServiceM8!</h1></body>
</html>"""

    return jsonify({"eventResponse": html})

Hi Jake,

Good write-up, and it’s worth tying this back to the official docs and samples so people have a single “known-good” reference point.

For a Web Service Hosted Add-on, ServiceM8 invokes your callback URL via an HTTPS POST, with the event provided as a JWT in the HTTP request body. The output your callback returns is rendered in the ServiceM8 UI inside an iframe, so you’ll also want to ensure you’re not sending an X-Frame-Options header that prevents iframe rendering.

For Action events specifically, our docs note your add-on needs to return HTML/JavaScript that’s rendered inside an iframe, and that the output must be a complete HTML document including , and tags.

One extra detail from the official “Hello World” sample add-ons: the handler returns a JSON object with the HTML under the eventResponse key (for example callback(null, { eventResponse: strHTMLResponse });). If you’re hitting the “Invalid JSON Response Received” modal, aligning your response shape to that sample is a good first check.

If your UI needs to resize or close the modal, include the Client JS SDK in the and initialise it with SMClient.init(), then you can call methods like resizeWindow() and closeWindow().

Once you’ve tried returning { “eventResponse”: “…” }, reply here with a small snippet of the exact response body you’re sending back so we can sanity-check it.

Thanks,
Cody