Templates

Gerencie os templates de WhatsApp da conta Meta ligada à conexão: listagem com status e categoria, detalhe com nota de qualidade, criação repassando o corpo da Graph API, edição, exclusão e sincronização sob demanda. Tudo fica restrito à WABA da conexão da sua chave. Exige plano Enterprise e os escopos `templates:read` / `templates:write`.

escopo: templates:readEnterprise

Lista os templates da conexão. Filtros: ?status=APPROVED|PENDING|REJECTED|PAUSED|DISABLED&category=MARKETING|UTILITY|AUTHENTICATION&name=<exato>&language=pt_BR&connection_id=<uuid>&limit=50 (até 200), por cursor: a continuação repete a consulta com &cursor=<pagination.next_cursor> até ele vir nulo. Com ?refresh=true a Prosalab sincroniza com a Meta antes de responder (use com parcimônia; a sincronização automática já roda a cada 6 h e os webhooks da Meta atualizam status e categoria na hora).

  • Sem `connection_id`, a API usa a única conexão pela API oficial da Meta da empresa. Com mais de uma, responde 422 `CONNECTION_REQUIRED` listando as opções.
  • Templates criados direto no WhatsApp Manager aparecem aqui depois da sincronização (automática a cada 6 h, ou imediata com `?refresh=true`).
  • Só conexões conectadas participam. Uma conexão desconectada não lista nem altera templates, mesmo que a WABA ainda tenha token guardado.
  • Ordem: `created_at` crescente (do template mais antigo ao mais novo) com desempate por `id`; template criado durante a leitura entra no fim. `?limit=1` sem cursor devolve o primeiro template e, se houver mais, `next_cursor`.

Resposta (200)

Corpo da resposta
{
  "success": true,
  "data": [
    {
      "id": "<uuid>",
      "meta_template_id": "1234567890",
      "name": "ailou_alerta_v1",
      "language": "pt_BR",
      "category": "MARKETING",
      "status": "APPROVED",
      "components": [
        {
          "type": "BODY",
          "text": "Alerta · *{{1}}*",
          "example": {
            "body_text": [
              [
                "Cancelamento"
              ]
            ]
          }
        }
      ],
      "connection_id": "<uuid>",
      "rejected_reason": null,
      "rejected_code": null,
      "submitted_at": "2026-10-23T12:52:30.000Z",
      "status_updated_at": "2026-10-23T12:57:47.000Z",
      "last_synced_at": "2026-10-23T13:00:02.000Z",
      "created_at": "2026-10-23T12:52:30.000Z",
      "updated_at": "2026-10-23T13:00:02.000Z"
    }
  ],
  "pagination": {
    "limit": 50,
    "next_cursor": null
  },
  "connection": {
    "id": "<uuid>",
    "name": "Produção",
    "identifier": "+55 24 99327-4061"
  }
}

Erros

StatusCódigoQuando ocorre
400INVALID_PARAMETERstatus ou category fora dos valores aceitos; `page`, `offset`, `per_page`, `page_size` ou `count=exact` (a leitura é por cursor); ou cursor adulterado ou de outra lista
404NOT_FOUNDconnection_id não pertence à empresa
422CONNECTION_REQUIREDEmpresa sem conexão Meta, ou com mais de uma e sem connection_id
422TEMPLATES_UNSUPPORTEDA conexão não é pela API oficial da Meta (ex.: UAZAPI) ou está sem credenciais
502PROVIDER_ERRORFalha na Meta durante o refresh

Exemplo

curl
curl -s -H "Authorization: Bearer <sua-key>" https://www.prosalab.com/api/v1/templates
escopo: templates:readEnterprise

Detalhe de um template pelo nome. Parâmetros: ?language=pt_BR (padrão pt_BR), &connection_id=<uuid>, &refresh=true para consultar a Meta na hora e trazer `quality_score`, `meta_rejected_reason` e `previous_category`.

  • `quality_score`, `meta_rejected_reason` e `previous_category` só vêm preenchidos com `?refresh=true`; sem ele são `null`.

Resposta (200)

Corpo da resposta
{
  "success": true,
  "data": {
    "id": "<uuid>",
    "meta_template_id": "1234567890",
    "name": "ailou_alerta_v1",
    "language": "pt_BR",
    "category": "MARKETING",
    "status": "APPROVED",
    "components": [
      {
        "type": "BODY",
        "text": "Alerta · *{{1}}*",
        "example": {
          "body_text": [
            [
              "Cancelamento"
            ]
          ]
        }
      }
    ],
    "connection_id": "<uuid>",
    "rejected_reason": null,
    "rejected_code": null,
    "submitted_at": "2026-10-23T12:52:30.000Z",
    "status_updated_at": "2026-10-23T12:57:47.000Z",
    "last_synced_at": "2026-10-23T13:00:02.000Z",
    "created_at": "2026-10-23T12:52:30.000Z",
    "updated_at": "2026-10-23T13:00:02.000Z",
    "quality_score": "GREEN",
    "meta_rejected_reason": null,
    "previous_category": "UTILITY"
  },
  "connection": {
    "id": "<uuid>",
    "name": "Produção",
    "identifier": "+55 24 99327-4061"
  }
}

Erros

StatusCódigoQuando ocorre
400INVALID_FIELDNome fora do padrão da Meta (letras minúsculas, números e underline)
404TEMPLATE_NOT_FOUNDNenhum template com esse nome e idioma na conexão
422CONNECTION_REQUIREDEmpresa sem conexão Meta, ou com mais de uma e sem connection_id
502PROVIDER_ERRORFalha na Meta durante o refresh

Exemplo

curl
curl -s -H "Authorization: Bearer <sua-key>" https://www.prosalab.com/api/v1/templates/{name}
escopo: templates:writeEnterprise

Cria um template na conta Meta da conexão. O corpo segue o formato da Graph API (name, language, category, components com exemplos e botões) e é repassado sem validação própria da Prosalab: a Meta é quem aceita ou recusa. Em cabeçalho de mídia (IMAGE, VIDEO, DOCUMENT), coloque uma URL https pública em `example.header_handle[0]`; a Prosalab baixa o arquivo, confere o formato, guarda uma cópia e faz o Resumable Upload na Meta, trocando a URL pelo handle. Os envios usam a cópia guardada, que é a URL devolvida em `components`.

  • Campos repassados à Meta além de name/language/category/components: `allow_category_change`, `parameter_format` e `message_send_ttl_seconds`. Qualquer outra chave é ignorada.
  • A Meta pode trocar a categoria na análise (ex.: UTILITY pedido, MARKETING aprovado). Acompanhe pelo webhook `template.status_updated` ou por GET /api/v1/templates/{name}?refresh=true.
  • Template AUTHENTICATION: corpo sem texto, com `add_security_recommendation`, rodapé com `code_expiration_minutes` e botão `OTP`/`COPY_CODE`, exatamente como na Graph API.
  • A Meta reserva por cerca de 30 dias o nome de um template excluído; crie com nome novo (ex.: sufixo de versão) em vez de reaproveitar.
  • A URL da mídia precisa ser https e pública (hosts de rede interna são recusados, inclusive após redirecionamento; até 3 redirecionamentos). Imagem: JPEG, PNG, WebP ou GIF de até 16 MB, convertida para JPEG quando não está no formato da Meta e entregue com até 5 MB. Vídeo: MP4 de até 16 MB. Documento: PDF de até 100 MB. O download tem 30 s de tempo limite (60 s para documento).
  • Se a Meta aceitou mas o registro local falhou, a resposta ainda é 201 com `warning`; não reenvie, o template aparece na próxima sincronização.

Modelo de envio

Corpo da requisição
{
  "name": "ailou_alerta_v2",
  "language": "pt_BR",
  "category": "UTILITY",
  "allow_category_change": true,
  "components": [
    {
      "type": "HEADER",
      "format": "VIDEO",
      "example": {
        "header_handle": [
          "https://cdn.exemplo.com.br/onboarding.mp4"
        ]
      }
    },
    {
      "type": "BODY",
      "text": "Alerta · *{{1}}*\n\nOcorrência: *{{2}}*",
      "example": {
        "body_text": [
          [
            "Cancelamento",
            "Maria cancelou o Plus"
          ]
        ]
      }
    },
    {
      "type": "BUTTONS",
      "buttons": [
        {
          "type": "URL",
          "text": "Abrir no painel",
          "url": "https://admin.exemplo.com.br/{{1}}",
          "example": [
            "https://admin.exemplo.com.br/cobranca"
          ]
        }
      ]
    }
  ],
  "connection_id": "<opcional — UUID da conexão>"
}

Resposta (201)

Corpo da resposta
{
  "success": true,
  "data": {
    "id": "<uuid>",
    "meta_template_id": "1234567890",
    "name": "ailou_alerta_v2",
    "language": "pt_BR",
    "category": "UTILITY",
    "status": "PENDING",
    "components": [],
    "connection_id": "<uuid>",
    "rejected_reason": null,
    "rejected_code": null,
    "submitted_at": "2026-10-23T12:52:30.000Z",
    "status_updated_at": "2026-10-23T12:57:47.000Z",
    "last_synced_at": "2026-10-23T13:00:02.000Z",
    "created_at": "2026-10-23T12:52:30.000Z",
    "updated_at": null
  }
}

Erros

StatusCódigoQuando ocorre
400INVALID_BODYBody não é JSON
400INVALID_FIELDname, language, category, components ou connection_id fora do formato (o campo vem em `field`)
404NOT_FOUNDconnection_id não pertence à empresa
409TEMPLATE_NAME_CONFLICTJá existe template com esse nome e idioma na conexão, ou a Meta recusou por nome em uso/reservado
422CONNECTION_REQUIREDEmpresa sem conexão Meta, ou com mais de uma e sem connection_id
422TEMPLATES_UNSUPPORTEDA conexão não é pela API oficial da Meta
422TEMPLATE_REJECTED_BY_METAA Meta recusou o corpo (erro 100 da Graph API). `provider_error` traz código, subcódigo, mensagem e trace
429RATE_LIMITEDA empresa passou de 20 downloads de mídia de cabeçalho por minuto (somando criação, edição e a tela); nada foi criado. Tente de novo após `Retry-After`
503HEADER_MEDIA_UNAVAILABLEA conversão da imagem do cabeçalho está indisponível no momento; nada foi criado. Tente de novo após `Retry-After`
502HEADER_MEDIA_UPLOAD_FAILEDNão foi possível baixar a URL da mídia, o arquivo não é do formato do cabeçalho, ou a Meta recusou o upload; nada foi criado. `detail` traz o motivo, com o código entre colchetes (ex.: [not_image_page], [too_large], [private_ip_blocked])
502PROVIDER_ERROROutra falha da Meta ao criar; `provider_error` traz o detalhe

Exemplo

curl
curl -s -X POST -H "Authorization: Bearer <sua-key>" -H "Content-Type: application/json" \
  -d '{"name":"ailou_alerta_v2","language":"pt_BR","category":"UTILITY","allow_category_change":true,"components":[{"type":"HEADER","format":"VIDEO","example":{"header_handle":["https://cdn.exemplo.com.br/onboarding.mp4"]}},{"type":"BODY","text":"Alerta · *{{1}}*\n\nOcorrência: *{{2}}*","example":{"body_text":[["Cancelamento","Maria cancelou o Plus"]]}},{"type":"BUTTONS","buttons":[{"type":"URL","text":"Abrir no painel","url":"https://admin.exemplo.com.br/{{1}}","example":["https://admin.exemplo.com.br/cobranca"]}]}],"connection_id":"<opcional — UUID da conexão>"}' \
  https://www.prosalab.com/api/v1/templates
escopo: templates:writeEnterprise

Edita um template aprovado, rejeitado ou pausado: envia `components` novos e/ou pede outra `category`. A Meta reanalisa e o status volta a PENDING. Parâmetros: ?language=pt_BR (padrão pt_BR) e &connection_id=<uuid>. Mídia de cabeçalho por URL funciona como na criação.

  • A Meta limita a frequência de edição (em geral 1 edição a cada 24 h e até 10 vezes por mês por template). Quando recusa, a resposta é 422 `TEMPLATE_REJECTED_BY_META` com o motivo em `provider_error`.
  • Template PENDING não pode ser editado; espere a decisão da Meta.
  • `components` substitui todos os componentes do template na Meta, inclusive o cabeçalho de mídia: reenvie o HEADER (com URL ou handle) junto com o BODY, senão ele some.
  • Troca de `category` em template APPROVED pode ser recusada pela Meta; a resposta traz o motivo em `provider_error`.
  • Se a Meta aceitou mas o registro local falhou, a resposta ainda é 200 com `warning`.

Modelo de envio

Corpo da requisição
{
  "components": [
    {
      "type": "BODY",
      "text": "Alerta · *{{1}}*\n\nOcorrência: *{{2}}*\nDetalhe: {{3}}",
      "example": {
        "body_text": [
          [
            "Cancelamento",
            "Maria cancelou o Plus",
            "Hotmart"
          ]
        ]
      }
    }
  ],
  "category": "UTILITY"
}

Resposta (200)

Corpo da resposta
{
  "success": true,
  "data": {
    "id": "<uuid>",
    "meta_template_id": "1234567890",
    "name": "ailou_alerta_v1",
    "language": "pt_BR",
    "category": "UTILITY",
    "status": "PENDING",
    "components": [],
    "connection_id": "<uuid>",
    "rejected_reason": null,
    "rejected_code": null,
    "submitted_at": "2026-10-23T12:52:30.000Z",
    "status_updated_at": "2026-10-23T14:10:00.000Z",
    "last_synced_at": "2026-10-23T13:00:02.000Z",
    "created_at": "2026-10-23T12:52:30.000Z",
    "updated_at": "2026-10-23T14:10:00.000Z"
  }
}

Erros

StatusCódigoQuando ocorre
400MISSING_FIELDBody sem components e sem category
400INVALID_FIELDcomponents ou category fora do formato
404TEMPLATE_NOT_FOUNDNenhum template com esse nome e idioma na conexão
409TEMPLATE_NOT_EDITABLEStatus atual não permite edição (só APPROVED, REJECTED, PAUSED) ou template sem id da Meta
422TEMPLATE_REJECTED_BY_METAA Meta recusou a edição; `provider_error` traz o detalhe
429RATE_LIMITEDCota de downloads de mídia de cabeçalho da empresa esgotada, como na criação; nada foi alterado
503HEADER_MEDIA_UNAVAILABLEConversão da imagem do cabeçalho indisponível no momento, como na criação; nada foi alterado
502HEADER_MEDIA_UPLOAD_FAILEDFalha ao baixar, validar ou subir a mídia do cabeçalho; nada foi alterado. `detail` traz o motivo, como na criação
502PROVIDER_ERROROutra falha da Meta

Exemplo

curl
curl -s -X PATCH -H "Authorization: Bearer <sua-key>" -H "Content-Type: application/json" \
  -d '{"components":[{"type":"BODY","text":"Alerta · *{{1}}*\n\nOcorrência: *{{2}}*\nDetalhe: {{3}}","example":{"body_text":[["Cancelamento","Maria cancelou o Plus","Hotmart"]]}}],"category":"UTILITY"}' \
  https://www.prosalab.com/api/v1/templates/{name}
escopo: templates:writeEnterprise

Exclui um template na Meta e no cache da Prosalab. Sem ?language, apaga o nome em todos os idiomas; com ?language=pt_BR, só aquele idioma. Aceita &connection_id=<uuid>.

  • A Meta é a fonte da verdade: a API consulta o nome lá antes de apagar. Se o template já não existe na Meta mas ainda consta no cache, só o registro local é removido e `deleted_on_meta` vem `false`.
  • Se a Meta aceitou mas o registro local não foi removido, a resposta é 200 com `warning`.

Resposta (200)

Corpo da resposta
{
  "success": true,
  "data": {
    "name": "ailou_alerta_v1",
    "language": null,
    "deleted_languages": [
      "pt_BR"
    ],
    "deleted_on_meta": true,
    "connection_id": "<uuid>",
    "note": "A Meta reserva o nome de um template excluído por cerca de 30 dias."
  }
}

Erros

StatusCódigoQuando ocorre
404TEMPLATE_NOT_FOUNDTemplate não existe nem na Prosalab nem na Meta
422TEMPLATE_REJECTED_BY_METAA Meta recusou a exclusão; `provider_error` traz o detalhe
502PROVIDER_ERROROutra falha da Meta

Exemplo

curl
curl -s -X DELETE -H "Authorization: Bearer <sua-key>" https://www.prosalab.com/api/v1/templates/{name}
Templates — Prosalab Developers