{"openapi":"3.0.3","info":{"title":"Ghost Call","version":"1.0.0","description":"API REST oficial da **Ghost Call** — chamadas de voz do WhatsApp, sessões, mensagens, gravações e webhooks.\n\n### Autenticação\n\nEnvie a chave da Ghost Call em todas as requisições:\n\n```\nX-API-Key: <sua-chave>\n```\n\nOpcional: `X-Client-Id` identifica o dono da chamada e filtra eventos.\n\n### Base URL\n\n`https://{subdominio}-ligacao.ghostapi.online`\n\n### Fluxo recomendado\n\n1. `POST /api/sessions` — crie a sessão WhatsApp.\n2. `POST /api/sessions/{sid}/pair` — inicie o QR e acompanhe `/api/events`.\n3. `POST /api/sessions/{sid}/calls` — disque para o número de destino.\n4. `GET /api/recordings` — consulte as gravações (MP3) ao encerrar.\n\n### Áudio WebRTC\n\nA API controla a sinalização da chamada. O áudio em tempo real exige um cliente WebRTC (navegador) com microfone — não dá para transmitir voz só com HTTP.\n\n### Formato de número\n\nUse DDI + DDD + número, sem `+` e sem espaços. Exemplo: `5511999999999`.","contact":{"name":"Ghost API","url":"https://ghostapi.online"}},"servers":[{"url":"https://{subdominio}-ligacao.ghostapi.online","description":"Sua API de ligação (subdomínio branded)"}],"tags":[{"name":"Sessões","description":"Contas WhatsApp (cada sessão = um aparelho pareado)"},{"name":"Chamadas","description":"Iniciar, atender, rejeitar e encerrar ligações"},{"name":"Mensagens","description":"Envio de texto, imagem, áudio, vídeo e documento"},{"name":"Webhook","description":"URL que recebe os eventos da sessão"},{"name":"Chatwoot","description":"Integração com inbox API do Chatwoot"},{"name":"Gravações","description":"Metadados e download do MP3 de cada chamada"},{"name":"Eventos & Histórico","description":"SSE em tempo real e histórico de ligações"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Chave da Ghost Call (header X-API-Key)"}},"parameters":{"ClientId":{"name":"X-Client-Id","in":"header","required":false,"schema":{"type":"string","example":"meuapp"},"description":"Identificador do cliente (dono da chamada / filtro de eventos)."},"Sid":{"name":"sid","in":"path","required":true,"schema":{"type":"string"},"description":"ID da sessão (conta WhatsApp)."},"CallId":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"ID da chamada."},"RecordingId":{"name":"rid","in":"path","required":true,"schema":{"type":"string"},"description":"ID da gravação."}},"schemas":{"Session":{"type":"object","properties":{"id":{"type":"string","example":"6458cca60a17c0cefa71de9da9dc92a2"},"name":{"type":"string","example":"WhatsApp"},"jid":{"type":"string","example":"556192264108:13@s.whatsapp.net"},"state":{"type":"string","enum":["open","connecting","close"],"example":"open"},"paired":{"type":"boolean","example":true}}},"StartCallRequest":{"type":"object","required":["phone"],"properties":{"phone":{"type":"string","example":"5561999999999","description":"DDI+DDD+número"},"duration_ms":{"type":"integer","example":300000},"record":{"type":"boolean","example":false,"description":"Força a gravação desta chamada mesmo que a gravação global esteja desligada (`GHOST_CALL_RECORD=false`). Com a gravação global ligada (padrão), toda chamada já é gravada independentemente deste campo.\n"}}},"Recording":{"type":"object","description":"Metadados de uma gravação de chamada.","properties":{"id":{"type":"string","example":"9f1c2a...","description":"ID da gravação"},"sessionId":{"type":"string"},"callId":{"type":"string"},"phone":{"type":"string","example":"5561999999999","description":"Número vinculado (só dígitos)"},"direction":{"type":"string","enum":["inbound","outbound"]},"startedAt":{"type":"integer","format":"int64","description":"epoch ms"},"endedAt":{"type":"integer","format":"int64","nullable":true,"description":"epoch ms"},"durationMs":{"type":"integer","format":"int64"},"format":{"type":"string","enum":["mp3","wav"],"example":"mp3"},"sizeBytes":{"type":"integer","format":"int64"},"status":{"type":"string","enum":["recording","ready","failed"]},"url":{"type":"string","example":"/api/recordings/9f1c2a.../file","description":"URL de download do arquivo"}}},"MediaBody":{"type":"object","required":["to"],"description":"Envie a mídia por **base64** OU por **url** (um dos dois).","properties":{"to":{"type":"string","example":"5561999999999","description":"Número ou JID completo"},"base64":{"type":"string","description":"Conteúdo em base64 (aceita prefixo data:...)"},"url":{"type":"string","description":"URL de onde baixar a mídia"},"mimetype":{"type":"string","example":"image/jpeg"}}}},"responses":{"SendOK":{"description":"Mensagem enviada","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"ID da mensagem no WhatsApp"},"to":{"type":"string"},"timestamp":{"type":"integer","format":"int64"}}}}}}}},"paths":{"/api/config":{"get":{"tags":["Sessões"],"summary":"Configuração do servidor (limite de chamadas simultâneas)","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"maxCallsPerSession":{"type":"integer","example":8,"description":"Máx. de chamadas simultâneas por sessão (0 = ilimitado)"},"recordingEnabled":{"type":"boolean","example":true,"description":"Se a gravação automática de chamadas está ligada"}}}}}}}}},"/api/sessions":{"get":{"tags":["Sessões"],"summary":"Lista as contas","parameters":[{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"$ref":"#/components/schemas/Session"}}}}}}}}},"post":{"tags":["Sessões"],"summary":"Cria uma conta","parameters":[{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","example":"Minha conta"}}}}}},"responses":{"200":{"description":"Criada","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}}}}}}}}},"/api/sessions/{sid}":{"delete":{"tags":["Sessões"],"summary":"Remove a conta","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"204":{"description":"Removida"}}}},"/api/sessions/{sid}/pair":{"post":{"tags":["Sessões"],"summary":"Inicia o pareamento (QR sai via SSE)","description":"Dispara a geração do QR Code; acompanhe os eventos `session-qr` / `auth-state` em `/api/events`.","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"204":{"description":"Pareamento iniciado"}}}},"/api/sessions/{sid}/logout":{"post":{"tags":["Sessões"],"summary":"Desconecta a conta (mantém a sessão)","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"204":{"description":"Desconectada"}}}},"/api/sessions/{sid}/calls":{"get":{"tags":["Chamadas"],"summary":"Quantas chamadas ativas a sessão tem (e o limite)","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"active":{"type":"integer","example":2},"maxCallsPerSession":{"type":"integer","example":8}}}}}}}},"post":{"tags":["Chamadas"],"summary":"Inicia uma chamada (faz o número de destino tocar)","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartCallRequest"}}}},"responses":{"200":{"description":"Chamada iniciada","content":{"application/json":{"schema":{"type":"object","properties":{"call":{"type":"object","properties":{"callId":{"type":"string"}}}}}}}},"503":{"description":"Conta não pareada"}}}},"/api/sessions/{sid}/calls/{id}/accept":{"post":{"tags":["Chamadas"],"summary":"Atende uma chamada recebida","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/CallId"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"Atendida"}}}},"/api/sessions/{sid}/calls/{id}/reject":{"post":{"tags":["Chamadas"],"summary":"Rejeita uma chamada recebida","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/CallId"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"Rejeitada"}}}},"/api/sessions/{sid}/calls/{id}":{"delete":{"tags":["Chamadas"],"summary":"Encerra a chamada","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/CallId"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"204":{"description":"Encerrada"}}}},"/api/sessions/{sid}/messages/text":{"post":{"tags":["Mensagens"],"summary":"Envia uma mensagem de texto","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["to","text"],"properties":{"to":{"type":"string","example":"5561999999999","description":"Número (DDI+DDD) ou JID completo (...@s.whatsapp.net / ...@g.us)"},"text":{"type":"string","example":"Olá!"}}}}}},"responses":{"200":{"$ref":"#/components/responses/SendOK"},"503":{"description":"Conta não pareada"}}}},"/api/sessions/{sid}/messages/image":{"post":{"tags":["Mensagens"],"summary":"Envia uma imagem","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/MediaBody"},{"type":"object","properties":{"caption":{"type":"string","example":"minha foto"}}}]}}}},"responses":{"200":{"$ref":"#/components/responses/SendOK"}}}},"/api/sessions/{sid}/messages/audio":{"post":{"tags":["Mensagens"],"summary":"Envia um áudio (use ptt=true para nota de voz)","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/MediaBody"},{"type":"object","properties":{"ptt":{"type":"boolean","example":true,"description":"true = nota de voz (push-to-talk)"}}}]}}}},"responses":{"200":{"$ref":"#/components/responses/SendOK"}}}},"/api/sessions/{sid}/messages/video":{"post":{"tags":["Mensagens"],"summary":"Envia um vídeo","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/MediaBody"},{"type":"object","properties":{"caption":{"type":"string"}}}]}}}},"responses":{"200":{"$ref":"#/components/responses/SendOK"}}}},"/api/sessions/{sid}/messages/document":{"post":{"tags":["Mensagens"],"summary":"Envia um documento/arquivo","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/MediaBody"},{"type":"object","properties":{"filename":{"type":"string","example":"contrato.pdf"}}}]}}}},"responses":{"200":{"$ref":"#/components/responses/SendOK"}}}},"/api/sessions/{sid}/webhook":{"get":{"tags":["Webhook"],"summary":"Consulta a URL de webhook da sessão","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"webhook":{"type":"string"}}}}}}}},"post":{"tags":["Webhook"],"summary":"Define a URL de webhook (recebe os eventos da sessão)","description":"Eventos enviados (POST JSON) no formato `{ session, event, timestamp, data }`.\n`event` pode ser:\n- `message` — mensagem recebida (com `data.text`, `data.type`, `data.raw`...);\n- `receipt` — confirmações de entrega/leitura;\n- `recording` — gravação de uma chamada finalizada. `data` traz\n  `{ id, callId, phone, direction, durationMs, format, sizeBytes, startedAt, endedAt, url }`,\n  onde `url` é o link para baixar o MP3 (absoluto se `GHOST_CALL_PUBLIC_URL` estiver definida).\n","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","example":"https://meu-sistema/webhook/ghost-call"}}}}}},"responses":{"200":{"description":"Salvo","content":{"application/json":{"schema":{"type":"object","properties":{"webhook":{"type":"string"}}}}}}}},"delete":{"tags":["Webhook"],"summary":"Remove o webhook da sessão","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"204":{"description":"Removido"}}}},"/api/sessions/{sid}/chatwoot":{"get":{"tags":["Chatwoot"],"summary":"Consulta a integração Chatwoot da sessão (token omitido)","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"chatwoot":{"type":"object"}}}}}}}},"post":{"tags":["Chatwoot"],"summary":"Conecta a sessão a uma inbox do Chatwoot (canal API)","description":"Configura a integração. Crie no Chatwoot uma inbox do tipo **API**, pegue o\n`inbox_id`, `inbox_identifier`, o `account_id` e um `account_token` (Access Token\ndo perfil/agente). Depois aponte o **webhook da inbox** para\n`POST /api/sessions/{sid}/chatwoot/webhook` para as respostas dos agentes saírem no WhatsApp.\n","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","account_id","account_token","inbox_id"],"properties":{"url":{"type":"string","example":"https://chatwoot.seudominio.com"},"account_id":{"type":"integer","example":1},"account_token":{"type":"string","example":"xxxxxxxxxxxxxxxx"},"inbox_id":{"type":"integer","example":5},"inbox_identifier":{"type":"string","example":"abcd1234"}}}}}},"responses":{"200":{"description":"Conectado"},"400":{"description":"Campos obrigatórios faltando"}}},"delete":{"tags":["Chatwoot"],"summary":"Desconecta a integração Chatwoot","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"204":{"description":"Desconectado"}}}},"/api/sessions/{sid}/chatwoot/webhook":{"post":{"tags":["Chatwoot"],"summary":"Endpoint que recebe o webhook do Chatwoot (agente -> WhatsApp)","description":"Configure este endereço como **webhook da inbox** no Chatwoot. Ao receber\n`message_created`/outgoing, envia a mensagem (texto e anexos) pelo WhatsApp.\n","parameters":[{"$ref":"#/components/parameters/Sid"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Payload do webhook do Chatwoot"}}}},"responses":{"200":{"description":"Processado"}}}},"/api/recordings":{"get":{"tags":["Gravações"],"summary":"Lista as gravações (filtra por número e/ou sessão)","description":"Retorna as gravações mais recentes. Use `phone` para buscar todas as gravações\nde um número específico (o caso de uso de integração com outros sistemas).\n","parameters":[{"$ref":"#/components/parameters/ClientId"},{"name":"phone","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra pelo número (DDI+DDD+número, só dígitos)."},{"name":"session","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra por ID de sessão."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"maximum":500}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"recordings":{"type":"array","items":{"$ref":"#/components/schemas/Recording"}}}}}}}}}},"/api/recordings/{rid}":{"get":{"tags":["Gravações"],"summary":"Metadados de uma gravação","parameters":[{"$ref":"#/components/parameters/RecordingId"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Recording"}}}},"404":{"description":"Gravação não encontrada"}}},"delete":{"tags":["Gravações"],"summary":"Apaga uma gravação (arquivo + metadados)","parameters":[{"$ref":"#/components/parameters/RecordingId"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"204":{"description":"Apagada"},"404":{"description":"Gravação não encontrada"}}}},"/api/recordings/{rid}/file":{"get":{"tags":["Gravações"],"summary":"Baixa o arquivo da gravação (MP3)","description":"Devolve o arquivo de áudio (MP3 por padrão; WAV em fallback se o ffmpeg falhar).\nSuporta requisições com `Range` (permite \"seek\" no player). Disponível apenas\nquando o `status` da gravação é `ready`.\n","parameters":[{"$ref":"#/components/parameters/RecordingId"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"Arquivo de áudio","content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Arquivo não disponível"}}}},"/api/sessions/{sid}/recordings":{"get":{"tags":["Gravações"],"summary":"Lista as gravações de uma sessão","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"},{"name":"phone","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"maximum":500}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"recordings":{"type":"array","items":{"$ref":"#/components/schemas/Recording"}}}}}}}}}},"/api/sessions/{sid}/history":{"get":{"tags":["Eventos & Histórico"],"summary":"Histórico de chamadas da conta (últimas 50)","parameters":[{"$ref":"#/components/parameters/Sid"},{"$ref":"#/components/parameters/ClientId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object"}}}}}}}}}},"/api/events":{"get":{"tags":["Eventos & Histórico"],"summary":"Stream de eventos em tempo real (SSE)","description":"Server-Sent Events. Tipos: `session-list`, `session-qr`, `auth-state`,\n`call-list`, `call-status`, `call-ended`, `incoming`, `incoming-claimed`,\n`recording-ready` (gravação MP3 pronta: `{ sessionId, callId, recordingId, phone, url, durationMs, format }`).\n","parameters":[{"name":"clientId","in":"query","schema":{"type":"string"},"description":"Identificador do cliente."}],"responses":{"200":{"description":"Stream text/event-stream","content":{"text/event-stream":{"schema":{"type":"string"}}}}}}}}}