Pular para o conteúdo

n8n

O n8n conversa com a Ligga API pelo nó HTTP Request, sem nó dedicado e sem código. Uma credencial de cabeçalho carrega a chave, e cada nó só precisa do método, da URL e do corpo.

A chave vive numa credencial, nunca escrita dentro do nó. Em Credentials → New, escolha Header Auth:

Campo Valor
Name Ligga API
Header Name Authorization
Header Value Bearer ligga_live_…

O Bearer e o espaço fazem parte do valor. Sem eles, toda chamada volta 401 invalid_token.

Todas as URLs desta página partem do mesmo endereço, https://api.ligga.app/functions/v1/api, seguido do caminho do recurso.

Adicione um nó HTTP Request e configure:

Campo Valor
Method GET
URL https://api.ligga.app/functions/v1/api/v1/me
Authentication Generic Credential Type → Header Auth → Ligga API

Clique em Test step. A resposta descreve a chave e confirma que a credencial está correta:

{
"data": {
"key_id": "c06d8705-f091-46e1-81bb-4a2cec656694",
"key_name": "Integração ERP",
"key_last4": "af0e",
"key_prefix": "live",
"scopes": [
"*:read",
"sales:write"
]
},
"meta": {
"request_id": "req_01JZXQ8V2K5M7N9P0R1S2T3U4V"
}
}

O nó HTTP Request pagina sozinho. Configure a listagem e ligue a paginação nas opções do nó:

Campo Valor
Method GET
URL https://api.ligga.app/functions/v1/api/v1/customers
Send Query Parameters ligado, com limit = 100
Options → Pagination → Pagination Mode Update a Parameter in Each Request
Type Query
Name cursor
Value {{ $response.body.pagination.next_cursor }}
Pagination Complete When Other
Complete Expression {{ !$response.body.pagination.has_more }}
Interval Between Requests 200 ms

O cursor é opaco: o nó só o copia da resposta anterior para a query da próxima requisição. limit vai de 1 a 100, com padrão 20.

Se preferir controlar o laço na mão, o desenho é o mesmo em nós:

  1. HTTP Request — GET /v1/customers?limit=100&cursor={{ $json.cursor ?? '' }}.

  2. Nó de processamento — o que você faz com data (gravar em planilha, enviar para outro sistema).

  3. If — condição {{ $json.pagination.has_more }} é verdadeira?

  4. Ramo verdadeiro — um nó Set guarda cursor = {{ $json.pagination.next_cursor }} e volta para o HTTP Request. O ramo falso encerra.

Repetir a execução de um workflow é normal no n8n: o usuário reprocessa, o trigger dispara duas vezes, um nó falha e você reexecuta. O cabeçalho Idempotency-Key é o que impede a segunda passada de criar uma segunda venda.

Campo Valor
Method POST
URL https://api.ligga.app/functions/v1/api/v1/sales
Authentication Header Auth → Ligga API
Send Headers ligado
Header — Idempotency-Key {{ $execution.id }}
Send Body ligado, Body Content Type = JSON
{
"customer_id": "{{ $json.customer_id }}",
"transaction_type": "sale",
"sale_date": "{{ $now.format('yyyy-MM-dd') }}",
"items": [
{
"catalog_type": "product",
"catalog_item_id": "{{ $json.product_id }}",
"quantity": {{ $json.quantity }},
"unit_price": {{ $json.unit_price }}
}
]
}

$execution.id muda a cada execução e é estável dentro dela, então a nova tentativa automática de um nó reaproveita a mesma chave. Se o mesmo workflow cria mais de um registro por execução, componha a chave com algo do item, por exemplo {{ $execution.id }}-{{ $json.id }}, para que cada registro tenha a sua.

Respostas de sucesso saem como application/json e o n8n as converte sozinho. Respostas de erro saem como application/problem+json, e esse tipo o n8n não converte: o corpo chega como texto.

Para ler type, detail e request_id de um 4xx ou 5xx, ligue Settings → On Error → Continue (using error output) no nó HTTP Request e faça o parse no ramo de erro:

Code node — ramo de erro
const bruto = $json.body ?? $json.error;
const problema = typeof bruto === 'string' ? JSON.parse(bruto) : bruto;
return [{
json: {
status: problema.status,
tipo: problema.type,
detalhe: problema.detail ?? problema.title,
request_id: problema.request_id,
},
}];
{
"type": "https://api.ligga.app/errors/validation",
"title": "Validation failed",
"status": 422,
"detail": "full_name is required",
"request_id": "req_01JZXQ8V2K5M7N9P0R1S2T3U4V"
}

Sem parse nenhum, o mesmo identificador chega pelo cabeçalho X-Request-Id. Com Options → Include Response Headers and Status ligada, use {{ $json.headers['x-request-id'] }} — é o que o time da Ligga pede para investigar uma chamada.

Sintoma type Causa
401 em todos os nós invalid_token Falta Bearer (com espaço) no valor do cabeçalho, ou a chave está errada
403 ao escrever insufficient_scope A chave não carrega o escopo de escrita do recurso, por exemplo customers:write
403 em um recurso só module_not_enabled O módulo daquele recurso não está no contrato
409 ao reexecutar idempotency_conflict Mesma Idempotency-Key com corpo diferente
422 ao criar validation Campo obrigatório ausente ou com tipo errado. Confira as aspas do JSON: quantity e unit_price são números
429 em laço rate-limit-exceeded Sem espera entre as voltas. Use Interval Between Requests ou um nó Wait
type e detail vazios — Corpo de erro ainda em texto: falta o parse acima

Para receber dados de um formulário ou de outro sistema e criar o registro na Ligga:

  1. Webhook — recebe a requisição do sistema de origem.

  2. Set — mapeia os campos recebidos para os nomes da Ligga, por exemplo full_name, email e phone.

  3. HTTP Request — POST /v1/customers, com a credencial Ligga API e o cabeçalho Idempotency-Key = {{ $execution.id }}.

  4. Respond to Webhook — devolve o id criado para quem chamou.

full_name é obrigatório e pelo menos um entre email e phone precisa vir preenchido, senão a criação volta 422 validation.