Embeddable post grid (widget)
A <script> tag that renders a grid of your published posts inside a page you
do not control — a partner’s site, a landing page, an app shell. It reads the
public read API and builds the DOM itself. No iframe.
The bundle is 4.4 KB gzipped and pulls in nothing else.
The snippet
Section titled “The snippet”-
Create a public read key.
In the admin, open Configurações → Integrações API → Criar chave, pick Pública de leitura (
pk_), and register the origins where the widget may run (for examplehttps://parceiro.comandhttps://www.parceiro.com). Thepk_…token is shown once. -
Paste the snippet.
<div data-canverly-grid data-key="pk_YOURKEY" data-limit="6"></div><script src="https://docs.canverly.com/widget/v1/grid.js"integrity="sha384-pHV9vZdn2P2/wjNayBRVklmrBLFKkPT5Hz6ZAHiodvsxUo4C9Vr5pbxdIbje3MEF"crossorigin="anonymous" defer></script>Keep the
integrityandcrossoriginattributes. They are what makes the browser refuse the file if a single byte of it ever differs from what you audited. -
Check the origin.
If the grid does not appear, open the console.
[canverly-grid] origem não está na allowlist da chavemeans the page’s origin is not registered on the key — add it in the admin. The API answers 403 in that case: the key is valid, the page it is used from is not.
Attributes
Section titled “Attributes”| Attribute | Default | Meaning |
|---|---|---|
data-key |
— | Required. The pk_… key. data-site is accepted as an alias. |
data-limit |
6 |
1–50. Out-of-range values are clamped locally with a console warning. |
data-category |
— | Category slug. |
data-tag |
— | Tag slug. |
data-language |
— | BCP-47 tag, e.g. pt-BR. |
data-locale |
<html lang> or pt-BR |
Locale for dates and labels. |
data-link-target |
_self |
Use _blank to open posts in a new tab (links always carry rel="noopener noreferrer"). |
data-empty-text |
localized | Message shown when the site has no matching posts. |
data-api |
https://api.canverly.com |
API base. Only https: (or localhost) is accepted. |
Several grids can live on the same page; each [data-canverly-grid] element is
rendered independently. The current state is exposed as data-state:
loading, ready, empty or error.
Content Security Policy
Section titled “Content Security Policy”If the host page sends a CSP, it needs three sources:
script-src https://docs.canverly.comconnect-src https://api.canverly.comimg-src https://canverly.b-cdn.netStyles need no CSP change: the widget ships its CSS inside the bundle and
applies it through a constructable stylesheet, which style-src 'self' does not
block. Only the legacy fallback loads grid.css as a <link>, and that needs
style-src https://docs.canverly.com. Every class is prefixed cvw-, so
nothing leaks into the host page’s styles.
Safety of what it renders
Section titled “Safety of what it renders”The widget never assigns HTML. Every value coming from the API goes in through
createElement + textContent, and every URL is scheme-checked before it
reaches the DOM. A post titled <script>alert(1)</script> renders as that
literal text, visibly — it does not execute, and it does not silently disappear.
This is enforced by tests that assert on the resulting DOM (no <script> node,
no extra <img>, the title node has zero child elements) plus a mutation test
that fails if anyone swaps textContent for innerHTML.
Limits
Section titled “Limits”- 600 requests/minute per key, plus 60 requests/minute per (key, IP) for public keys. The per-IP dimension exists so a single hostile visitor cannot drain the quota of every page that embeds your grid.
- One page view = one request. The widget renders a single page of results and
ignores
next_cursoron purpose. - On any failure the widget renders nothing visible and logs to the console. It never throws into the host page.
Versioning
Section titled “Versioning”The version is in the path, and the bytes at a published path never change.
/widget/v1/grid.js will always be the file whose hash is above; a new build is
published under a new path. That is what lets the snippet pin integrity and
what lets us serve the file with a one-year immutable cache. If we ever ship a
breaking change, it becomes /widget/v2/grid.js and this page gets a new
snippet — the one you pasted keeps working.
Current hashes are published next to the file at
/widget/v1/INTEGRITY.txt.