Guia do integrador¶
👩💻 Para quem é: quem vai programar a integração do lado do outro sistema (CRM, BI, ERP). 🔑 Você precisa de: as duas credenciais da chave, que o administrador da DZ cria em
Configurações → Integrações(veja Integrações e API). 📘 Referência completa: Referência da API (todos os campos, tipos e respostas).
A API de integração do Airo é somente leitura e devolve os saltos duplos de uma DZ — com o passageiro, o instrutor, o produto, a reserva ligada e, se a chave permitir, o contato e o resumo financeiro do atendimento. Cada chave pertence a uma DZ e só enxerga os dados dela.
O contrato (nomes de campos, valores de status e code) é em inglês; esta documentação é em português.
Endereço e autenticação¶
Endereço base: https://integrations.airodz.com/v1
Toda requisição leva as duas credenciais da chave, nos cabeçalhos:
| Cabeçalho | Valor |
|---|---|
x-api-key |
A chave do gateway |
Authorization |
Bearer airo_live_… — o segredo da chave |
As credenciais são mostradas uma única vez, quando a DZ cria a chave. Se forem perdidas, a DZ cria outra.
Regras da requisição:
- Só
GET, sem corpo. Outros métodos recebem405. - Só os parâmetros documentados; parâmetro desconhecido recebe
400. - Chame a API do seu servidor. Requisições de navegador (com o cabeçalho
Origin) são recusadas com403 origin_not_allowed, e a API não tem CORS. Nunca coloque as credenciais em código de navegador, app de celular ou repositório. - O cabeçalho
X-Tenant-IDe tokens de login do Airo são ignorados/recusados: a DZ vem da chave. - Chave recém-criada: pode levar alguns minutos para começar a funcionar no gateway. Nesse intervalo a resposta é
403 forbidden(chave do gateway inválida) — aguarde e tente de novo antes de concluir que há erro.
Testar a conexão — GET /me¶
curl https://integrations.airodz.com/v1/me \
-H "x-api-key: $AIRO_GATEWAY_KEY" \
-H "Authorization: Bearer $AIRO_SECRET"
{
"dropzone": { "id": "88a788c0-2fc2-4a68-a536-f72aaa1d3d2d", "name": "DZ Céu Azul" },
"key": {
"name": "CRM RD Station",
"prefix": "airo_live_AbCdEf123456…",
"scopes": ["tandem_jumps.read", "tandem_jumps.contact.read"],
"expires_at": "2027-10-22T00:00:00Z"
}
}
Use /me no botão "testar conexão" do seu sistema e para avisar antes do vencimento (expires_at). Ela conta no limite diário como qualquer outra chamada.
Escopos¶
A DZ escolhe os escopos ao criar a chave. Sem o escopo, os campos correspondentes não vêm na resposta (não vêm nem como null).
| Escopo | Na tela do Airo | O que libera |
|---|---|---|
tandem_jumps.read |
Saltos duplos | GET /tandem-jumps (sempre presente) |
tandem_jumps.contact.read |
Contato | email e phone do passageiro e do comprador |
tandem_jumps.charges.read |
Cobranças | financial e booking.amounts |
Para mudar os escopos, a DZ cria uma chave nova e revoga a antiga.
Listar saltos duplos — GET /tandem-jumps¶
| Parâmetro | Obrigatório | Regra |
|---|---|---|
from |
Sim | Primeira data do salto, YYYY-MM-DD |
to |
Sim | Última data do salto, YYYY-MM-DD, inclusive. Janela de no máximo 31 dias (to até 30 dias depois de from) |
limit |
Não | Itens por página, de 1 a 200 (padrão 100) |
cursor |
Não | O next_cursor da página anterior |
curl "https://integrations.airodz.com/v1/tandem-jumps?from=2026-10-01&to=2026-10-31&limit=200" \
-H "x-api-key: $AIRO_GATEWAY_KEY" \
-H "Authorization: Bearer $AIRO_SECRET"
A resposta é { "data": [ ... ], "next_cursor": "..." }, ordenada pela data do salto. Cada item é uma vaga de passageiro de salto duplo — um passageiro, um item. O id é o da vaga.
Paginação¶
Enquanto next_cursor não for null, repita a chamada com os mesmos from e to e cursor=<next_cursor>:
curl "https://integrations.airodz.com/v1/tandem-jumps?from=2026-10-01&to=2026-10-31&limit=200&cursor=$NEXT_CURSOR" \
-H "x-api-key: $AIRO_GATEWAY_KEY" \
-H "Authorization: Bearer $AIRO_SECRET"
O cursor é assinado: só vale para a mesma chave e a mesma janela. Cursor alterado, ou usado com outro from/to, recebe 400 invalid_cursor.
Exemplo de item¶
Com os três escopos:
{
"id": "5b7c1f8e-2a0e-4c55-9d2b-0d7f3c1e9a11",
"jump_date": "2026-10-13",
"departed_at": "2026-10-13T10:40:00-03:00",
"status": "jumped",
"load": { "number": 7, "status": "landed" },
"passenger": {
"id": "0e6f4d1c-8a7b-4e2f-9c3d-5a6b7c8d9e0f",
"name": "Maria Silva",
"email": "maria@example.com",
"phone": "+5547999990000"
},
"instructor": { "name": "João Instrutor" },
"product": { "id": "77d5aea0-1f2a-4586-a1b5-1aaa5ef1176f", "name": "Tandem Completo" },
"extras": [],
"booking": {
"source": "bookeo",
"number": "1234567",
"product": { "bookeo_name": "Salto Duplo Simples", "id": "47758e07-6816-49cc-8965-c0f18e9a2d3b", "name": "Tandem Simples" },
"amounts": { "currency": "BRL", "total": "1000.00", "deposit": "300.00", "participants": 1 },
"buyer": { "id": "0e6f4d1c-8a7b-4e2f-9c3d-5a6b7c8d9e0f", "name": "Maria Silva", "email": "maria@example.com", "phone": "+5547999990000" }
},
"financial": {
"status": "settled",
"currency": "BRL",
"jump": {
"status": "billed",
"charged": "1700.00",
"slot_total": "1700.00",
"paid_by_group": false,
"items": [{ "kind": "product", "description": "TANDEM COMPLETO", "amount": "1700.00" }]
},
"other_charges": [{ "kind": "transfer", "description": "fotos", "date": "2026-10-13", "amount": "70.00" }],
"payments": [
{ "kind": "bookeo_deposit", "date": "2026-10-01", "amount": "300.00" },
{ "kind": "pix", "date": "2026-10-13", "amount": "1470.00" }
],
"adjustments": [],
"totals": { "charged": "1770.00", "paid": "1770.00", "adjustments": "0.00", "balance": "0.00" },
"ignored_count": 0
},
"updated_at": "2026-10-13T14:20:00-03:00"
}
Neste exemplo, a passageira reservou um Tandem Simples no Bookeo e fez upgrade para Tandem Completo na DZ (booking.product é o agendado; product é o realizado).
Campos principais¶
| Campo | Significado |
|---|---|
id |
Identificador da vaga — use como chave do upsert |
jump_date |
Data do salto (data do manifesto) |
departed_at |
Horário real da decolagem, com o fuso da DZ. null se a decolagem ainda não aconteceu ou não foi registrada — nunca é o horário previsto |
status |
Situação do passageiro (tabela abaixo) |
load |
Número da decolagem no dia e o status dela (draft, scheduled, boarding, departed, landed) |
passenger |
Passageiro do duplo. email/phone só com o escopo de contato |
instructor |
Instrutor do duplo. null quando não dá para identificar com segurança (ex.: grupo sem instrutor ou com mais de um candidato). Nunca é o cinegrafista |
product |
Produto da vaga no Airo — o que foi realizado |
extras |
Complementos da vaga (ex.: edição de vídeo), com quantidade |
booking |
Reserva ligada à vaga (veja abaixo) |
financial |
Resumo financeiro do atendimento (veja abaixo). Só com o escopo de cobranças |
updated_at |
Última alteração da vaga, da decolagem, da pessoa, da reserva ou de um lançamento financeiro do atendimento. Informativo: não use para sincronizar (veja Sincronização) |
Situações (status)¶
| Valor | Significado |
|---|---|
jumped |
Saltou: a decolagem saiu (departed ou landed) e o passageiro estava a bordo |
returned_aboard |
Retornou a bordo: embarcou, mas voltou com o avião sem saltar |
no_show |
Não compareceu |
scheduled |
Ainda não voou: a decolagem não saiu (rascunho, agendada ou embarcando) |
Todos os status vêm na lista, para você medir a conversão (agendou × saltou). Decolagens canceladas não aparecem. Instrutores, cinegrafistas e outros atletas da decolagem não são itens da lista.
Reserva (booking)¶
booking vem preenchido quando a vaga está ligada a uma única reserva: a mesma pessoa é participante da reserva e está no manifesto daquele dia (ou, quando a reserva não tem manifesto, a data agendada é a do salto). Traz a origem (bookeo), o número da reserva, o produto agendado (nome no Bookeo e produto correspondente no Airo) e o comprador.
booking é null quando:
- o passageiro chegou direto na DZ, sem reserva; ou
- a ligação é ambígua (por exemplo, duas reservas da mesma pessoa no dia) — o Airo prefere não ligar a ligar errado.
⚠️ "Sem reserva" só significa "chegou direto na DZ" a partir da data em que a DZ começou a sincronizar com o Bookeo. Antes dessa data, as reservas não foram importadas para o Airo. A data não sai na API: peça à DZ (é a Data inicial para sincronização nas configurações do Bookeo dela).
Com o escopo de cobranças, booking.amounts traz o total e o sinal como o Bookeo informou (null quando ausentes), e participants é o número de participantes da reserva inteira. Numa reserva com dois passageiros, os dois itens trazem os mesmos amounts.
Resumo financeiro do atendimento (financial)¶
Só com o escopo tandem_jumps.charges.read. É um resumo, montado na hora da consulta, do que está lançado na carteira do passageiro para aquele atendimento — o salto e tudo o que foi cobrado e pago em volta dele. A API só lê: nada que você consulta muda valores no Airo.
| Grupo | O que entra | Sinal |
|---|---|---|
jump |
A cobrança da vaga, com a composição em items: product (o produto), condition (acréscimo de condição de decolagem, ex.: Corte) e extra (complemento) |
Positivo |
other_charges |
Outros débitos do atendimento: transfer (ex.: fotos pagas ao fotógrafo), debit (débito manual), other_slot (outra vaga da mesma pessoa), group_split (parte da vaga de outra pessoa do grupo) |
Positivo |
payments |
Pagamentos: bookeo_deposit (sinal do Bookeo), booking_payment, o método (pix, card, cash, …), transfer_in (alguém pagou pelo passageiro), reconciliation, settlement (com sinal) e withdrawal (dinheiro devolvido ao passageiro, negativo) |
Positivo, exceto settlement e withdrawal |
adjustments |
Ajustes manuais: descontos (positivo, a favor do passageiro) e correções (negativo, ex.: taxa de parcelamento) | Com sinal |
Totais (totals):
charged— tudo o que foi cobrado (vaga + outros débitos), positivo;paid— tudo o que foi pago, positivo;adjustments— soma dos ajustes, com sinal;balance— saldo = pago + ajustes − cobrado. Positivo = crédito do passageiro; negativo = o passageiro deve; zero = quitado.
Outros campos:
jump.statusépendingenquanto a decolagem não pousou (a cobrança nasce no pouso;itemsvem vazio e os valoresnull) ebilleddepois.jump.chargedé quanto esta carteira pagou pela vaga;jump.slot_totalé o custo total da vaga. Quando outra pessoa do grupo pagou (paid_by_group: true),chargedpode ser0.00eslot_totalmostra o valor cheio.financial.statusésettledcom a decolagem pousada e saldo zero;opennos outros casos.- Se a mesma pessoa tem duas vagas de duplo no mesmo dia, só a primeira traz o resumo completo; a outra traz só
jump. ignored_countconta lançamentos do período que não se encaixam em nenhum grupo (não entram nos totais).
Que lançamentos entram: o sinal da reserva (em qualquer data — ele costuma chegar dias antes) e os lançamentos da carteira desde o dia do salto (no primeiro salto da pessoa, desde o início da carteira) até o próximo salto dela. Fotos e pagamentos feitos dias depois do salto entram no mesmo atendimento.
🔄 O resumo pode mudar depois do salto — fotos pagas dias depois, um desconto lançado na semana seguinte, uma correção na decolagem. Por isso a recomendação de re-puxar os últimos 30 dias todos os dias.
Valores em dinheiro são strings decimais com 2 casas ("1700.00"), em BRL. Não converta para ponto flutuante.
Sincronização¶
A v1 funciona por consulta (sem webhooks). O padrão recomendado:
- Carga histórica: percorra o passado em janelas de até 31 dias (ex.:
2026-06-01a2026-07-01, depois2026-07-02a2026-08-01, …), paginando cada janela até o fim. - Todo dia: puxe os últimos 30 dias (
from= hoje − 29,to= hoje), paginando até o fim. - Upsert pelo
id: grave ou atualize cada item peloidda vaga. - Id que sumiu = excluído ou movido. Se um
idque você já tinha não aparece mais numa janela que você puxou inteira, a vaga foi apagada ou o grupo foi movido para outra decolagem — e mover recria as vagas com id novo. Marque o antigo como removido no seu sistema.
Faça a sincronização num horário tranquilo e em sequência (uma requisição por vez). Uma DZ com 300 duplos por mês cabe em 2 requisições por dia com limit=200.
Limites¶
Por chave: 1 requisição por segundo (rajada de até 10) e 5.000 requisições por dia. O dia vira às 00:00 UTC (21:00 em Brasília).
Acima do limite, a resposta é 429 com o cabeçalho Retry-After (em segundos):
rate_limited— taxa por segundo excedida. Espere o tempo indicado e repita.quota_exceeded— limite diário excedido. Tente de novo depois da virada do dia.
Respeite o Retry-After e use espera crescente entre tentativas.
Erros¶
Todas as respostas de erro têm o mesmo formato:
{
"error": {
"code": "unauthorized",
"message": "Invalid or missing credentials.",
"request_id": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef"
}
}
Use o code na sua lógica (a message é em inglês e pode mudar) e informe o request_id ao suporte.
| HTTP | code |
Quando acontece | O que fazer |
|---|---|---|---|
400 |
invalid_request |
Parâmetro faltando, inválido ou desconhecido; janela maior que 31 dias; corpo na requisição | Corrija a chamada |
400 |
invalid_cursor |
Cursor alterado, de outra janela ou de outra chave | Recomece a janela sem cursor |
401 |
unauthorized |
Credencial ausente ou errada, as duas credenciais não são da mesma chave, chave expirada ou revogada, quem criou a chave saiu da equipe ou perdeu a permissão. A resposta é sempre a mesma, de propósito | Peça à DZ para conferir a chave na tela Integrações (e, se for o caso, criar outra) |
403 |
forbidden |
Chave do gateway (x-api-key) ausente, inválida, revogada ou suspensa (ex.: módulo de integrações desligado na DZ) — ou criada há poucos minutos e ainda propagando |
Confira o cabeçalho x-api-key; se a chave é nova, aguarde alguns minutos; se persistir, fale com a DZ |
403 |
insufficient_scope |
A chave não tem o escopo necessário | Peça à DZ uma chave com o escopo |
403 |
origin_not_allowed |
Requisição feita de navegador | Chame a API do seu servidor |
404 |
not_found |
Caminho inexistente | Confira o endereço (/v1/...) |
405 |
method_not_allowed |
Método diferente de GET |
Use GET |
429 |
rate_limited / quota_exceeded |
Limite excedido | Espere o Retry-After |
500 |
internal_error |
Erro do Airo | Tente de novo mais tarde; se persistir, informe o request_id |
Boas práticas¶
- Guarde as duas credenciais num cofre de segredos do seu servidor, nunca no código.
- Uma chave por sistema. Ao trocar a chave, a DZ cria a nova, você troca no seu sistema e a DZ revoga a antiga — sem interrupção.
- Ignore campos que você não conhece: campos novos podem ser adicionados a qualquer momento. Na v1 nada é renomeado nem removido; uma mudança incompatível seria uma
v2, anunciada com antecedência. - Toda chamada fica na trilha de auditoria da DZ (quando, de onde e o que foi consultado).
- Dados pessoais (nome, e-mail, telefone) estão sob a LGPD: use só para a finalidade combinada com a DZ.
Referência e download¶
- Referência da API — todos os campos e respostas.
- openapi.yaml — a especificação (formato OpenAPI 2.0 / Swagger) para importar no Postman, Insomnia ou gerador de cliente.