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.
The Required Contract
Your endpoint must return the following:
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.
Common Mistakes
If you are seeing the JSON parse error, check for these frequent pitfalls:
-
Returning an HTTP 302 redirect: This results in an empty body, causing JSON.parse() to fail.
-
Returning Content-Type: text/html with raw HTML: JSON.parse() will immediately fail when it hits <!DOCTYPE....
-
Using the wrong JSON key: For example, returning {"html": "..."} instead of the required {"eventResponse": "..."}.
-
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.
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})