openapi: 3.1.0
info:
  title: SiteUp API — WhatsApp Groups (Gestão)
  version: "1.0.0"
  description: |
    API de gestão de grupos WhatsApp na SiteUp (campanhas, salas, membros, disparos,
    sequências e webhooks). Validada em smoke 2026-08-01 (staging + reads prod).

    ## Auth
    - Preferido: header `api_access_token: <token do usuário>`
    - Alternativo (browser/E2E): Devise `access-token`, `client`, `uid` após POST `/auth/sign_in`

    ## Base
    - Produção: `https://app.siteup.com.br`
    - Staging: `https://staging.siteup.com.br`

    ## Regras de produto
    - Capacidade de sala: **1024**
    - Criar campanha exige `group_ids` (criar grupo antes)
    - Canal (`@newsletter`) não tem membros, admin promovível nem teto: só link de convite.
      Criar canal só pelo provisionamento em lote (`chat_kind: channel`)
    - Comunidade não é criável por API (o WAHA só lê): criar à mão no número conectado e
      adotar o **grupo de avisos** com `is_community: true`
    - Telefones: E.164 **sem** `+` (ex: `5551989769026`)
    - Audio: preferir **mp3**; caption de áudio é ignorada no WAHA
    - `remove_members_after` default **false** em QA
    - Mutações de campanha/grupo/webhook/sequência: tipicamente **admin**

    ## Smoke 2026-08-01
    - PASS: groups CRUD-ish, members add, campaigns create/show/health, broadcasts create/show,
      invite_code, sync_members, webhooks create, activities/index, waha sessions, prod reads
    - FAIL/edge: `PUT campaigns` rename simples → 500; `POST sequences` mínimo → 500
servers:
  - url: https://app.siteup.com.br
    description: Produção
  - url: https://staging.siteup.com.br
    description: Staging
tags:
  - name: Groups
  - name: Members
  - name: Campaigns
  - name: Broadcasts
  - name: Webhooks
  - name: Sequences
  - name: Activities
  - name: WAHA
paths:
  /api/v1/accounts/{account_id}/whatsapp_groups:
    get:
      tags: [Groups]
      summary: Listar grupos
      parameters:
        - $ref: '#/components/parameters/account_id'
        - { name: status, in: query, schema: { type: string } }
        - { name: inbox_id, in: query, schema: { type: integer } }
        - { name: search, in: query, schema: { type: string } }
      responses:
        '200': { description: OK }
    post:
      tags: [Groups]
      summary: Criar grupo (WAHA + SiteUp)
      parameters:
        - $ref: '#/components/parameters/account_id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [whatsapp_group]
              properties:
                whatsapp_group:
                  type: object
                  required: [name, inbox_id]
                  properties:
                    name: { type: string }
                    description: { type: string }
                    inbox_id: { type: integer }
                    max_members: { type: integer, default: 1024 }
                    initial_members:
                      type: array
                      items: { type: string }
                      example: ['5551989769026']
                    admin_members:
                      type: array
                      items: { type: string }
      responses:
        '201': { description: Created }
  /api/v1/accounts/{account_id}/whatsapp_groups/{id}:
    parameters:
      - $ref: '#/components/parameters/account_id'
      - $ref: '#/components/parameters/id'
    get:
      tags: [Groups]
      summary: Detalhe do grupo
      responses: { '200': { description: OK } }
    put:
      tags: [Groups]
      summary: Atualizar grupo
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                whatsapp_group:
                  type: object
                  properties:
                    name: { type: string }
                    description: { type: string }
                    status: { type: string }
                    max_members: { type: integer }
                    welcome_message: { type: string }
      responses: { '200': { description: OK } }
    delete:
      tags: [Groups]
      summary: Excluir / leave grupo
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_groups/adopt:
    post:
      tags: [Groups]
      summary: Adotar grupo, canal ou comunidade já existente
      description: |
        Registra na conta um grupo/canal que já existe no WhatsApp, em vez de criar um novo via WAHA.
        O `jid` define o tipo: `@g.us` = grupo/comunidade, `@newsletter` = canal.
        A inbox precisa enxergar o grupo e, exceto em canal, ser admin nele.
        Comunidade não é criável por API: crie à mão no número conectado e adote o grupo de avisos
        dela com `is_community: true`.
      parameters: [{ $ref: '#/components/parameters/account_id' }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [whatsapp_group]
              properties:
                whatsapp_group:
                  type: object
                  required: [inbox_id, jid]
                  properties:
                    inbox_id: { type: integer }
                    jid:
                      type: string
                      description: Termina em @g.us (grupo/comunidade) ou @newsletter (canal)
                      example: '120363012345678901@g.us'
                    name: { type: string }
                    max_members: { type: integer, default: 1024 }
                    launch_id: { type: string }
                    is_community:
                      type: boolean
                      default: false
                      description: >-
                        true só para o grupo de avisos de uma comunidade; conferido no WAHA
                        (IsAnnounce + LinkedParentJID) antes de aceitar
      responses:
        '201': { description: 'Adotado. Mesmo corpo de POST whatsapp_groups, com chat_kind e community.' }
        '404': { description: inbox_id não pertence à conta }
        '422':
          description: |
            Recusado. Corpo `{ "error": <símbolo> }` — símbolo, não texto pronto para exibir:
            invalid_jid, already_adopted, session_conflict, not_visible_to_session, not_admin,
            waha_unreachable, adoption_failed, community_must_be_group, community_is_parent_jid,
            not_community_group.
  /api/v1/accounts/{account_id}/whatsapp_group_launches/provision:
    post:
      tags: [Groups]
      summary: Provisionar em lote as salas de um lançamento
      description: |
        Cria em lote as salas de um lançamento e, opcionalmente, a campanha guarda-chuva.
        Parâmetros no nível raiz do corpo, sem wrapper.
        Informe `inbox_ids` (rodízio entre inboxes) ou `inbox_id`.
        `chat_kind: channel` é a única via de criação de canal — `POST whatsapp_groups` só cria grupo.
        Canal ignora max_members, initial_members e admin_members.
      parameters: [{ $ref: '#/components/parameters/account_id' }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [launch_id, count, name_prefix]
              properties:
                launch_id: { type: string }
                count: { type: integer, minimum: 1, maximum: 20 }
                name_prefix: { type: string }
                inbox_ids:
                  type: array
                  items: { type: integer }
                inbox_id: { type: integer }
                chat_kind: { type: string, enum: [group, channel], default: group }
                group_type: { type: string, enum: [permanent, temporary, event], default: permanent }
                lifecycle_days: { type: integer }
                max_members: { type: integer, default: 1024 }
                description: { type: string }
                welcome_message: { type: string }
                default_member_label: { type: string }
                initial_members:
                  type: array
                  items: { type: string }
                  example: ['5551989769026']
                admin_members:
                  type: array
                  items: { type: string }
                owner_phone: { type: string }
                create_campaign: { type: boolean, default: false }
                campaign_name: { type: string }
      responses:
        '201':
          description: >-
            Criado. Corpo com ok, errors, groups[] (id, name, waha_group_id, launch_id, chat_kind,
            channel_invite_link) e campaign.
        '404': { description: Nenhuma das inboxes informadas pertence à conta }
        '422':
          description: >-
            Mesmo corpo com ok=false. errors traz launch_id_blank, name_prefix_blank, count_invalid
            ou inbox_blank, ou um item por sala que falhou.
  /api/v1/accounts/{account_id}/whatsapp_groups/available_waha_sessions:
    get:
      tags: [WAHA]
      summary: Sessões WAHA disponíveis
      parameters: [{ $ref: '#/components/parameters/account_id' }]
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_groups/inbox_status:
    get:
      tags: [WAHA]
      summary: Status live das inboxes WAHA
      parameters: [{ $ref: '#/components/parameters/account_id' }]
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_groups/resolve_session:
    post:
      tags: [WAHA]
      summary: Resolver sessão WAHA para inbox
      parameters: [{ $ref: '#/components/parameters/account_id' }]
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                inbox_id: { type: integer }
                session_name: { type: string }
                phone_number: { type: string }
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_groups/{id}/sync_members:
    post:
      tags: [Members]
      summary: Sincronizar membros (async job)
      parameters:
        - $ref: '#/components/parameters/account_id'
        - $ref: '#/components/parameters/id'
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_groups/{id}/invite_code:
    get:
      tags: [Groups]
      summary: Código / link de convite
      parameters:
        - $ref: '#/components/parameters/account_id'
        - $ref: '#/components/parameters/id'
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_groups/{id}/revoke_invite_code:
    post:
      tags: [Groups]
      summary: Revogar invite
      parameters:
        - $ref: '#/components/parameters/account_id'
        - $ref: '#/components/parameters/id'
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_groups/{id}/register_webhook:
    post:
      tags: [Webhooks]
      summary: Registrar webhook WAHA do grupo
      parameters:
        - $ref: '#/components/parameters/account_id'
        - $ref: '#/components/parameters/id'
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_groups/{whatsapp_group_id}/members:
    parameters:
      - $ref: '#/components/parameters/account_id'
      - $ref: '#/components/parameters/whatsapp_group_id'
    get:
      tags: [Members]
      summary: Listar membros
      parameters:
        - { name: include_removed, in: query, schema: { type: boolean } }
      responses: { '200': { description: OK } }
    post:
      tags: [Members]
      summary: Adicionar membros
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phones]
              properties:
                phones:
                  type: array
                  items: { type: string }
                  example: ['5551989769026']
      responses: { '201': { description: Created } }
  /api/v1/accounts/{account_id}/whatsapp_groups/{whatsapp_group_id}/members/{id}:
    delete:
      tags: [Members]
      summary: Remover membro
      parameters:
        - $ref: '#/components/parameters/account_id'
        - $ref: '#/components/parameters/whatsapp_group_id'
        - $ref: '#/components/parameters/id'
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_groups/{whatsapp_group_id}/members/{id}/promote:
    post:
      tags: [Members]
      summary: Promover a admin
      parameters:
        - $ref: '#/components/parameters/account_id'
        - $ref: '#/components/parameters/whatsapp_group_id'
        - $ref: '#/components/parameters/id'
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_groups/{whatsapp_group_id}/members/{id}/demote:
    post:
      tags: [Members]
      summary: Rebaixar admin
      parameters:
        - $ref: '#/components/parameters/account_id'
        - $ref: '#/components/parameters/whatsapp_group_id'
        - $ref: '#/components/parameters/id'
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_group_campaigns:
    get:
      tags: [Campaigns]
      summary: Listar campanhas
      parameters: [{ $ref: '#/components/parameters/account_id' }]
      responses: { '200': { description: OK } }
    post:
      tags: [Campaigns]
      summary: Criar campanha
      description: group_ids é obrigatório. Crie o grupo antes.
      parameters: [{ $ref: '#/components/parameters/account_id' }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [whatsapp_group_campaign]
              properties:
                whatsapp_group_campaign:
                  type: object
                  required: [name, group_ids]
                  properties:
                    name: { type: string }
                    status: { type: string, example: active }
                    group_ids:
                      type: array
                      items: { type: integer }
                      minItems: 1
                    settings:
                      type: object
                      properties:
                        default_inbox_id: { type: integer }
                        admin_inbox_ids:
                          type: array
                          items: { type: integer }
                        max_members: { type: integer, default: 1024 }
                        auto_provision: { type: boolean }
                        name_prefix: { type: string }
                        admin_phones:
                          type: array
                          items: { type: string }
      responses: { '201': { description: Created } }
  /api/v1/accounts/{account_id}/whatsapp_group_campaigns/{id}:
    parameters:
      - $ref: '#/components/parameters/account_id'
      - $ref: '#/components/parameters/id'
    get:
      tags: [Campaigns]
      summary: Detalhe campanha
      responses: { '200': { description: OK } }
    put:
      tags: [Campaigns]
      summary: Atualizar campanha
      description: Edge 500 reportado em rename simples (2026-08-01). Preferir payloads validados.
      requestBody:
        content:
          application/json:
            schema: { type: object }
      responses: { '200': { description: OK } }
    delete:
      tags: [Campaigns]
      summary: Excluir campanha
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_group_campaigns/{id}/health:
    get:
      tags: [Campaigns]
      summary: Health da campanha
      parameters:
        - $ref: '#/components/parameters/account_id'
        - $ref: '#/components/parameters/id'
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_group_campaigns/{id}/broadcasts:
    get:
      tags: [Campaigns]
      summary: Broadcasts da campanha
      parameters:
        - $ref: '#/components/parameters/account_id'
        - $ref: '#/components/parameters/id'
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_group_broadcasts:
    get:
      tags: [Broadcasts]
      summary: Listar disparos
      parameters:
        - $ref: '#/components/parameters/account_id'
        - { name: bucket, in: query, schema: { type: string, enum: [scheduled, completed] } }
        - { name: page, in: query, schema: { type: integer } }
        - { name: per_page, in: query, schema: { type: integer } }
      responses: { '200': { description: OK } }
    post:
      tags: [Broadcasts]
      summary: Criar disparo
      description: |
        media_type: image | video | document | audio | poll | contact | event | location.
        Agendar: scheduled_at ISO-8601. Imediato: omitir scheduled_at.
        Audio: mp3. content vira caption em mídia (exceto audio).
      parameters: [{ $ref: '#/components/parameters/account_id' }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [whatsapp_group_broadcast]
              properties:
                whatsapp_group_broadcast:
                  type: object
                  properties:
                    content: { type: string }
                    media_type: { type: string }
                    media_url: { type: string }
                    poll_options:
                      type: array
                      items: { type: string }
                    poll_multiple: { type: boolean }
                    extras:
                      type: object
                      description: contact_*, event_*, latitude/longitude/title/address
                    target_group_ids:
                      type: array
                      items: { type: integer }
                    whatsapp_group_campaign_id: { type: integer }
                    scheduled_at: { type: string, format: date-time }
                    send_speed: { type: number }
                    remove_members_after: { type: boolean, default: false }
      responses: { '201': { description: Created } }
  /api/v1/accounts/{account_id}/whatsapp_group_broadcasts/{id}:
    get:
      tags: [Broadcasts]
      summary: Detalhe do disparo
      parameters:
        - $ref: '#/components/parameters/account_id'
        - $ref: '#/components/parameters/id'
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_group_broadcasts/{id}/cancel:
    post:
      tags: [Broadcasts]
      summary: Cancelar disparo agendado/queued
      parameters:
        - $ref: '#/components/parameters/account_id'
        - $ref: '#/components/parameters/id'
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_group_activities:
    get:
      tags: [Activities]
      summary: Log de atividades
      parameters: [{ $ref: '#/components/parameters/account_id' }]
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_group_webhooks:
    get:
      tags: [Webhooks]
      summary: Listar webhooks de entrada
      parameters:
        - $ref: '#/components/parameters/account_id'
        - { name: group_id, in: query, schema: { type: integer } }
      responses: { '200': { description: OK } }
    post:
      tags: [Webhooks]
      summary: Criar webhook de entrada
      parameters: [{ $ref: '#/components/parameters/account_id' }]
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                whatsapp_group_webhook:
                  type: object
                  properties:
                    name: { type: string }
                    platform: { type: string }
                    active: { type: boolean }
                    whatsapp_group_id: { type: integer }
                    create_contact: { type: boolean }
                    auto_invite: { type: boolean }
                    field_mapping: { type: object }
      responses: { '201': { description: Created } }
  /api/v1/accounts/{account_id}/whatsapp_group_webhooks/{id}:
    parameters:
      - $ref: '#/components/parameters/account_id'
      - $ref: '#/components/parameters/id'
    get:
      tags: [Webhooks]
      summary: Detalhe webhook (webhook_url + token)
      responses: { '200': { description: OK } }
    put:
      tags: [Webhooks]
      summary: Atualizar webhook
      requestBody:
        content:
          application/json:
            schema: { type: object }
      responses: { '200': { description: OK } }
    delete:
      tags: [Webhooks]
      summary: Excluir webhook
      responses: { '200': { description: OK } }
  /api/v1/accounts/{account_id}/whatsapp_group_sequences:
    get:
      tags: [Sequences]
      summary: Listar sequências
      parameters: [{ $ref: '#/components/parameters/account_id' }]
      responses: { '200': { description: OK } }
    post:
      tags: [Sequences]
      summary: Criar sequência
      description: Requer schema completo de steps. Create mínimo pode 422/500 (smoke 2026-08-01).
      parameters: [{ $ref: '#/components/parameters/account_id' }]
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                whatsapp_group_sequence:
                  type: object
                  properties:
                    name: { type: string }
                    status: { type: string }
                    target_group_ids:
                      type: array
                      items: { type: integer }
                    whatsapp_group_sequence_steps_attributes:
                      type: array
                      items: { type: object }
      responses: { '201': { description: Created } }
  /api/v1/accounts/{account_id}/whatsapp_group_sequences/{id}:
    parameters:
      - $ref: '#/components/parameters/account_id'
      - $ref: '#/components/parameters/id'
    get:
      tags: [Sequences]
      summary: Detalhe sequência
      responses: { '200': { description: OK } }
    put:
      tags: [Sequences]
      summary: Atualizar sequência
      requestBody:
        content:
          application/json:
            schema: { type: object }
      responses: { '200': { description: OK } }
    delete:
      tags: [Sequences]
      summary: Excluir sequência
      responses: { '200': { description: OK } }
components:
  parameters:
    account_id:
      name: account_id
      in: path
      required: true
      schema: { type: integer }
    id:
      name: id
      in: path
      required: true
      schema: { type: integer }
    whatsapp_group_id:
      name: whatsapp_group_id
      in: path
      required: true
      schema: { type: integer }
  securitySchemes:
    userApiKey:
      type: apiKey
      in: header
      name: api_access_token
security:
  - userApiKey: []
