{"openapi":"3.1.0","info":{"title":"OhZap — gateway de WhatsApp da Lions Intelligence","version":"0.2.0","description":"api de whatsapp da lions. um contrato so, dois motores por baixo: **meta cloud oficial**\n(+ coexistencia) e **linked-device proprio** (qr, protocolo do whatsapp web — nao oficial,\nuse com responsabilidade).\n\n- autenticacao da api: header `x-api-key` (chave criada no painel `/painel`).\n- envelope: respostas de sucesso vem em `{ \"data\": ... }`; erros em `{ \"error\": \"...\" }`.\n- cotas do plano: `429 { error, plan, used, limit }` ao estourar instancias ou mensagens/mes.\n- webhooks assinados: header `x-ohzap-signature` = HMAC-SHA256 do `timestamp.corpo` com o segredo da instancia.\n- painel do dev (`/panel/*`): login por codigo no whatsapp, cookie `ohzap_panel` (httponly), mesma origem.\n"},"servers":[{"url":"https://ohzap.atendeaioh.app"}],"tags":[{"name":"saude"},{"name":"instancias"},{"name":"mensagens"},{"name":"contatos"},{"name":"publico"},{"name":"painel"}],"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key"},"panelCookie":{"type":"apiKey","in":"cookie","name":"ohzap_panel"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"}}},"QuotaError":{"type":"object","properties":{"error":{"type":"string"},"plan":{"type":"string","example":"dev"},"used":{"type":"integer"},"limit":{"type":"integer"}}},"Instance":{"type":"object","properties":{"id":{"type":"string","example":"ozi_ab12cd34ef56"},"name":{"type":"string"},"provider":{"type":"string","enum":["meta_cloud","meta_coexistence","linked_device"]},"status":{"type":"string","enum":["created","waiting_qr","connecting","connected","disconnected","logged_out"]},"phoneNumber":{"type":["string","null"]},"webhookUrl":{"type":["string","null"]},"hasWebhookSecret":{"type":"boolean"},"hasMetaCredentials":{"type":"boolean"},"counters":{"type":"object","properties":{"sent":{"type":"integer"},"received":{"type":"integer"},"reconnects":{"type":"integer"}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"lastError":{"type":["string","null"]}}},"SendResult":{"type":"object","properties":{"providerMessageId":{"type":"string"}}},"ApiKey":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":["string","null"]},"prefix":{"type":"string","example":"ohz_ab12cd34"},"createdAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"revokedAt":{"type":["string","null"],"format":"date-time"}}},"UsageBucket":{"type":"object","properties":{"sent":{"type":"integer"},"received":{"type":"integer"},"media":{"type":"integer"},"total":{"type":"integer"}}}},"responses":{"Unauthorized":{"description":"chave de api ausente ou invalida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"nao encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Quota":{"description":"cota do plano atingida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaError"}}}}}},"security":[{"apiKey":[]}],"paths":{"/health":{"get":{"tags":["saude"],"security":[],"summary":"vida do servico (sem api-key, fora do rate limit)","responses":{"200":{"description":"ok","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"service":{"type":"string"},"instances":{"type":"integer"}}}}}}}}},"/v1/instances":{"post":{"tags":["instancias"],"summary":"cria instancia e ja inicia o motor","description":"sem `provider`, escolhe sozinho (meta se tiver credencial, senao qr). checa `max_instances` do plano.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":80},"provider":{"type":"string","enum":["meta_cloud","meta_coexistence","linked_device"]},"meta":{"type":"object","properties":{"accessToken":{"type":"string"},"phoneNumberId":{"type":"string"}}}}}}}},"responses":{"201":{"description":"criada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Instance"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"description":"entrada invalida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/Quota"}}},"get":{"tags":["instancias"],"summary":"lista as instancias do tenant (paginada)","description":"`data` continua sendo o array; `total`, `limit` e `offset` vem ao lado. default 50, maximo 200.","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":200}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"lista","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Instance"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/instances/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["instancias"],"summary":"status, numero, contadores, ultimo erro","responses":{"200":{"description":"ok","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Instance"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["instancias"],"summary":"para o motor, remove o cadastro e apaga a sessao","responses":{"200":{"description":"removida"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/instances/{id}/qr":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["instancias"],"summary":"qr atual em data-url (motor qr, enquanto aguardando pareamento)","responses":{"200":{"description":"qr disponivel","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"qrDataUrl":{"type":"string"},"status":{"type":"string"}}}}}}}},"409":{"description":"sem qr no momento (status atual no corpo)"}}}},"/v1/instances/{id}/webhook":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"patch":{"tags":["instancias"],"summary":"configura o webhook; devolve o segredo UMA vez","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"}}}}}},"responses":{"200":{"description":"configurado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"url":{"type":"string"},"secret":{"type":"string"}}}}}}}}}}},"/v1/instances/{id}/restart":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"post":{"tags":["instancias"],"summary":"reinicia o motor da instancia","responses":{"200":{"description":"reiniciada"}}}},"/v1/instances/{id}/messages":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"post":{"tags":["mensagens"],"summary":"envia texto ou midia","description":"`idempotencyKey` repetida (24 h) devolve o mesmo resultado sem reenviar (`deduplicated: true`).\ncheca `max_messages_month` do plano contra as mensagens enviadas no mes.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["to"],"properties":{"to":{"type":"string","description":"e.164 sem + ou id de grupo (...@g.us)","example":"5534999990000"},"text":{"type":"string"},"idempotencyKey":{"type":"string"},"media":{"type":"object","properties":{"kind":{"type":"string","enum":["image","video","audio","document","sticker"]},"url":{"type":"string","format":"uri","description":"url publica de onde o gateway baixa o arquivo"},"mimetype":{"type":"string"},"fileName":{"type":"string"},"caption":{"type":"string"},"ptt":{"type":"boolean","description":"audio como mensagem de voz"}}}}}}}},"responses":{"200":{"description":"enviada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SendResult"},"deduplicated":{"type":"boolean"}}}}}},"422":{"description":"entrada invalida"},"429":{"$ref":"#/components/responses/Quota"},"502":{"description":"falha no motor ao enviar"}}}},"/v1/instances/{id}/messages/{messageId}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string"}},{"name":"chat","in":"query","required":true,"schema":{"type":"string"},"description":"telefone ou jid do chat onde a mensagem esta"}],"delete":{"tags":["mensagens"],"summary":"apagar para todos","responses":{"200":{"description":"apagada"}}}},"/v1/instances/{id}/media/{messageId}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["mensagens"],"summary":"arquivo de midia recebida (baixado e guardado pelo motor)","responses":{"200":{"description":"binario com content-type do arquivo"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/instances/{id}/contacts/{phone}/avatar":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"phone","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["contatos"],"summary":"url da foto de perfil (null se nao houver)","responses":{"200":{"description":"ok"}}}},"/v1/instances/{id}/contacts/{phone}/exists":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"phone","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["contatos"],"summary":"o numero tem whatsapp?","responses":{"200":{"description":"ok","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"exists":{"type":"boolean"},"jid":{"type":["string","null"]}}}}}}}}}}},"/v1/openapi.json":{"get":{"tags":["publico"],"security":[],"summary":"este contrato em json (renderizado em /docs)","responses":{"200":{"description":"openapi"}}}},"/public/access-request":{"post":{"tags":["publico"],"security":[],"summary":"pedido de acesso (lista de espera). alias — POST /api/acesso. rate limit 10/15 min por ip.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["nome","contato"],"properties":{"nome":{"type":"string"},"contato":{"type":"string","description":"email ou whatsapp"},"uso":{"type":"string"},"origem":{"type":"string"}}}}}},"responses":{"200":{"description":"recebido"},"422":{"description":"nome/contato obrigatorios"},"429":{"description":"rate limit"}}}},"/panel/auth/request-code":{"post":{"tags":["painel"],"security":[],"summary":"envia codigo de 6 digitos pro whatsapp cadastrado (3/15 min por telefone e por ip)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["phone"],"properties":{"phone":{"type":"string","example":"5534999990000"}}}}}},"responses":{"200":{"description":"enviado"},"404":{"description":"telefone nao cadastrado; peca acesso na landing"},"429":{"description":"rate limit"},"502":{"description":"nao foi possivel enviar pelo whatsapp"},"503":{"description":"envio nao configurado no gateway"}}}},"/panel/auth/verify":{"post":{"tags":["painel"],"security":[],"summary":"valida o codigo (10 min, 5 tentativas) e cria a sessao (cookie ohzap_panel, 30 dias)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["phone","code"],"properties":{"phone":{"type":"string"},"code":{"type":"string","pattern":"^[0-9]{6}$"}}}}}},"responses":{"200":{"description":"sessao criada (set-cookie)"},"401":{"description":"codigo incorreto/expirado"},"429":{"description":"codigo bloqueado por excesso de tentativas"}}}},"/panel/auth/logout":{"post":{"tags":["painel"],"security":[{"panelCookie":[]}],"summary":"encerra a sessao","responses":{"200":{"description":"ok"}}}},"/panel/me":{"get":{"tags":["painel"],"security":[{"panelCookie":[]}],"summary":"developer + tenant + plano/limites","responses":{"200":{"description":"ok"},"401":{"description":"sem sessao"}}}},"/panel/keys":{"get":{"tags":["painel"],"security":[{"panelCookie":[]}],"summary":"chaves do tenant (sem o segredo)","responses":{"200":{"description":"ok","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}}}}}}}},"post":{"tags":["painel"],"security":[{"panelCookie":[]}],"summary":"cria chave — o segredo `apiKey` aparece UMA vez","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","maxLength":60}}}}}},"responses":{"201":{"description":"criada"}}}},"/panel/keys/{id}":{"delete":{"tags":["painel"],"security":[{"panelCookie":[]}],"summary":"revoga a chave (para de autenticar na hora)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"revogada"},"404":{"description":"nao encontrada ou ja revogada"}}}},"/panel/instances":{"get":{"tags":["painel"],"security":[{"panelCookie":[]}],"summary":"instancias do tenant","responses":{"200":{"description":"ok"}}}},"/panel/usage":{"get":{"tags":["painel"],"security":[{"panelCookie":[]}],"summary":"consumo do mes (por dia e por instancia) + limite do plano","parameters":[{"name":"month","in":"query","schema":{"type":"string","pattern":"^[0-9]{4}-[0-9]{2}$"}}],"responses":{"200":{"description":"ok","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"month":{"type":"string"},"limit":{"type":["integer","null"]},"totals":{"$ref":"#/components/schemas/UsageBucket"},"byDay":{"type":"array","items":{"$ref":"#/components/schemas/UsageBucket"}},"byInstance":{"type":"array","items":{"$ref":"#/components/schemas/UsageBucket"}}}}}}}}}}}},"/panel/access-requests":{"get":{"tags":["painel"],"security":[{"panelCookie":[]}],"summary":"pedidos de acesso (so developer do tenant admin)","responses":{"200":{"description":"ok"},"403":{"description":"nao e admin"}}}},"/panel/access-requests/{id}/approve":{"post":{"tags":["painel"],"security":[{"panelCookie":[]}],"summary":"aprova o lead — cria tenant + developer + chave inicial e manda a chave no whatsapp","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tenantId":{"type":"string","description":"slug; default derivado do nome"},"name":{"type":"string"},"plan":{"type":"string","enum":["dev","start","agencia","provider"],"default":"dev"},"phone":{"type":"string","description":"obrigatorio se o contato do pedido nao for whatsapp"}}}}}},"responses":{"201":{"description":"aprovado (apiKey volta na resposta; delivered diz se o whatsapp saiu)"},"409":{"description":"ja aprovado ou telefone ja cadastrado"},"422":{"description":"contato nao e whatsapp"}}}}}}