Hey Bubble community! I wanted to share a solution I finally cracked after 30+ hours of trial and error — in case it helps someone else out there struggling with JSON modification and external API workflows.
My challenge
I needed to:
- Receive structured JSON content from an external API (in my case, Eventbrite),
- Modify it — specifically update an image inside a nested object and bump the
page_version_number
, and - Send the modified version back to Eventbrite.
What didn’t work
I initially set the API Connector to expect JSON as the response type. The problem? Bubble wouldn’t let me easily work with or re-use the entire JSON structure — especially when dealing with complex nested arrays and objects. Trying to pass this into toolbox (server script) didn’t work.
The fix: Treat the response as text and process it manually
Here’s the key insight:
-
Set the response type to “text” in the API Connector.
-
Save the response as a plain text field in your database or pass it straight into server script.
-
Use Toolbox’s “Run JavaScript” in a backend workflow to parse, modify, and rebuild the JSON.
-
Send the final result wherever you need — re-encoded as JSON.
The JavaScript I used in Toolbox (server-side action)
async function updateJson() {
try {
let json = JSON.parse(properties.thing1);
let new_image_url = properties.thing2;
let new_image_id = properties.thing3;
// âś… Update herocarousel image
if (json.widgets && json.widgets[0]?.data?.slides[0]?.image) {
json.widgets[0].data.slides[0].image.url = new_image_url;
json.widgets[0].data.slides[0].image.image_id = new_image_id;
}
// âś… Clean function
function clean(obj) {
if (Array.isArray(obj)) return obj.map(clean);
if (obj && typeof obj === 'object') {
const cleaned = {};
for (const key in obj) {
if (
key === "resource_uris" ||
key === "resource_url" ||
key === "semantic_purpose" ||
key === "timed_display" ||
key === "display_restrictions" ||
(key === "id" && obj[key] === "")
) continue;
cleaned[key] = clean(obj[key]);
}
return cleaned;
}
return obj;
}
// âś… Keep only these widget types, and remove duplicate 'faq' if both exist
const allowedWidgets = [];
const widgetTypes = new Set();
for (const w of json.widgets || []) {
if (["herocarousel", "faqs"].includes(w.type) && !widgetTypes.has(w.type)) {
allowedWidgets.push(w);
widgetTypes.add(w.type);
}
}
const cleanedJson = {
access_type: json.access_type,
purpose: json.purpose,
modules: clean(json.modules),
widgets: clean(allowedWidgets)
};
return JSON.stringify(cleanedJson);
} catch (error) {
return JSON.stringify({ error: error.message });
}
}
updateJson();
Why this works better
- By treating the original API response as raw text, you sidestep Bubble’s sometimes-too-strict JSON parsing.
- Toolbox gives you full JS power — no more nested “:extract with Regex” madness.
- Your backend stays clean, scalable, and debuggable.
I had to do a normal GET request to also be able to bump the version number. Other than that I posted the JSON body as a raw body back to eventbrite. All works fine.
Bubble’s API Connector must receive structured content as text, not JSON, if you want to parse and manipulate it. At least from the Eventbrite API as far as I can work it.
I tried sending it off to AWS Lambda, Make, and even Zerowork — nothing worked. It wasn’t until I discovered that I had to receive the JSON as raw text in the API Connector that things finally clicked. Thanks for reading, I hope it can help you with something.