Bulk-personalize an Abyssale design from a dataset
This is "mail merge" for creatives: one existing Abyssale design, one row of data per output, many finished creatives without touching the editor. Each row becomes one generate_banners_async call with its own element overrides.
Prerequisites
The Abyssale MCP server must be connected with the generate:banner scope (and read:design/read:credits for the lookup and preflight steps below).
Not for printer_multipage designs — those use abyssale-print-catalog instead, with a different addressing scheme (pages, not per-row elements).
Steps
Identify the design. Use
search_designs/list_designsto find it,get_designto confirm it and — critically — to see the real layer names and which attributes are overridable. Never guess layer names or property names; an override with a wrong key is either rejected or silently ignored depending on the field, and either way you don't get the personalization you asked for.Get a concrete reference. Call
get_design_formatfor the format you'll generate, to see one format's current values — useful for knowing what a valid value looks like for each attribute you're about to override (e.g. is a color a hex string, is an image field a URL).Map the dataset to layers. Get the user's rows (they may paste a CSV, describe a table, or reference a file) and map each column to a layer name + attribute from step 1. Confirm the mapping with the user before generating anything if any column name doesn't obviously match a layer — a wrong mapping silently produces N wrong creatives, which is expensive to redo.
Preflight the credit cost. Before firing off a large batch, call
get_remaining_creditsand tell the user how many rows you're about to generate against how many credits are available. Don't launch a 200-row batch that will exhaust the workspace's credits halfway through without saying so first.Submit one
generate_banners_asynccall per row, withelementsbuilt from that row's values (layer name → override properties). Passtemplate_format_namesif the user wants multiple formats per row, not just one. Submit every row's call before polling any of them — don't wait for row 1 to finish before submitting row 2.Poll each
generation_request_idwithcheck_generation_statusuntil every row is finalized. Treatis_finalized: falseas normal and re-poll later, not as a failure.Report as one batch, once everything is done: which rows succeeded, which (if any) failed and why, and the asset URLs — matched back to the row/identifier the user will recognize (product name, city, etc.), not just the raw generation id.
Notes
- Don't reuse one row's
elementsobject as a base and mutate it in place across the loop if you're tracking state carefully — build a fresh override object per row so a bug in one row's mapping can't leak into another's. - If the dataset is large enough that credits or rate limits are a real risk, say so and offer to run a small batch first (e.g. 3-5 rows) so the user can confirm the mapping looks right before committing to the full run.