API Admin › Recursos
Temas
Os temas da biblioteca da loja: criar, baixar e gravar os arquivos, enviar imagens e fontes, guardar versões, abrir uma prévia, conferir e publicar. São as rotas que o CLI de temas usa, e servem a qualquer automação de agência.
| Escopo | O que abre |
|---|---|
read_themes | ler os temas, os arquivos, as versões, a conferência e o zip |
write_themes | criar, renomear e excluir temas, gravar arquivos, versões e prévias |
publish_themes | publicar um tema (só junto com write_themes; o painel o marca sensível) |
A biblioteca tem no máximo 20 temas, e a vitrine mostra um deles, o publicado. Nada do que a API grava aparece na vitrine antes da publicação. Depois de publicar, a vitrine mostra a mudança em até 60 segundos.
O que a integração cria, grava e publica aparece no painel "pela integração", com o nome dela; o que o login do CLI pela conta grava fica no nome da pessoa.
O objeto tema
{
"id": "5b0d6f1e-...",
"name": "Tema da agência",
"source": "custom",
"platform_theme": "pilar",
"published": false,
"tree": "9f2c4e0b8a7d6c5e4f3a2b1c0d9e8f7a",
"errors": 0,
"development": false,
"expires_at": null,
"github": {
"repository": "agencia/tema-da-loja",
"branch": "main",
"folder": "tema",
"url": "https://github.com/agencia/tema-da-loja",
"status": "active",
"last_commit": {
"sha": "4f1c2b9e0d8a7c6b5a4f3e2d1c0b9a8f7e6d5c4b",
"message": "Acrescenta o selo"
},
"synced_at": "2026-10-03T18:04:00.000Z"
},
"pull_request": null,
"created_at": "2026-10-03T18:00:00.000Z",
"updated_at": "2026-10-03T18:05:00.000Z"
}source:platformé um tema da plataforma, com o código só para leitura (a loja muda o conteúdo pelo painel);customtem o código na loja, e os arquivos se gravam pela API.platform_theme: o tema da plataforma de onde vem o código. Nocustom, de qual ele nasceu (nullquando veio de um zip).tree: o estado dos arquivos em trabalho, que muda a cada gravação (nullno tema da plataforma).errors: quantos erros a conferência do código achou. Com erro, o tema não é publicado.development: é um tema de desenvolvimento, do CLI ou da prévia de um pull request (ver Temas de desenvolvimento). Fica fora da biblioteca e nunca é publicado.expires_at: quando o tema de desenvolvimento vence (nullnos outros). Cada gravação empurra a data.github: o tema conectado a uma branch do GitHub (ver O tema no GitHub): o repositório (dono/nome), a branch, a pasta do tema no repositório (vazia é a raiz), o endereço do repositório, a situação e o último commit sincronizado.statuséactiveoupaused(o App perdeu o acesso: foi desinstalado, ou o repositório ou a branch sumiu).nullno tema sem conexão. O tema conectado tem o código só para leitura pela API (ver Os arquivos).pull_request: no tema de desenvolvimento da prévia de um pull request, o número, o repositório e o endereço dele;nullnos outros.
Os temas
GET /themes
A biblioteca inteira, o publicado primeiro: { "data": [tema...], "next_cursor": null }. Cabe numa página. Os temas de desenvolvimento ficam de fora; ?development=only lista só eles, e ?development=include, a biblioteca e eles.
GET /themes/{id}
Um tema.
POST /themes
Acrescenta um tema à biblioteca. Responde 201 com o tema.
{
"source": "fork",
"theme_id": "<id do Pilar da loja>",
"name": "Tema da agência"
}source | O que cria |
|---|---|
platform | um tema da plataforma (platform_theme: pilar, editorial, vertice, aura, colheita ou estante), num estilo (style, opcional) |
duplicate | a cópia de um tema da biblioteca (theme_id), com o código e o conteúdo dele |
fork | a cópia de um tema da plataforma da biblioteca (theme_id) com o código inteiro na loja, para gravar os arquivos |
name é opcional (até 60 caracteres) e não se repete na loja; sem ele, a API escolhe um livre.
PATCH /themes/{id}
Renomeia: { "name": "Novo nome" }. Nome repetido: 409.
DELETE /themes/{id}
Exclui o tema (204). O publicado, ou o que tem publicação agendada no painel, não sai (409).
Os arquivos
O tema conectado a uma branch do GitHub (ver O tema no GitHub), e o tema da prévia de um pull request, têm o código só para leitura: a gravação de arquivos, o envio de assets, a troca pelo zip e a restauração de uma versão respondem 409 theme_connected. A leitura e a publicação continuam iguais. O campo github do tema diz se ele está conectado, e pull_request, se é a prévia de um pull request.
GET /themes/{id}/files
A lista dos arquivos, com o checksum de cada um (a base da gravação) e os problemas da conferência:
{
"theme_id": "5b0d6f1e-...",
"read_only": false,
"tree": "9f2c4e0b8a7d6c5e4f3a2b1c0d9e8f7a",
"files": [
{
"path": "layout/theme.liquid",
"checksum": "1a2b...",
"size": 2048,
"binary": false,
"url": null
},
{
"path": "assets/logo.png",
"checksum": "3c4d...",
"size": 5120,
"binary": true,
"url": "https://..."
}
],
"problems": [
{
"code": "unknown_snippet",
"file": "sections/banner.liquid",
"line": 3,
"column": 5,
"end_line": 3,
"end_column": 28,
"message": "O snippet botao não existe em snippets/",
"severity": "error"
}
]
}O checksum são 32 caracteres hexadecimais (os 128 primeiros bits do SHA-256 do conteúdo). Os arquivos de um tema da plataforma vêm com read_only: true.
Os problemas vêm do verificador de temas, o mesmo do editor do painel:
code: a regra, estável e em inglês (unknown_snippet,missing_translation...). É por ele que o seu programa decide; a lista das regras, com a explicação e o conserto de cada uma, está no guia do verificador.severity:errorimpede publicar;warningsó informa.lineecolumncomeçam em 1;end_lineeend_columnmarcam o fim do trecho, quando se sabe. Problema do tema inteiro (um template que falta) vem comfileelinenulos.- Os problemas guardados antes do verificador vêm com
code,column,end_lineeend_columnnulos até o tema ser conferido de novo (ochecke a publicação conferem).
GET /themes/{id}/files/content?path=...
O conteúdo de um arquivo: { "path", "checksum", "binary", "content", "url" }. O texto vem em content; imagem, fonte e vídeo vêm pelo url, na CDN.
PUT /themes/{id}/files
Grava, apaga e move arquivos de texto, tudo ou nada:
{
"write": [
{ "path": "sections/banner.liquid", "content": "...", "base": "1a2b..." },
{ "path": "snippets/selo.liquid", "content": "...", "base": null }
],
"delete": [{ "path": "snippets/velho.liquid", "base": "5e6f..." }],
"move": [
{
"from": "snippets/a.liquid",
"to": "snippets/b.liquid",
"base": "7a8b..."
}
]
}base é o checksum que foi lido (null para arquivo novo). Se algum arquivo mudou desde a leitura (outra pessoa no editor do painel, outro processo), nada é gravado e a resposta é 409 file_conflict, com os conflitos e o checksum de agora (null: o arquivo não existe mais):
{
"error": {
"type": "conflict",
"code": "file_conflict",
"message": "Estes arquivos mudaram desde a leitura. Leia de novo e grave com o checksum de agora.",
"request_id": "...",
"conflicts": [
{ "path": "sections/banner.liquid", "current_checksum": "9d0e..." }
]
}
}- O corpo tem no máximo 1 MB: mande os arquivos em partes. Acima disso,
413 payload_too_large. - Imagens, fontes e vídeos vão por
POST /themes/{id}/assets. - Gravar num tema da plataforma é
409 theme_read_only: crie umfork. - O código com erro é gravado (a resposta traz os
problems); só não é publicado.
A resposta é a lista dos arquivos, como em GET /themes/{id}/files.
POST /themes/{id}/assets
Envia arquivos para assets/, em multipart/form-data: até 20 no campo files, de até 10 MB cada, gravados em assets/<nome do arquivo>. CSS e JS também podem ir por aqui. O campo bases, opcional, é um JSON com os checksums lidos:
curl https://api.denaryx.com/admin/2026-10/themes/<id>/assets \
-H "Authorization: Bearer dnxa_..." \
-F "files=@logo.png" \
-F 'bases={"assets/logo.png": "3c4d..."}'Com as bases, o envio confere como a gravação (409 file_conflict); sem elas, o arquivo com o mesmo nome é substituído.
As versões
O código de um tema custom guarda versões: a cada publicação que mudou o código, a cada zip enviado, antes de restaurar e quando se pede.
GET /themes/{id}/versions
{ "data": [versão...], "next_cursor": null }, da mais nova à mais antiga:
{
"id": "e1f2...",
"number": 4,
"tree": "9f2c...",
"note": "Antes do lançamento",
"reason": "manual",
"created_at": "2026-10-03T18:10:00.000Z",
"current": true
}reason: publish, manual, import, fork, before_restore ou before_import. Ficam as 100 mais recentes, mais as que uma publicação cita. A versão que nasce de um push numa branch do GitHub conectada ao tema vem com reason: import e a nota abc1234: primeira linha do commit.
POST /themes/{id}/versions
Guarda o código de agora como versão, com uma nota opcional ({ "note": "..." }, até 200 caracteres). Responde 201 com a versão.
POST /themes/{id}/versions/{version_id}/restore
O código da versão volta a ser o trabalho do tema; o de antes vira uma versão (before_restore). O conteúdo da loja e a vitrine não mudam. Responde com a lista dos arquivos.
O zip
GET /themes/{id}/export
O zip do código de um tema custom, sem o conteúdo da loja (application/zip).
POST /themes/import
Um tema novo a partir de um zip, em multipart/form-data: o zip no campo file (até 50 MB e 2.000 arquivos) e o nome, opcional, em name. As pastas de um tema são layout, templates, sections, blocks, snippets, assets, config e locales; o zip pode ter tudo dentro de uma pasta. Responde 201, com o tema e, em ignored, cada arquivo que ficou de fora e o motivo:
{ "theme": { "id": "...", "source": "custom", ... }, "ignored": [] }Com development=true, nasce um tema de desenvolvimento, e content_from (opcional) diz de qual tema da loja vem o conteúdo (ver abaixo).
POST /themes/{id}/import
Os arquivos do zip (campo file) substituem os do tema; o código de antes vira uma versão, e o conteúdo da loja fica. Responde 200, no mesmo formato.
A prévia
POST /themes/{id}/previews
Uma prévia do tema na vitrine, antes de publicar:
{ "type": "temporary" }: vale 60 minutos e acompanha o trabalho (cada gravação aparece ao recarregar). É a do desenvolvimento.{ "type": "shared", "name": "Para o cliente", "expires_in_days": 7, "frozen": true }: o link para mandar ao cliente, com validade de 1, 7 ou 30 dias. Congelado (frozen), mostra para sempre o código e o conteúdo de agora; senão, acompanha o trabalho. Aparece no painel, em Temas, onde se revoga.
Responde 201:
{
"url": "https://minhaloja.denaryx.store/previa?token=...",
"expires_at": "2026-10-10T18:00:00.000Z",
"shared": {
"id": "...",
"name": "Para o cliente",
"frozen_at": "2026-10-03T18:00:00.000Z",
"created_at": "2026-10-03T18:00:00.000Z"
}
}O url leva o token e aparece só nesta resposta. A loja tem no máximo 20 links compartilhados ativos.
Conferir e publicar
GET /themes/{id}/check
O que o painel mostra em "Antes de publicar":
{
"theme": { ... },
"can_publish": true,
"problems": [],
"content_impact": [{ "where": "Ajustes gerais", "change": "O ajuste \"cor_destaque\" não existe mais no tema" }],
"missing_templates": [{ "template": "product", "name": "presente", "count": 3 }],
"live": { "theme_name": "Pilar", "published_at": "2026-09-30T12:00:00.000Z" }
}can_publish: o token tempublish_themese o código não tem erro.content_impact: o conteúdo da loja que o código deste tema deixa de ler (a seção, o bloco ou o ajuste que não existe mais, ou o valor que volta ao padrão).missing_templates: os modelos de página que produtos, categorias e páginas usam e o tema não tem; com ele no ar, essas páginas usam o modelo padrão.live: o que a vitrine mostra agora (nullse a loja nunca publicou).
POST /themes/{id}/publish
Pede o escopo publish_themes, para qualquer tema (o da plataforma também troca o que o comprador vê). O tema vai para a vitrine com o código e o conteúdo de agora, e o painel guarda a publicação para a loja voltar a ela. Corpo opcional: { "note": "Lançamento" } (até 200 caracteres). Responde com o tema.
Código com erro não vai ao ar:
{
"error": {
"type": "invalid_request",
"code": "theme_has_errors",
"message": "O código do tema tem 1 erro. Corrija antes de publicar.",
"request_id": "...",
"problems": [
{
"code": "unknown_snippet",
"file": "sections/banner.liquid",
"line": 3,
"column": 5,
"end_line": 3,
"end_column": 28,
"message": "O snippet botao não existe em snippets/",
"severity": "error"
}
]
}
}O tema de desenvolvimento nunca é publicado: 409 development_theme.
Agendar a publicação e voltar a uma publicação anterior ficam no painel nesta versão.
Temas de desenvolvimento
O tema de desenvolvimento é o de uma pessoa (ou de uma integração) trabalhando no código, como o denaryx tema dev faz (Desenvolver com o tema dev): cada um no seu, sem mexer no tema que o cliente está aprovando nem na vitrine.
- Nasce por
POST /themes/importcomdevelopment=true: o código é o do zip, e o conteúdo (as seções e os ajustes montados no editor do painel) é a cópia do conteúdo do temacontent_from, lido pelo código novo. Semcontent_from, o padrão do tema enviado. O nome é opcional e, se já existir, ganha um número. - Não conta nos 20 da biblioteca: são até 5 por integração (ou pessoa) e 30 por loja; acima disso,
409 development_theme_limit. - Grava-se como qualquer tema
custom(PUT .../files,POST .../assets, a prévia), e cada gravação empurra oexpires_atpara 7 dias depois. - Não é publicado nem agendado (
409 development_theme). - Vence 7 dias depois da última gravação e sai na limpeza diária, com as prévias e as versões dele.
- Não aparece no
GET /themessem o?development. O painel o mostra na página Temas, em "Em desenvolvimento". - A prévia de um pull request também é um tema de desenvolvimento: o App da Denaryx no GitHub cria um para cada pull request aberto para a branch de um tema conectado (A prévia por pull request). Ele vem com o
pull_request, o código muda só pelo push no pull request (a gravação pela API responde409 theme_connected), conta nos 30 da loja e não nos 5 de cada um (até 10 por conexão), e o link dele conta nos 20 links da loja.
O ciclo do CLI
O CLI de temas (denaryx) faz este ciclo por você, com o verificador sem rede, as bases guardadas e os envios em partes. Para uma automação própria:
GET /token: confere a integração e os escopos.GET /themese, para começar,POST /themescomsource: fork(ouPOST /themes/importcom o zip da agência).GET /themes/{id}/fileseGET .../files/content?path=para baixar; guarde ochecksumde cada arquivo.PUT /themes/{id}/filescom as bases, em partes de até 1 MB;POST .../assetspara os binários. Num409 file_conflict, leia de novo os arquivos dos conflitos e decida.POST /themes/{id}/previewscomtemporarypara ver na vitrine. Para cada pessoa trabalhar sem atropelar as outras, um tema de desenvolvimento (POST /themes/importcomdevelopment=true).GET /themes/{id}/checkePOST /themes/{id}/publish.
Mande Idempotency-Key nas escritas (Escrita repetível): numa queda de rede, repetir a chamada não grava duas vezes. Vale também para os envios de arquivos.