The Cellpy custom element interface and REST API reference.
Cellpy exposes two surfaces for programmatic control: the custom element API for interacting with rendered blocks on the page, and the REST management API for managing blocks, containers, and tokens from your backend or CI pipeline.
Attributes, events, and JavaScript methods on the <cellpy-block> element.
Programmatically manage blocks, containers, and tokens via HTTPS.
REST API requests require a Bearer token.
Create an API token in the Cellpy dashboard under Account → API Tokens. Include it in every REST request as an Authorization header:
curl https://api.cellpy.com/v1/blocks \
-H "Authorization: Bearer your_token_here"| Name | Type | Description |
|---|---|---|
Base URL | | https://api.cellpy.com/v1 |
Format | | All requests and responses use JSON (Content-Type: application/json). |
Authorization | header | Bearer <token> |
The <cellpy-block> HTML custom element — attributes, CSS variables, and events.
The runtime registers the <cellpy-block> custom element when https://cdn.cellpy.com/runtime.js is loaded. Each element manages its own Shadow DOM and fetches the assigned block from the CDN.
| Name | Type | Description |
|---|---|---|
slugrequired | string | The container slug from your dashboard. This is the permanent embed identifier — the assigned block can change without modifying the attribute. |
env | string | 'production' (default) | 'staging'. Controls which published version is loaded. |
cache | number | Cache TTL override in seconds. Overrides the plan-level CDN cache. Use with caution in production. |
loading | string | 'eager' (default) | 'lazy'. 'lazy' defers loading until the element is near the viewport. |
Blocks expose CSS custom properties for theming. Set them on the element or any ancestor — they cascade into the Shadow DOM as inherited properties.
<!-- On the element -->
<cellpy-block slug="pricing" style="--cellpy-primary: #e11d48;"></cellpy-block>
<!-- On an ancestor (e.g. your brand theme) -->
<div style="--cellpy-primary: #6366f1;">
<cellpy-block slug="hero-main"></cellpy-block>
<cellpy-block slug="cta-footer"></cellpy-block>
</div>| Name | Type | Description |
|---|---|---|
cellpy:load | CustomEvent | Fires when the block has finished loading and is rendered. detail: { blockId, version, slug }. |
cellpy:error | CustomEvent | Fires if the block fails to load. detail: { slug, status, message }. |
cellpy:refresh | CustomEvent | Fires after a manual refresh() call completes. |
Imperative control of cellpy-block elements from JavaScript.
Every <cellpy-block> element exposes a JavaScript API. Access it like any custom element:
const block = document.querySelector('cellpy-block[slug="pricing"]')
// Force a fresh fetch from the CDN, bypassing cache
await block.refresh()
// Get info about the currently loaded block
const info = block.blockInfo
// → { blockId: "abc123", version: 3, slug: "pricing", env: "production" }
// Listen for load events
block.addEventListener('cellpy:load', (e: CustomEvent) => {
console.log('Block loaded:', e.detail.blockId)
})
// Switch environments at runtime
block.setAttribute('env', 'staging')| Name | Type | Description |
|---|---|---|
refresh() | () => Promise<void> | Force reload the block from the CDN, bypassing the browser cache. |
blockInfo | BlockInfo | null | Returns { blockId, version, slug, env } once loaded, null while loading or on error. |
shadowRoot | ShadowRoot | The element's Shadow DOM root — for advanced DOM inspection only. Do not mutate. |
refresh() method respects your CDN cache TTL — calling it rapidly won't bypass the CDN. For instant updates during development, use the env="staging" attribute, which has no CDN caching.Manage blocks in your Workspace programmatically.
/blocksList all blocks in your Workspace. Supports ?status=pending|saved|archived and ?q= for keyword search.
/blocks/:idGet a single block by ID. Returns full HTML, CSS, metadata, and version history.
/blocksCreate a new block in your Workspace (equivalent to 'Push to Workspace').
/blocks/:idUpdate an existing block's HTML, CSS, name, or status.
/blocks/:idArchive a block. Archived blocks are not deleted — they can be restored.
# Create a block (push to Workspace)
curl -X POST https://api.cellpy.com/v1/blocks \
-H "Authorization: Bearer your_token_here" \
-H "Content-Type: application/json" \
-d '{
"name": "pricing-cards",
"html": "<div class=\"card\">...</div>",
"css": ".card { padding: 24px; }",
"environment": "staging"
}'
# Response
{
"id": "blk_x7qP2kLm",
"name": "pricing-cards",
"status": "pending",
"environment": "staging",
"createdAt": "2026-06-04T10:00:00Z"
}| Name | Type | Description |
|---|---|---|
id | string | Unique block ID. Prefixed with blk_. |
name | string | Human-readable name for the block in your Workspace. |
html | string | The block's HTML markup. |
css | string | The block's CSS. Always scoped to Shadow DOM at render time. |
status | string | 'pending' | 'saved' | 'archived'. |
environment | string | 'staging' | 'production'. |
version | number | Increments with each update. The CDN serves the latest saved version. |
createdAt | string | ISO 8601 timestamp. |
updatedAt | string | ISO 8601 timestamp of the most recent update. |
Manage container assignments and publishing.
/containersList all containers in your organization.
/containers/:slugGet a container's current state — assigned block, live version, and embed metadata.
/containers/:slugUpdate the assigned block and/or trigger a publish to production.
# Assign a block and publish to production
curl -X PATCH https://api.cellpy.com/v1/containers/pricing \
-H "Authorization: Bearer your_token_here" \
-H "Content-Type: application/json" \
-d '{
"blockId": "blk_x7qP2kLm",
"publish": true
}'
# Response
{
"slug": "pricing",
"blockId": "blk_x7qP2kLm",
"liveVersion": 1,
"publishedAt": "2026-06-04T10:05:00Z",
"cdnUrl": "https://cdn.cellpy.com/b/blk_x7qP2kLm/v1.js"
}Manage API tokens programmatically.
/tokensList all API tokens for your account (secrets are never returned after creation).
/tokensCreate a new API token. The secret is returned only once in the response.
/tokens/:idRevoke a token immediately. All requests using that token will return 401.
How blocks are served to end users.
When a container is published, Cellpy generates an immutable versioned bundle and stores it on Cloudflare's edge network. The runtime fetches this bundle for each <cellpy-block> element.
| Name | Type | Description |
|---|---|---|
CDN URL format | | https://cdn.cellpy.com/b/:blockId/v:version.js |
Cache-Control | | Immutable bundles: max-age=31536000, immutable. Container resolution: max-age varies by plan (1h – 24h). |
Edge locations | | Cloudflare's global network — 300+ PoPs. Sub-100ms TTFB from most locations. |
Staging CDN | | Staging blocks use no-store, no-cache — every request fetches a fresh version. Use staging for development. |
# Fetch block metadata (no auth required for public containers)
GET https://cdn.cellpy.com/c/:container-slug/meta.json
# Response
{
"slug": "pricing",
"blockId": "blk_x7qP2kLm",
"version": 3,
"bundleUrl": "https://cdn.cellpy.com/b/blk_x7qP2kLm/v3.js",
"updatedAt": "2026-06-04T10:05:00Z"
}| Name | Type | Description |
|---|---|---|
400 | Bad Request | The request body is malformed or missing required fields. |
401 | Unauthorized | Missing or invalid API token. |
403 | Forbidden | Your token doesn't have permission for this resource or action. |
404 | Not Found | The requested block, container, or token does not exist. |
409 | Conflict | A container slug or block name already exists. |
422 | Unprocessable Entity | Validation failed — check the errors array in the response body. |
429 | Too Many Requests | Rate limit exceeded. See Retry-After header. |
500 | Internal Server Error | Unexpected server error. Retry with exponential back-off. |
{
"error": "validation_failed",
"message": "Block name is required",
"errors": [
{ "field": "name", "message": "Required" }
]
}| Name | Type | Description |
|---|---|---|
Free plan | | 60 requests / minute per token. |
Pro plan | | 300 requests / minute per token. |
Enterprise plan | | Custom. Contact sales. |
Retry-After | header | Seconds until the rate limit window resets — included in 429 responses. |