Skip to content

Blueprints

A blueprint describes what a marketplace should look like. The agent writes one, previews it with apply_blueprint and dry_run: true, reads the plan, then applies it with the plan_hash the preview returned.

{
"settings": { "App.name": "Acme", "Seo.title": "Hébergements insolites partout en France" },
"user_groups": [
{ "title": "Hôtes", "handle": "hotes", "can_post_listing": true },
{ "title": "Voyageurs", "handle": "voyageurs", "can_post_listing": false }
],
"listing_attributes": [
{ "title": "Type de bien", "alias": "type_de_bien", "input_type": "select",
"searchable": true, "search_filter": true,
"options": ["Cabane", "Yourte", "Roulotte"] },
{ "title": "Capacité", "alias": "capacite", "input_type": "number" }
],
"categories": [
{ "title": "Logements insolites", "slug": "logements-insolites", "children": [
{ "title": "Cabane dans les arbres" },
{ "title": "Yourte", "children": [{ "title": "Yourte avec spa" }] }
]}
],
"order_types": [
{ "handle": "reservation", "title": "Réservation", "transaction_type": "booking",
"booking_type": "period", "calendar_enabled": true, "price_enabled": true,
"supplier_fees_percentage": 12 }
]
}

Sections are applied in a fixed order, whatever order you write them in: settings, user_groups, listing_attributes, categories, order_types, collection_models, collections, pages, blog, menus. Each section is planned against the marketplace as it stands before the apply.

Each entry is identified by its key — a slug, a handle or an alias, depending on the section. An entry whose key exists updates that item; one whose key does not exist creates a new item. When you omit the key, most sections derive it from the title, so the same blueprint gives the same key on every run. To update something, copy its key from describe_marketplace.

Absence is never deletion. An item missing from the blueprint is left as it is. You delete in two ways, both requiring the marketplace:destroy scope:

  • "delete": true on one entry — supported by pages, collections and collection_models;
  • prune: true on the call — deletes, in every section the blueprint carries, the items it leaves out. settings and menus are never pruned.

The server declines deletions that would lose data (a category with listings, a group with members…). They appear in the plan as noop operations with a note explaining why.

Unknown names are refused, not ignored. An unknown section, or an unknown field in an entry, stops the whole blueprint with a message listing what is accepted. A blueprint never half-applies because of a typo. (One exception: an unknown key inside a listing attribute’s option is ignored.)

Send JSON types. Booleans as true/false and numbers as numbers. Most fields read a string such as "false" as true; a few refuse anything but a boolean (a menu link’s new_tab, the no_index and no_follow of collections and models).

References by id need two applies. Collection conditions name categories, groups, order types and attribute options by the ids describe_marketplace gives. Something created by a blueprint gets its id when it is applied, so a condition naming it goes in the next blueprint. References by slug (a page’s parent, an article’s category) can point to an item created in the same blueprint.

settings:write. An object of setting name to value. Values are strings, numbers, booleans or null, stored as text: true as 1, false and null as an empty value.

{
"settings": {
"App.name": "Acme",
"App.lang": "fr",
"Seo.title": "Hébergements insolites partout en France",
"Theme.footer.nb_columns": "3",
"Theme.Blog.grid_preset": "preset-2"
}
}

Writable names

Name Values
App.name, App.lang, App.timezone Exactly these three names
Any name starting with Homepage., Listing., Blog., Contact. or Seo. Text. Use names describe_marketplace shows.
Theme.footer.nb_columns 0 to 4
Theme.footer.columns.1.content to Theme.footer.columns.4.content HTML (see Markup)
Theme.footer.social_buttons 1 or true switches them on; any other value switches them off
Theme.Listing.grid_preset, Theme.User.grid_preset preset-1, preset-2
Theme.Blog.grid_preset preset-1, preset-2, preset-3
ThemeSettings.<package>.<option> An option of the active theme package, as listed in describe_marketplace → theme.package.options[].key, validated by its type; a select takes one of its choices. Writing an option’s default value removes the stored value.

Everything else is refused, Theme.* included: the theme, its custom CSS and JavaScript and its colour file are set by an administrator in the back office (Themes → Customize). A name or a value that looks like a credential is refused. Settings are never deleted, even with prune.

settings:write. A list of member groups, keyed on handle.

{ "user_groups": [{ "title": "Hôtes", "handle": "hotes", "can_post_listing": true, "requires_moderation": true }] }
Field Type Notes
title string Required in every entry
handle string The key. Derived from title when omitted (Garage partenaire → garage-partenaire). A new title keeps it: unlike in the back office, the address does not follow the title.
can_post_listing boolean Members may post listings
is_onboarding boolean
is_active boolean
is_public boolean
can_change_group boolean
requires_moderation boolean
sort_priority integer
type integer 1 individual, 2 organisation, 3 non-profit

Renaming a group means sending its existing handle with the new title. A group without a handle (describe_marketplace shows handle: null) cannot be written by a blueprint.

Deleting: prune only. A group with members is never deleted.

catalog:write. A list of the fields a listing carries, keyed on alias, each with its options.

{
"listing_attributes": [
{ "title": "Équipements", "alias": "equipements", "input_type": "checkbox",
"options": ["Wi-Fi", "Spa", { "title": "Animaux acceptés", "alias": "animaux" }] }
]
}
Field Type Notes
title string Required in every entry
alias string The key. Derived from title when omitted, with underscores (Type de bien → type_de_bien).
input_type string select, checkbox, input, textarea, number, boolean, link, price, date, text. Defaults to select, also on an update: always send it.
value_type string string, text, number, json
description string
required boolean
searchable boolean
search_filter boolean Offered as a search filter
display_unselected_option boolean
apply_to_all boolean Attached to every category
visibility integer 1 everyone, 2 administrators only. Other numbers are not refused: send one of these.
sort_priority integer
options list Required for select and checkbox (at least one), refused for other types

An option is a string (its title) or an object { "title", "alias"?, "sort_priority"? }. Options are keyed on their alias, derived from the title when omitted; sort_priority defaults to the option’s position in the list. Options you leave out are kept, unless you prune.

Deleting: prune only. It deletes the attributes the blueprint leaves out, and the options it leaves out of the attributes it carries. Values already recorded on listings for a deleted attribute are lost. System attributes are never deleted.

catalog:write. The category tree, nested with children, keyed on slug.

{
"categories": [
{ "title": "Logements insolites", "slug": "logements-insolites", "children": [
{ "title": "Cabane dans les arbres", "slug": "cabane-dans-les-arbres", "is_featured": true }
]}
]
}
Field Type Notes
title string Required in every entry
slug string The key, and the category’s public address. Derived from title when omitted. A new title keeps it: unlike in the back office, the address does not follow the title.
children list Subcategories. Three levels at most.
page_title, description, description_long, meta_title, meta_description, image_alt string
display_in_search, display_in_menu, is_filter, is_featured, cart_ok boolean
sitemap_changefreq string always, hourly, daily, weekly, monthly, yearly, never
sitemap_priority number 0 to 1

Three rules matter:

  • The slug is the identity. Two categories may share a title, never a slug. To change a category, carry its slug: a renamed category without its slug is a new category, and the plan says so in a note (A category titled "Diagnostic" already exists under the slug "diagnostic-auto". This will create a SECOND one…). A slug cannot be changed here; changing the title keeps the slug and the address.
  • The tree comes from the nesting. An entry’s parent is the entry it is nested in, and a top-level entry has no parent. To update a subcategory, nest it under its parent, as in the tree. Placing an existing category elsewhere moves it, with its subcategories (the plan notes it). parent_id, lft, rght, level and similar fields are refused.
  • Three levels at most. A fourth level is refused.

Deleting: prune only. A category that has subcategories or holds listings is never deleted; a branch you want gone takes one apply per level.

catalog:write. What ordering means on the marketplace: selling, booking, digital or display only.

{
"order_types": [
{ "handle": "location-courte", "title": "Location courte durée",
"transaction_type": "booking", "booking_type": "period",
"calendar_enabled": true, "price_enabled": true,
"customer_fees_percentage": 5, "supplier_fees_percentage": 10 }
]
}

Key. Order types have no slug. Give each one a handle you choose: it becomes the type’s URL token when the type is created, and matches it afterwards. Without a handle, types are matched on their exact title, and a rename creates a new type. The URL token of an existing type is never changed; to update a type created in the back office, send its url_token (from describe_marketplace) as handle, or its exact title.

Field Type Notes
title string Required in every entry
handle string The key (see above)
transaction_type string no_transaction, booking, selling, digital_asset
booking_type string period, qty
order_btn_text, availability_type string
apply_to_all_groups, price_enabled, shipping_enabled, weight_enabled, stock_enabled, stock_auto_decrement, restock_on_cancel, hide_if_out_of_stock, min_qty_enabled, qty_in_cart, calendar_enabled, last_day_included, order_time_enabled, digital_assets_enabled, listings_needs_approval, vendor_manage_publish_status, all_new_listings_on_homepage, use_supplier_avatar_as_listing_cover, contact_supplier_enabled, supplier_fixed_fees_on_item, customer_fixed_fees_on_item boolean
supplier_fees_flat, customer_fees_flat number Not negative
supplier_fees_percentage, customer_fees_percentage number 0 to 100
min_image_number, max_image_number integer Not negative; min not above max
upload_listing_image_mandatory boolean true sets min_image_number to 1 when you do not send min_image_number

A new type starts from the platform’s defaults; the blueprint overrides what it names.

Deleting: prune only. A type used by listings is never deleted, and a type the blueprint names (by handle or by title) is kept. Check the plan’s delete operations before applying a prune all the same: a type you renamed without a handle is a new type, and the old one is pruned.

catalog:write. A collection model makes one storefront collection per value of a criterion (one per brand, per city…) and keeps them in step. Keyed on handle, an internal name: a model has no page of its own.

The fields and their rules are those of the Admin API (create a collection model).

{
"collection_models": [
{
"handle": "logements-par-type",
"title": "Logements par type",
"type": "listing",
"pivot": { "type": "attribute", "field": "type_de_bien", "values": [
{ "value": "811", "label": "Cabane" },
{ "value": "812", "label": "Yourte" }
]},
"handle_pattern": "logements-{value}",
"title_pattern": "Logements : {value}",
"sort_by": "manual",
"collections": { "812": { "page_title": "Dormir dans une yourte" } }
}
]
}
Field Notes
handle Required. The key.
title, pivot, handle_pattern Required to create a model
type listing (default) or user. Fixed once the model has collections: make a new model instead.
pivot { "type", "field"?, "values": [{ "value", "label" }] }. type: attribute, category, seller_group, order_type, city, country, department or tag (for users: seller_group, city, country, department); field: an attribute’s alias. Sent, it replaces the previous criterion whole.
rules Conditions every collection of the model shares (see Conditions), all combined with AND
sort_by alphabetical, count, manual
handle_pattern, title_pattern, page_title, subtitle, description Patterns: {value} stands for each value’s label
image_alt, template, thumbnail, meta_title, meta_description, meta_keywords, no_index, no_follow, sitemap_changefreq, sitemap_priority Defaults for its collections
collections { "<value>": { … } }: the texts one of its collections replaces. null takes the model’s pattern again; "" leaves the field empty on purpose, except title (a collection always has a name). Works for a collection the same blueprint creates.
detach ["<value>", …]: values you took out of pivot.values whose collection leaves the model and keeps what it has, instead of going offline
delete true, alone with handle, deletes the model

The plan shows what happens to each of its collections, under the collections section:

  • a new value creates a collection, online, at the address its pattern gives — or at the first free -2, -3… when that address is taken, and the note says by what;
  • the conditions of the others follow the model;
  • a value taken out puts its collection offline, its texts kept;
  • a value that comes back puts its collection back online.

Deleting a model leaves its collections online, on their own. Images are uploaded in the back office: image is refused.

catalog:write. Storefront collections, keyed on handle, which is their address (/handle). Another handle is another collection.

The fields and their rules are those of the Admin API (create a collection).

{
"collections": [
{
"handle": "cabanes-avec-spa",
"title": "Cabanes avec spa",
"page_title": "Cabanes avec spa privatif",
"is_online": true,
"rules": {
"inclusion": { "type": "AND", "rules": [
{ "type": "category", "operator": "is", "value": ["102"] },
{ "type": "attribute", "field": "equipements", "operator": "is", "value": ["905"] }
]},
"exclusion": []
}
},
{ "handle": "promo-ete", "delete": true, "redirect_to": "/cabanes-avec-spa" }
]
}
Field Notes
handle Required. The key and the address.
title Required to create a collection
storefront_enabled A new collection is a storefront page (true)
is_online A new collection is offline unless you set true
type listing or user
rules Its conditions (see below)
page_title, subtitle, description (HTML), image_alt, canonical_url, meta_title, meta_description, meta_keywords
template A collection template suffix the theme has
thumbnail A card snippet the theme has
no_index, no_follow Booleans. A new collection is not indexed by search engines unless you send no_index: false
sitemap_changefreq, sitemap_priority
delete true, alone with handle and optionally redirect_to
redirect_to With delete only: an address of this marketplace (/another-address) that the deleted address redirects to permanently (301). Without it, the address answers 404.

A collection’s address cannot be one a page already holds, or one the marketplace reserves.

A collection made by a model takes its conditions, type and online state from the model. Here you can only replace its texts: title, page_title, subtitle, description, image_alt, template, thumbnail, canonical_url, meta_title, meta_description, meta_keywords, no_index, no_follow, sitemap_changefreq, sitemap_priority — null for the model’s, "" for nothing (not for title: a collection always has a name). Writing the same collection here and under its model in one blueprint is refused (written twice).

Deleting: delete: true on the entry, or prune (which deletes the storefront collections without a model that the blueprint leaves out). Neither reaches a collection made by a model (it leaves through its model) or a collection without a storefront page.

{
"inclusion": { "type": "AND", "rules": [{ "type": "…", "operator": "…", "value": "…", "value_2": "…", "field": "…" }] },
"exclusion": [{ "type": "…", "operator": "…", "value": "…" }]
}

A collection presents the items matching its inclusion conditions — all of them (AND) or any one (OR) — minus those matching one of its exclusions. Empty conditions (no inclusion rule and no exclusion) are stored as no conditions.

type What it compares
title The listing’s title
category Category ids (subcategories included)
vendor Seller user ids
seller_group User group ids
order_type Order type ids
shipping_method Shipping method ids
attribute An attribute (field: its alias) and option ids, or numbers
tag, city, country Text values
department Department codes
price, original_price, weight, stock Numbers
location_radius A distance around a point

Operators: is, not, less, more, between (with value_2), starts_with, ends_with, contains, not_contains, is_set, is_not_set, within. Exclusions accept the types that name what an item belongs to: category, tag, vendor, seller_group, order_type, shipping_method, city, country, department.

The simplest way to get conditions right is to read an existing collection with read_item and start from its rules.

content:write. CMS pages, keyed on slug, the page’s address.

{
"pages": [
{ "slug": "a-propos", "title": "Qui sommes-nous ?", "body": "<p>Acme rassemble les hébergements insolites de France.</p>", "is_online": true },
{ "slug": "equipe", "title": "L'équipe", "parent": "a-propos" },
{ "slug": "faq", "is_online": false },
{ "slug": "ancienne-offre", "delete": true, "redirect_to": "/a-propos" }
]
}
Field Type Notes
slug string The key. Derived from title when omitted.
title string Required to create a page; optional to update one
parent string Slug of the parent page, which may be created in the same blueprint. Omit it to leave the page where it is; a page cannot be moved back to the top level here. A parent that does not exist is reported at apply, not in the preview.
body string HTML (see Markup)
page_title, subtitle, template, layout, identifier, locale, meta_title, meta_description, meta_keywords, canonical_url string
is_online, no_index, no_follow, is_searchable boolean
sort_priority integer
delete true Alone with slug and optionally redirect_to
redirect_to string With delete only, as for collections

An update carries only what changes. The slug cannot be changed: another slug is another page. Custom CSS and JavaScript are not writable on a page.

Deleting: delete: true on the entry, or prune. A page that has child pages is never deleted.

content:write. Blog categories and articles, both keyed on slug.

{
"blog": {
"categories": [{ "title": "Actualités", "slug": "actualites" }],
"posts": [
{ "title": "Bienvenue sur Acme", "category": "actualites",
"summary": "Ce que vous trouverez ici.", "body": "<p>…</p>",
"cover": "aurora", "image_alt": "Une cabane au lever du jour", "is_online": true }
]
}
}

The section accepts posts or articles for the article list, and articles may also be nested in their category ("categories": [{ "title": …, "posts": [ … ] }]). A plain list is read as categories only.

Categories

Field Type Notes
title string Required in every entry
slug string The key. Derived from title when omitted. A new title keeps it: unlike in the back office, the address does not follow the title.
description, page_title, locale, meta_title, meta_description, meta_keywords, canonical_url string
no_index, no_follow boolean

Articles

Field Type Notes
title string Required in every entry
slug string The key. Derived from title when omitted. A new title keeps it: unlike in the back office, the address does not follow the title.
category string Required. Slug of a blog category, existing or created in the same blueprint. Used when the article is created. A category that does not exist is reported at apply, not in the preview.
summary, body (HTML), image_alt, meta_title, meta_description string
is_online boolean false when omitted, also on an update. Send true to keep a published article online.
no_index, no_follow boolean
cover string One of the built-in covers aurora, sienna, moss, plum, slate, amber, or an https:// image URL

An article’s publication date is the day it is created, and cannot be set.

Deleting: prune only. It deletes the blog categories the blueprint leaves out (never one that holds articles) and every article it leaves out.

content:write. The links of existing menus, keyed on the menu’s slug (main-menu, footer-menu… as describe_marketplace lists them).

{
"menus": [
{ "menu": "footer-menu", "links": [
{ "title": "Qui sommes-nous ?", "url": "/a-propos" },
{ "title": "Nous écrire", "url": "mailto:contact@example.com" },
{ "title": "Instagram", "url": "https://instagram.com/acme", "new_tab": true }
]}
]
}

An entry gives all the links of the menu, in order, and replaces them; [] empties the menu. A link left out is removed, which is an edit, not a deletion: it does not need marketplace:destroy, and what it pointed to is untouched.

Field Notes
menu Required. Slug of an existing menu. Menus are not created or deleted here.
links Required. At most 50.
links[].title Required. Plain text, 255 characters at most, no < or >
links[].url Required. An address of the marketplace (/contact), http:// or https://, mailto: or tel:. 255 characters at most.
links[].new_tab true or false (default)

Page and article bodies, collection and model descriptions, footer columns and several homepage settings are HTML shown as is on the storefront. Anywhere in a blueprint, a string whose markup would run code is refused, with its path. Nothing is cleaned silently: what would be stored differently from what you wrote is refused.

  • Allowed elements: p, br, hr, span, div, section, article, header, footer, aside, h1–h6, strong, b, em, i, u, s, small, sub, sup, mark, abbr, cite, q, code, pre, kbd, del, ins, time, blockquote, address, figure, figcaption, ul, ol, li, dl, dt, dd, table, caption, thead, tbody, tfoot, tr, th, td, colgroup, col, a, img, picture, source. Any other element is refused.
  • Refused attributes: any on… event attribute, srcdoc, formaction, xlink:href; a javascript:, vbscript: or data: URL in href, src, srcset and other URL attributes; a style using expression(, behavior: or -moz-binding. A value that is itself a javascript:, vbscript: or data: URL is refused wherever it appears (a canonical_url, a setting).
  • Settings: a value without any element that holds a quote followed by name= is refused, because some settings are printed inside an HTML attribute.
pages[0].body: contains a <script> element. Not writable through MCP: markup that runs code is added by the marketplace admin, by hand, in the back-office editor of this item, not by an agent. Write it without, or tell the admin to add it there.

A refused blueprint comes back as a tool error (isError: true) whose text starts with the path of the problem. Nothing is written. The most frequent ones:

Message Cause
<section>: unknown section. Supported: … The section does not exist (a theme section, for example)
<path>: unknown field(s): …. Writable: … A field the section does not write
<path>.title: is required. Missing title where every entry needs one
<path>.slug: duplicate slug "…". Two entries resolve to the same key; give one an explicit slug
listing_attributes[0].options: an attribute of type "select" needs at least one option. select or checkbox without options (remember input_type defaults to select)
categories[0].children[0].children[0].children: categories may be nested 3 levels deep at most; … A fourth level
order_types[0].supplier_fees_percentage: cannot exceed 100. Out-of-range commission
settings.Theme.custom_js: not writable through MCP: … A theme setting outside the writable list; the message names the back-office screen
settings.Stripe.mode: this key is not writable through MCP. … A setting outside the writable names
settings.<name>: the value looks like a …; credentials do not belong in settings. A credential-shaped value
pages[faq].title: is required to create a page; no page has this slug yet. A new page without a title (often a mistyped slug)
pages[<slug>].delete: 2 child page(s) sit under it. Delete or move them first. Deleting a page that has children
collections[<handle>].redirect_to: only goes with delete: true. redirect_to on an entry that is not deleted
collections[<handle>]: … written on the model "…" this collection is made by … Conditions, type or online state on a model’s collection
menus[<slug>]: no such menu. Menus: main-menu, footer-menu. Menus are not created here. Unknown menu
blog.posts[0].category: is required: name the slug of the category this article belongs to. Article without a category

Errors from the collection and model rules (a held address, an unknown template, a missing pattern…) come back in the same format, with the rule’s own message.