basePath: /v1
definitions:
  responses.IntegrationBooking:
    description: Reserva ligada à vaga (só quando a ligação é única). Traz o agendado;
      o realizado é product + financial.jump.
    properties:
      amounts:
        allOf:
        - $ref: '#/definitions/responses.IntegrationBookingAmounts'
        description: Valores da reserva como a origem informou. Só com o escopo tandem_jumps.charges.read.
      buyer:
        allOf:
        - $ref: '#/definitions/responses.IntegrationPerson'
        description: Comprador da reserva; e-mail e telefone só com tandem_jumps.contact.read.
      number:
        description: Número da reserva na origem.
        example: "1234567"
        type: string
      product:
        allOf:
        - $ref: '#/definitions/responses.IntegrationBookingProduct'
        description: Produto agendado.
      source:
        description: Origem da reserva.
        example: bookeo
        type: string
    type: object
  responses.IntegrationBookingAmounts:
    description: Valores da reserva como o Bookeo informou (null quando ausentes).
    properties:
      currency:
        description: Moeda.
        example: BRL
        type: string
      deposit:
        description: Sinal pago na reserva.
        example: "300.00"
        type: string
      participants:
        description: Número de participantes da reserva.
        example: 1
        type: integer
      total:
        description: Total da reserva (todos os participantes).
        example: "1000.00"
        type: string
    type: object
  responses.IntegrationBookingProduct:
    description: 'Produto agendado: nome no Bookeo e o produto correspondente no Airo.'
    properties:
      bookeo_name:
        description: Nome do produto no Bookeo.
        example: Salto Duplo Simples
        type: string
      id:
        description: Produto do Airo mapeado; null se não mapeado.
        example: 47758e07-6816-49cc-8965-c0f18e9a2d3b
        type: string
      name:
        description: Nome do produto do Airo mapeado.
        example: Tandem Simples
        type: string
    type: object
  responses.IntegrationDropzone:
    description: DZ dona da chave.
    properties:
      id:
        description: Identificador da DZ.
        example: 88a788c0-2fc2-4a68-a536-f72aaa1d3d2d
        type: string
      name:
        description: Nome da DZ.
        example: DZ Exemplo
        type: string
    type: object
  responses.IntegrationError:
    description: Envelope de erro, igual em todas as respostas de erro (da aplicação
      e do gateway).
    properties:
      error:
        $ref: '#/definitions/responses.IntegrationErrorBody'
    type: object
  responses.IntegrationErrorBody:
    description: Detalhe do erro.
    properties:
      code:
        description: Código estável para a sua lógica.
        enum:
        - invalid_request
        - invalid_cursor
        - unauthorized
        - forbidden
        - insufficient_scope
        - origin_not_allowed
        - not_found
        - method_not_allowed
        - rate_limited
        - quota_exceeded
        - bad_request
        - internal_error
        example: unauthorized
        type: string
      message:
        description: Mensagem legível (em inglês); pode mudar, não use na lógica.
        example: Invalid or missing credentials.
        type: string
      request_id:
        description: Identificador da requisição; informe ao suporte.
        example: c6af9ac6-7b61-11e6-9a41-93e8deadbeef
        type: string
    type: object
  responses.IntegrationExtra:
    description: Complemento da vaga.
    properties:
      name:
        description: Nome do complemento.
        example: Vídeo e fotos
        type: string
      quantity:
        description: Quantidade.
        example: 1
        type: integer
    type: object
  responses.IntegrationFinancial:
    description: 'Financeiro do atendimento, a partir da carteira do passageiro. Só
      com o escopo tandem_jumps.charges.read. Vale pago + ajustes − cobrado = saldo;
      saldo positivo é crédito do passageiro, negativo é valor devido. Se a pessoa
      tem mais de uma vaga de duplo no dia, só a primeira traz o resumo completo:
      as outras trazem só jump (status, other_charges, payments, adjustments, totals
      e ignored_count ausentes).'
    properties:
      adjustments:
        description: Ajustes manuais (descontos e correções), com sinal.
        items:
          $ref: '#/definitions/responses.IntegrationFinancialAdj'
        type: array
      currency:
        description: Moeda.
        example: BRL
        type: string
      ignored_count:
        description: Lançamentos da janela que não se encaixam em nenhum grupo (não
          entram nos totais).
        example: 0
        type: integer
      jump:
        allOf:
        - $ref: '#/definitions/responses.IntegrationFinancialJump'
        description: Cobrança da vaga.
      other_charges:
        description: 'Outros débitos da janela: transfer (ex.: fotos), debit (débito
          manual), other_slot (outra vaga da pessoa), group_split (rateio de vaga
          de outra pessoa). Valores positivos.'
        items:
          $ref: '#/definitions/responses.IntegrationFinancialLine'
        type: array
      payments:
        description: 'Pagamentos da janela: bookeo_deposit, booking_payment, o método
          (pix, card, cash, …), deposit, transfer_in, reconciliation, settlement (com
          sinal) e withdrawal (devolução, negativo).'
        items:
          $ref: '#/definitions/responses.IntegrationFinancialLine'
        type: array
      status:
        description: settled (load pousada e saldo zero) ou open.
        enum:
        - settled
        - open
        example: settled
        type: string
      totals:
        allOf:
        - $ref: '#/definitions/responses.IntegrationFinancialTotals'
        description: Totais do atendimento.
    type: object
  responses.IntegrationFinancialAdj:
    description: Ajuste manual, com sinal (+ crédito para o passageiro / − débito).
    properties:
      amount:
        description: Valor com sinal (string decimal, 2 casas).
        example: "170.00"
        type: string
      date:
        description: Data efetiva (YYYY-MM-DD).
        example: "2026-10-13"
        format: date
        type: string
      description:
        description: Descrição como lançada.
        example: Desconto - 10% NIVER
        type: string
      related_kind:
        description: 'A que o ajuste se refere: jump (cobrança da vaga), payment ou
          other_charge; null se não identificado.'
        enum:
        - jump
        - payment
        - other_charge
        example: jump
        type: string
    type: object
  responses.IntegrationFinancialJump:
    description: Cobrança da vaga. Fica pending (valores null, items vazio) até a
      load pousar.
    properties:
      charged:
        description: Quanto esta carteira pagou pela vaga.
        example: "1700.00"
        type: string
      items:
        description: Composição da cobrança.
        items:
          $ref: '#/definitions/responses.IntegrationFinancialJumpItem'
        type: array
      paid_by_group:
        description: A vaga foi paga (no todo ou em parte) por outra pessoa do grupo.
        example: false
        type: boolean
      slot_total:
        description: Custo total da vaga, somando todos os pagadores.
        example: "1700.00"
        type: string
      status:
        description: pending (load ainda não pousou) ou billed.
        enum:
        - pending
        - billed
        example: billed
        type: string
    type: object
  responses.IntegrationFinancialJumpItem:
    description: Parte da cobrança da vaga.
    properties:
      amount:
        description: Valor (string decimal, 2 casas).
        example: "1700.00"
        type: string
      description:
        description: Descrição.
        example: TANDEM COMPLETO
        type: string
      kind:
        description: 'product (produto), condition (acréscimo de condição, ex.: Corte)
          ou extra (complemento).'
        enum:
        - product
        - condition
        - extra
        example: product
        type: string
    type: object
  responses.IntegrationFinancialLine:
    description: Um outro débito ou um pagamento da janela do atendimento.
    properties:
      amount:
        description: Valor (string decimal, 2 casas).
        example: "1470.00"
        type: string
      date:
        description: Data efetiva (YYYY-MM-DD).
        example: "2026-10-13"
        format: date
        type: string
      description:
        description: Descrição como lançada (outros débitos).
        example: fotos
        type: string
      kind:
        description: Tipo do lançamento (ver other_charges e payments).
        example: pix
        type: string
    type: object
  responses.IntegrationFinancialTotals:
    description: 'Totais: charged e paid positivos; adjustments e balance com sinal.'
    properties:
      adjustments:
        description: Soma dos ajustes, com sinal.
        example: "0.00"
        type: string
      balance:
        description: Saldo = paid + adjustments − charged.
        example: "0.00"
        type: string
      charged:
        description: Total cobrado (vaga + outros débitos).
        example: "1770.00"
        type: string
      paid:
        description: Total pago.
        example: "1770.00"
        type: string
    type: object
  responses.IntegrationKeyInfo:
    description: Chave que fez a chamada (sem nenhum segredo).
    properties:
      expires_at:
        description: Validade da chave (RFC 3339).
        example: "2027-10-22T00:00:00Z"
        type: string
      name:
        description: Nome dado à chave na criação.
        example: CRM RD Station
        type: string
      prefix:
        description: Início do segredo, para identificar a chave.
        example: airo_live_AbCdEf123456…
        type: string
      scopes:
        description: 'Escopos da chave: tandem_jumps.read, tandem_jumps.contact.read,
          tandem_jumps.charges.read.'
        example:
        - tandem_jumps.read
        items:
          type: string
        type: array
    type: object
  responses.IntegrationLoad:
    description: Load (decolagem) da vaga.
    properties:
      number:
        description: Número da load no dia.
        example: 7
        type: integer
      status:
        description: Status da load.
        enum:
        - draft
        - scheduled
        - boarding
        - departed
        - landed
        - cancelled
        example: landed
        type: string
    type: object
  responses.IntegrationMe:
    description: 'Resposta de GET /me: a DZ e a chave que fez a chamada.'
    properties:
      dropzone:
        $ref: '#/definitions/responses.IntegrationDropzone'
      key:
        $ref: '#/definitions/responses.IntegrationKeyInfo'
    type: object
  responses.IntegrationNamed:
    description: Referência só com o nome.
    properties:
      name:
        description: Nome.
        example: João Instrutor
        type: string
    type: object
  responses.IntegrationPerson:
    description: Passageiro ou comprador.
    properties:
      email:
        description: E-mail. Só com o escopo tandem_jumps.contact.read.
        example: maria@example.com
        type: string
      id:
        description: Identificador da pessoa.
        example: 0e6f4d1c-8a7b-4e2f-9c3d-5a6b7c8d9e0f
        type: string
      name:
        description: Nome.
        example: Maria Silva
        type: string
      phone:
        description: Telefone. Só com o escopo tandem_jumps.contact.read.
        example: "+5547999990000"
        type: string
    type: object
  responses.IntegrationProduct:
    description: Produto do Airo.
    properties:
      id:
        description: Identificador do produto.
        example: 77d5aea0-1f2a-4586-a1b5-1aaa5ef1176f
        type: string
      name:
        description: Nome do produto.
        example: Tandem Completo
        type: string
    type: object
  responses.IntegrationTandemJump:
    description: Uma vaga de passageiro de salto duplo.
    properties:
      booking:
        allOf:
        - $ref: '#/definitions/responses.IntegrationBooking'
        description: Reserva ligada à vaga; null sem reserva ou com ligação ambígua.
      departed_at:
        description: Decolagem real da load (nunca o horário previsto); null se não
          registrada.
        example: "2026-10-13T10:40:00-03:00"
        type: string
      extras:
        description: Complementos da vaga.
        items:
          $ref: '#/definitions/responses.IntegrationExtra'
        type: array
      financial:
        allOf:
        - $ref: '#/definitions/responses.IntegrationFinancial'
        description: Financeiro do atendimento. Só com o escopo tandem_jumps.charges.read.
      id:
        description: Identificador da vaga (chave do upsert). Mover o grupo para outra
          load recria a vaga com id novo.
        example: 5b7c1f8e-2a0e-4c55-9d2b-0d7f3c1e9a11
        type: string
      instructor:
        allOf:
        - $ref: '#/definitions/responses.IntegrationNamed'
        description: Instrutor do duplo; null quando não identificado.
      jump_date:
        description: Data do salto (YYYY-MM-DD).
        example: "2026-10-13"
        format: date
        type: string
      load:
        allOf:
        - $ref: '#/definitions/responses.IntegrationLoad'
        description: Load da vaga.
      passenger:
        allOf:
        - $ref: '#/definitions/responses.IntegrationPerson'
        description: Passageiro.
      product:
        allOf:
        - $ref: '#/definitions/responses.IntegrationProduct'
        description: Produto da vaga no Airo (o realizado); null se não houver.
      status:
        description: jumped (saltou), returned_aboard (retornou a bordo), no_show
          (não compareceu) ou scheduled (ainda não voou).
        enum:
        - jumped
        - returned_aboard
        - no_show
        - scheduled
        example: jumped
        type: string
      updated_at:
        description: Última alteração da vaga, load, pessoa, reserva ou lançamento
          da janela (informativo).
        example: "2026-10-13T14:20:00-03:00"
        type: string
    type: object
  responses.IntegrationTandemJumpPage:
    description: Uma página de GET /tandem-jumps.
    properties:
      data:
        description: Vagas da página.
        items:
          $ref: '#/definitions/responses.IntegrationTandemJump'
        type: array
      next_cursor:
        description: Cursor da próxima página (com os mesmos from e to); null na última.
        type: string
    type: object
host: integrations.airodz.com
info:
  contact:
    name: Suporte Airo
    url: https://docs.airodz.com
  description: |-
    API somente leitura para sistemas externos da DZ (CRM, BI) consultarem os saltos duplos. Cada chave pertence a uma DZ e só enxerga os dados dela.

    ## Autenticação
    Toda requisição leva **as duas credenciais** da chave, criadas na tela *Integrações* do Airo e mostradas uma única vez:
    - `x-api-key: <chave do gateway>`
    - `Authorization: Bearer airo_live_…` (o segredo)

    Faltou uma delas, ou não são da mesma chave, ou a chave expirou/foi revogada: `401` (sempre com a mesma resposta) ou `403` (chave do gateway ausente ou inválida). Chame a API **do seu servidor**: requisições de navegador (com `Origin`) são recusadas. Só `GET`, sem corpo.

    ## Escopos
    Definidos na criação da chave:
    - `tandem_jumps.read` — lista de saltos duplos (obrigatório para `/tandem-jumps`).
    - `tandem_jumps.contact.read` — inclui `email` e `phone` do passageiro e do comprador. Sem ele, os campos não vêm.
    - `tandem_jumps.charges.read` — inclui `financial` e `booking.amounts`. Sem ele, os campos não vêm.

    ## Limites (por chave)
    **1 requisição/s, rajada de 10, 5.000 requisições/dia** (o dia vira às 00:00 UTC). Acima disso o gateway responde `429` com o cabeçalho `Retry-After` (segundos): `rate_limited` (taxa) ou `quota_exceeded` (cota diária). Espere o tempo indicado antes de tentar de novo.

    ## Paginação e sincronização
    `/tandem-jumps` exige `from` e `to` (datas do salto, `YYYY-MM-DD`, janela de **no máximo 31 dias**). `limit` vai de 1 a **200** (padrão 100). Enquanto `next_cursor` não for `null`, repita a chamada com os **mesmos** `from` e `to` e `cursor=<next_cursor>`; o cursor é assinado e só vale para a mesma chave e janela.
    Recomendação: puxe diariamente os **últimos 30 dias** e faça *upsert* pelo `id` (pagamentos e fotos chegam depois do salto). Um `id` que some de uma janela já puxada foi excluído ou movido (mover um grupo de load recria as vagas com `id` novo). Para o histórico, percorra janelas de até 31 dias.

    ## Formatos
    Valores em dinheiro são strings decimais com 2 casas (`"1700.00"`), em BRL. Datas `YYYY-MM-DD`; horários RFC 3339 com o fuso da DZ. Campos novos podem ser adicionados a qualquer momento (ignore os que não conhece); nada é renomeado ou removido na v1.

    ## Erros
    Todas as respostas de erro têm o mesmo envelope: `{"error": {"code": "…", "message": "…", "request_id": "…"}}`. Use `code` na sua lógica (`invalid_request`, `invalid_cursor`, `unauthorized`, `forbidden`, `insufficient_scope`, `origin_not_allowed`, `not_found`, `method_not_allowed`, `rate_limited`, `quota_exceeded`, `internal_error`) e informe o `request_id` ao suporte.
  title: Airo — API de integração
  version: v1
paths:
  /me:
    get:
      description: 'Descreve a chave que fez a chamada: a DZ, o nome da chave, os
        escopos e a validade. Nunca traz dados de quem criou a chave. Use para o botão
        "testar conexão" do seu sistema. Conta na cota e fica na trilha de auditoria
        da DZ.'
      operationId: integrations-me
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/responses.IntegrationMe'
        "400":
          description: Parâmetro de consulta desconhecido ou corpo na requisição (invalid_request)
          schema:
            $ref: '#/definitions/responses.IntegrationError'
        "401":
          description: Credenciais ausentes ou inválidas, chave expirada ou revogada
            (unauthorized; sempre a mesma resposta)
          schema:
            $ref: '#/definitions/responses.IntegrationError'
        "403":
          description: Chave do gateway ausente/inválida (forbidden) ou requisição
            de navegador (origin_not_allowed)
          schema:
            $ref: '#/definitions/responses.IntegrationError'
        "429":
          description: Taxa (rate_limited) ou cota diária (quota_exceeded) excedida;
            respeite o cabeçalho Retry-After
          headers:
            Retry-After:
              description: Segundos de espera antes de tentar de novo
              type: integer
          schema:
            $ref: '#/definitions/responses.IntegrationError'
        "500":
          description: Erro interno (internal_error); informe o request_id ao suporte
          schema:
            $ref: '#/definitions/responses.IntegrationError'
      security:
      - GatewayKey && BearerSecret: []
      summary: Testar a conexão
      tags:
      - Integrations
  /tandem-jumps:
    get:
      description: |-
        Vagas de passageiro de salto duplo da DZ com data do salto entre `from` e `to` (inclusive, no máximo 31 dias), em todos os status (`jumped`, `returned_aboard`, `no_show`, `scheduled`), ordenadas pela data do salto. Uma vaga por passageiro: o `id` é o da vaga.

        **Sincronização**: puxe diariamente os últimos 30 dias e faça *upsert* pelo `id` — pagamentos e fotos chegam depois do salto. Um `id` que some de uma janela já puxada foi excluído ou movido (mover um grupo para outra load recria as vagas com `id` novo).

        **Paginação**: enquanto `next_cursor` não for `null`, repita com os mesmos `from` e `to` e `cursor=<next_cursor>`.

        **Escopos**: `email` e `phone` do passageiro e do comprador só com `tandem_jumps.contact.read`; `financial` e `booking.amounts` só com `tandem_jumps.charges.read` — sem o escopo, os campos não aparecem.

        **Financeiro do atendimento** (`financial`): resumo da carteira do passageiro — cobrança da vaga, outros débitos e transferências (ex.: fotos), pagamentos, ajustes e saldo, com `pago + ajustes − cobrado = saldo`. Considera o sinal da reserva e os lançamentos desde o dia do salto (no primeiro salto, desde o início da carteira) até o próximo salto da pessoa; pode mudar depois do salto. Dinheiro é string decimal com 2 casas.
      operationId: integrations-tandem-jumps-list
      parameters:
      - description: Primeira data do salto (YYYY-MM-DD)
        format: date
        in: query
        name: from
        required: true
        type: string
      - description: Última data do salto (YYYY-MM-DD), no máximo 30 dias depois de
          from (janela de 31 dias)
        format: date
        in: query
        name: to
        required: true
        type: string
      - default: 100
        description: Itens por página (1 a 200)
        in: query
        maximum: 200
        minimum: 1
        name: limit
        type: integer
      - description: next_cursor da página anterior (só vale para a mesma chave, from
          e to)
        in: query
        name: cursor
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/responses.IntegrationTandemJumpPage'
        "400":
          description: Parâmetros inválidos (invalid_request) ou cursor inválido (invalid_cursor)
          schema:
            $ref: '#/definitions/responses.IntegrationError'
        "401":
          description: Credenciais ausentes ou inválidas, chave expirada ou revogada
            (unauthorized; sempre a mesma resposta)
          schema:
            $ref: '#/definitions/responses.IntegrationError'
        "403":
          description: Chave sem o escopo tandem_jumps.read (insufficient_scope),
            chave do gateway ausente/inválida (forbidden) ou requisição de navegador
            (origin_not_allowed)
          schema:
            $ref: '#/definitions/responses.IntegrationError'
        "429":
          description: Taxa (rate_limited) ou cota diária (quota_exceeded) excedida;
            respeite o cabeçalho Retry-After
          headers:
            Retry-After:
              description: Segundos de espera antes de tentar de novo
              type: integer
          schema:
            $ref: '#/definitions/responses.IntegrationError'
        "500":
          description: Erro interno (internal_error); informe o request_id ao suporte
          schema:
            $ref: '#/definitions/responses.IntegrationError'
      security:
      - GatewayKey && BearerSecret: []
      summary: Listar saltos duplos
      tags:
      - Integrations
schemes:
- https
securityDefinitions:
  BearerSecret:
    description: Bearer airo_live_… — o segredo da chave, no formato `Bearer <segredo>`.
    in: header
    name: Authorization
    type: apiKey
  GatewayKey:
    description: Chave do gateway, mostrada uma única vez na criação da chave. Controla
      a taxa e a cota diária.
    in: header
    name: x-api-key
    type: apiKey
swagger: "2.0"
