Theme App Extensions
Register UI blocks that merchants can place anywhere in their Liquid themes — without modifying theme files.
Architecture
App Blocks allow OAuth apps to inject UI into merchant storefronts by registering a block definition. The merchant activates the block in the theme editor, which adds a section reference with type @app/{app_slug}/blocks/{block_slug} to the page template. At render time, Cartly resolves this reference and renders the block's Liquid template (or iframe/remote URL) in place.
Render Types
liquid— Liquid template string rendered server-side by liquidjsembedded— iframe pointing torender_url(same as App Bridge)remote— server-side fetch fromrender_url, HTML injected into page
Lifecycle
- Register — App calls
POST /apps/api/theme-extensionswith block definition (OAuth token required). - Activate — When a merchant installs the app,
ActivateBlocksForInstallis called, creatingAppBlockInstallrecords per shop. - Place — Merchant adds the block to a page template via the theme editor. Section type is stored as
@app/{app_slug}/blocks/{block_slug}. - Render — Storefront resolves the section type, looks up the
AppBlock, and renders via the declared render type. - Uninstall cleanup —
DeactivateBlocksForUninstallremovesAppBlockInstallrecords andCleanBlockReferencesFromPagespurges all@app/...references from page templates.
POST
/apps/api/theme-extensionsoauthRegister an app block. The app_slug and block_type_slug form the unique section type used in page templates.
Request Body
json
{
"block_type_slug": "product-upsell",
"category": "product",
"description": "Shows a recommended product below the add-to-cart button",
"liquid_template": "<div class=\"upsell\">{{ block.settings.title }}</div>",
"name": "Product Upsell",
"render_type": "liquid",
"schema_json": {
"settings": [
{
"id": "title",
"label": "Heading",
"type": "text"
}
]
}
}GET
/admin/app-blocksadmin-jwtList all app blocks available to the merchant (activated blocks from installed apps).
Code Examples
javascript
// Register a Liquid app block
const res = await fetch('https://cartly.pro/apps/api/theme-extensions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${oauthToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
block_type_slug: 'product-upsell',
name: 'Product Upsell',
render_type: 'liquid',
liquid_template: '<div>{{ block.settings.heading }}</div>',
schema_json: {
settings: [{ type: 'text', id: 'heading', label: 'Heading', default: 'You may also like' }],
},
}),
});