GuruAgenda API
menu Guias

Template livre#

Alguns destinos esperam um formato próprio: o webhook de entrada de um CRM que quer {"nome": …, "telefone": …}, um sistema legado, um formulário. Com format: "custom", você escreve o corpo do pedido como um template, e nós o preenchemos com os dados de cada evento.

Exemplo#

{
  "name": "CRM: novos agendamentos",
  "url": "https://crm.example.com/api/leads",
  "events": ["appointment.created"],
  "format": "custom",
  "template": {
    "method": "POST",
    "content_type": "application/json",
    "body": "{\"nome\": {{ data.object.client.name | json }}, \"telefone\": {{ data.object.client.phone | json }}, \"quando\": {{ data.object.start | date('d/m/Y H:i') | json }}, \"evento\": {{ type | json }}}",
    "headers": { "Authorization": "Bearer {{ secret_ref('crm_token') }}" },
    "query": { "origem": "guruagenda" }
  },
  "secrets": { "crm_token": "o-token-do-seu-crm" }
}

O corpo, com um agendamento da Maria Souza, chega assim:

{"nome": "Maria Souza", "telefone": "+5511999999999", "quando": "20/10/2026 14:30", "evento": "appointment.created"}

Linguagem#

Os templates usam a sintaxe do Jinja: {{ … }} insere um valor, {% if … %} e {% for … %} controlam o texto.

Variáveis disponíveis:

Variável O que é
id, type, created_at, api_version Dados do evento.
data.object O objeto completo (agendamento, cliente, sinal…), como na API.
data.changes O que mudou, com from e to.
context.actor, context.source Quem causou o evento e por onde.
organization.name, organization.timezone A sua organização.
now Data e hora da entrega.
secret_ref('nome') Um segredo guardado no webhook (veja abaixo).

Filtros: json, date, default, upper, lower, truncate, replace, join, first, last e length. O date formata datas com d (dia), m (mês), Y (ano com 4 dígitos), y (2 dígitos), H (hora), i (minuto) e s (segundo), no fuso da organização: {{ data.object.start | date('d/m/Y H:i') }}.

Corpo em JSON: sempre | json#

Com content_type: "application/json", toda inserção {{ … }} no corpo precisa terminar no filtro json, e o valor vai sem aspas em volta: o filtro já gera o texto entre aspas, escapa caracteres especiais e escreve null quando o valor não existe.

"nome": {{ data.object.client.name | json }}

Assim, um nome com aspas ou quebra de linha nunca quebra o JSON. Ao salvar, montamos o corpo com um evento de exemplo e recusamos o template se o resultado não for JSON válido.

Outros formatos#

content_type body
application/json (padrão) Texto do template; o resultado precisa ser JSON.
application/x-www-form-urlencoded Objeto campo → template, por exemplo {"nome": "{{ data.object.client.name }}"}.
text/plain Texto livre.

method pode ser POST (padrão), PUT ou PATCH. headers e query aceitam até 10 itens cada, também como template.

Segredos#

Tokens do destino não devem ficar no texto do template, porque o template aparece de volta na API e na tela. Guarde-os em secrets (até 10, nomes com letras minúsculas, números e _) e use {{ secret_ref('nome') }}. Os valores ficam cifrados e nunca voltam: a API mostra só secret_names, e o histórico de entregas mostra •••• no lugar deles. No PATCH, os segredos enviados se juntam aos existentes; mande null para apagar um.

Limites e erros#

  • Template com até 16 KB; resultado com até 64 KB, montado em até 100 ms.
  • Não são permitidos macro, call, include, import, extends, nem mais de 2 laços um dentro do outro.
  • Template inválido ao salvar responde 422 template_invalid, com o campo em errors[].
  • Se um template falhar na entrega (um valor que estourou um limite, por exemplo), a entrega falha na hora, sem novas tentativas, e a falha aparece no histórico.

As entregas do formato custom são assinadas como as do formato genérico: veja Verificar a assinatura.