Autenticação
Todos os pedidos à API utilizam uma chave de API como bearer token. Crie uma em Developer → API keys. As chaves são apenas de leitura ou leitura/escrita.
curl https://savrsoft.com/api/v1/events \ -H "Authorization: Bearer savr_live_your_key_here"
URL base: https://savrsoft.com/api/v1 · Todas as respostas são em JSON. Os erros devolvem { "error": "…" } com um estado 4xx/5xx.
Códigos de estado
| Código | Quando o recebe |
|---|---|
200 | Leitura bem-sucedida. |
201 | Criado com sucesso. O corpo é { "id": 42 } — obtenha o registo se precisar do resto da informação. |
400 | Falta um campo obrigatório, por exemplo { "error": "name is required" }, ou não foi possível ler um cursor de paginação. |
401 | Sem chave de API, uma malformada, ou uma chave que foi revogada. |
403 | Foi utilizada uma chave só de leitura para uma escrita. Crie uma chave de leitura/escrita em Developer → API keys. |
404 | O registo não existe, ou pertence a outra organização — as duas situações são deliberadamente indistinguíveis. Também é devolvido para um caminho ou método não reconhecido. |
Âmbito desta API
A API é apenas de leitura e criação. Não existem endpoints DELETE, PUT ou PATCH
— um pedido que utilize um destes devolve 404 Unknown endpoint, tal como qualquer caminho não reconhecido. A eliminação e
edição fazem-se na aplicação, para que uma integração não possa destruir os registos de um caterer. Se precisar de acesso de escrita além da criação,
diga-nos o que está a construir.
Paginação
Todos os endpoints de listagem são paginados. Passe limit (predefinição 100, máximo 500) e
siga next_cursor até que has_more seja false:
{
"data": [ ... ],
"has_more": true,
"next_cursor": "eyJuYW1lIjoiQWNtZSIsImlkIjo0Mn0"
}
Passe o cursor diretamente de volta para obter a página seguinte. Trate-o como opaco — a codificação pode mudar:
curl "https://savrsoft.com/api/v1/clients?limit=100&cursor=eyJuYW1lIjoiQWNtZSIsImlkIjo0Mn0" \ -H "Authorization: Bearer savr_live_your_key_here"
A paginação está ancorada à última linha recebida, e não a um offset, pelo que as linhas criadas ou eliminadas enquanto percorre as páginas não farão com que salte ou repita um registo. Um cursor mantém-se válido indefinidamente.
| Parâmetro | Comportamento |
|---|---|
limit | 1–500. Se estiver ausente, for zero, negativo ou não numérico, assume 100; qualquer valor superior a 500 é limitado a 500. |
cursor | next_cursor da página anterior. Omita-o para a primeira página. Um cursor que não possa ser lido — corrompido ou proveniente de um endpoint diferente — devolve 400 Invalid cursor em vez de recomeçar silenciosamente do início. |
Ordenação: /events e /invoices mais recentes primeiro; /recipes e
/clients por nome, com os empates resolvidos pelo id para que a paginação se mantenha estável quando os nomes se repetem.
Endpoints
/v1/meA sua organização (id, nome, moeda).
/v1/eventsLista de eventos, mais recentes primeiro. Paginado — ver Paginação acima.
/v1/events/{id}Um evento com os seus pratos.
/v1/events · requer leitura/escritaCriar um evento. Corpo:
{ "name": "Acme holiday party", "event_date": "2026-12-18",
"guests": 120, "price_per_head": 55, "event_type": "corporate" }
/v1/recipes · GET/v1/recipes/{id}Listar receitas (com alergénios & indicações dietéticas), ou uma receita com ingredientes. A lista é paginada e ordenada por nome.
/v1/clients · POST/v1/clientsListar clientes (paginados, por nome) ou criar um. Corpo de criação: { "name": "...", "email": "...", "phone": "...", "company": "..." }
/v1/invoices · GET/v1/invoices/{id}Listar faturas com totais calculados, das mais recentes às mais antigas, ou uma fatura com as linhas de detalhe. A lista é paginada.
Webhooks
Registe um endpoint em Developer → Webhooks. Nós enviamos um POST com um payload JSON quando ocorrem eventos:
{
"id": "evt_abc123",
"type": "event.inquiry",
"created_at": "2026-07-17T18:20:00.000Z",
"data": { "id": 42, "name": "Corporate lunch — Dana Ruiz", ... }
}
Tipos de eventos
| Tipo | Ativa quando |
|---|---|
event.created | Um evento é criado |
event.inquiry | Chega um novo pedido de catering online |
invoice.sent | Uma fatura é marcada como enviada |
invoice.paid | Uma fatura é marcada como paga |
proposal.approved | Um cliente aprova uma proposta |
client.created | Um cliente é criado |
Verificação de assinaturas
Cada envio inclui um cabeçalho x-savrsoft-signature : o HMAC-SHA256 do corpo bruto do pedido, assinado com a sua chave secreta de assinatura do webhook. Calcule o mesmo valor e compare:
// Node.js
const crypto = require('crypto');
const sig = crypto.createHmac('sha256', SIGNING_SECRET)
.update(rawBody).digest('hex');
if (sig !== req.headers['x-savrsoft-signature']) reject();
