Pular para conteúdo

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 recebem 405.
  • 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 com 403 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-ID e 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 é pending enquanto a decolagem não pousou (a cobrança nasce no pouso; items vem vazio e os valores null) e billed depois.
  • 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), charged pode ser 0.00 e slot_total mostra o valor cheio.
  • financial.status é settled com a decolagem pousada e saldo zero; open nos 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_count conta 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:

  1. Carga histórica: percorra o passado em janelas de até 31 dias (ex.: 2026-06-01 a 2026-07-01, depois 2026-07-02 a 2026-08-01, …), paginando cada janela até o fim.
  2. Todo dia: puxe os últimos 30 dias (from = hoje − 29, to = hoje), paginando até o fim.
  3. Upsert pelo id: grave ou atualize cada item pelo id da vaga.
  4. Id que sumiu = excluído ou movido. Se um id que 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.