Pular para o conteúdo principal

Media

Endpoints para gerenciamento de mídia: download de inbound, upload para Meta, transcrição (Whisper) e OCR.

Base path: /v1/media

Modelo Media

Quando o gateway recebe uma mensagem com mídia (image, video, audio, document), ele:

  1. Baixa do servidor de origem (Meta CDN ou Baileys S3).
  2. Faz upload para o S3 interno (bucket configurado por env).
  3. Cria registro MediaObject com TTL 7 dias.
  4. No evento message:received, devolve o mediaUrl apontando para GET /v1/media/:id (proxy do gateway, não o link cru).
{
"id": "med-1234-5678-...",
"messageId": "ABCD1234...",
"contentType": "image/jpeg",
"sizeBytes": 234567,
"filename": "foto.jpg",
"expiresAt": "2026-05-28T14:00:00.000Z",
"ackedAt": null,
"deletedAt": null
}

TTL e ACK pattern:

  • Mídia que não recebe ACK em 7 dias é deletada automaticamente (S3 lifecycle policy).
  • Cliente pode fazer ACK explícito (POST /:id/ack) após baixar — gateway deleta na hora.
  • Vencimento dispara evento media:expired no webhook.

GET /v1/media/:id

Redireciona para signed URL S3 (TTL 10min). Use em <img src>, <audio src>, ou clientes HTTP normais.

curl -L https://wpp.ogmma.com.br/v1/media/med-1234-... \
-H "X-API-Key: ak_live_..." \
-o foto.jpg

Response 302Location: https://s3.amazonaws.com/...?X-Amz-Signature=...

Errors

HTTPCodeQuando
404MEDIA_NOT_FOUNDID inexistente ou de outra account
410MEDIA_EXPIREDTTL 7d passou
410MEDIA_DELETEDACK já fez delete explícito

GET /v1/media/:id/info

Metadata sem fazer download.

curl https://wpp.ogmma.com.br/v1/media/med-1234-.../info \
-H "X-API-Key: ak_live_..."

Response 200 — mesmo objeto Media descrito acima.


POST /v1/media/:id/ack

ACK explícito: marca como ackedAt + dispara delete S3. Use depois de baixar e persistir do seu lado.

curl -X POST https://wpp.ogmma.com.br/v1/media/med-1234-.../ack \
-H "X-API-Key: ak_live_..."

Response 204 (idempotente — chamadas múltiplas retornam 204)


POST /v1/media/upload

Upload de mídia para o Meta (gera metaMediaId reutilizável). Use quando você quer mandar a mesma mídia para vários destinatários sem rebaixar — cache de envio.

Headers

HeaderObrigatórioDescrição
X-API-Keysim
X-Phone-IdsimUUID do phone WABA
Content-Typesimapplication/json

Body

CampoTipoObrigatórioDescrição
mediaUrlURLsimURL pública HTTPS de onde baixar
mimeTypestringsimEx.: image/jpeg
filenamestringnão255 chars
curl -X POST https://wpp.ogmma.com.br/v1/media/upload \
-H "X-API-Key: ak_live_..." \
-H "X-Phone-Id: 5f7b2e1c-..." \
-H "Content-Type: application/json" \
-d '{
"mediaUrl": "https://cdn.acme.com/foto.jpg",
"mimeType": "image/jpeg",
"filename": "foto.jpg"
}'

Response 201

{ "metaMediaId": "1234567890" }

Use o metaMediaId no content.metaMediaId de POST /v1/messages em vez de mediaUrl.

Limites Meta: 5MB image, 16MB video/audio, 100MB document. Para arquivos maiores em templates, use Resumable Upload abaixo.


POST /v1/media/upload/resumable

Upload reumível Meta. Necessário para arquivos ≥5MB usados como header_handle em templates ou em uploads grandes. Retorna um handle opaco (h:...) para usar em template.components.

Headers

HeaderObrigatório
X-API-Keysim
X-Phone-Idsim

Body

CampoTipoObrigatório
mediaUrlURLsim
mimeTypestringsim
fileNamestringsim
appIdstringsim (Meta App ID — geralmente o mesmo de META_APP_ID)
curl -X POST https://wpp.ogmma.com.br/v1/media/upload/resumable \
-H "X-API-Key: ak_live_..." \
-H "X-Phone-Id: 5f7b2e1c-..." \
-H "Content-Type: application/json" \
-d '{
"mediaUrl": "https://cdn.acme.com/video.mp4",
"mimeType": "video/mp4",
"fileName": "promo.mp4",
"appId": "1234567890"
}'

Response 201

{ "handle": "h:ABCD1234...", "sessionId": "...:abc" }

DELETE /v1/media/meta/:metaId

Deleta uma mídia no Meta (libera storage da WABA).

Headers: X-Phone-Id obrigatório.

curl -X DELETE https://wpp.ogmma.com.br/v1/media/meta/1234567890 \
-H "X-API-Key: ak_live_..." \
-H "X-Phone-Id: 5f7b2e1c-..."

Response 204


Transcrição (Whisper)

Transcreve áudios via OpenAI Whisper. BYO-key: account precisa ter configurado openaiApiKey via PATCH /v1/account/integrations/openai.

POST /v1/media/:id/transcribe

Enfileira transcrição. Idempotente — cache hit retorna direto.

curl -X POST https://wpp.ogmma.com.br/v1/media/med-1234-.../transcribe \
-H "X-API-Key: ak_live_..."

Response 202 (primeira chamada)

{ "status": "PENDING", "mediaId": "med-1234-..." }

Response 200 (cache hit — já transcrito antes)

{
"status": "COMPLETED",
"text": "Olá, gostaria de saber...",
"language": "pt",
"durationSeconds": 12.5,
"transcribedAt": "2026-05-21T14:00:30.000Z",
"cached": true
}

Response 202 (já em progresso)

{ "status": "PROCESSING", "mediaId": "med-1234-..." }

Errors

HTTPCodeQuando
400MEDIA_NOT_AUDIOcontentType não é audio/*
410MEDIA_EXPIREDTTL 7d passou
410MEDIA_DELETEDACK já deletou

GET /v1/media/:id/transcription

Estado atual. Polling-friendly.

curl https://wpp.ogmma.com.br/v1/media/med-1234-.../transcription \
-H "X-API-Key: ak_live_..."

Response 200

{
"status": "COMPLETED",
"text": "Olá, gostaria de saber...",
"language": "pt",
"durationSeconds": 12.5,
"transcribedAt": "2026-05-21T14:00:30.000Z",
"error": null
}

Status: PENDINGPROCESSINGCOMPLETED ou FAILED (com error populado).

Errors

HTTPCodeQuando
404TRANSCRIPTION_NOT_REQUESTEDVocê não chamou POST /transcribe ainda

OCR (extração de texto de imagem)

Igual ao transcribe, mas para image/*. Usa OpenAI Vision (gpt-4o-mini com prompt OCR).

POST /v1/media/:id/ocr

curl -X POST https://wpp.ogmma.com.br/v1/media/med-1234-.../ocr \
-H "X-API-Key: ak_live_..."

Response 202{ "status": "PENDING", "mediaId": "..." }

Response 200 (cache hit)

{ "status": "COMPLETED", "text": "TEXTO EXTRAÍDO...", "extractedAt": "...", "cached": true }

Errors

HTTPCodeQuando
400MEDIA_NOT_IMAGEcontentType não é image/* (PDFs ainda não suportados)
410MEDIA_EXPIRED

GET /v1/media/:id/ocr

Estado atual.

{
"status": "COMPLETED",
"text": "...",
"extractedAt": "2026-05-21T14:00:30.000Z",
"error": null
}

Custos de IA

OpenAI cobra por:

  • Whisper (whisper-1): ~$0.006/min de áudio.
  • OCR (gpt-4o-mini vision): ~$0.00015/imagem.

Cada chamada é registrada em ai_usage_events (acessível via /v1/admin/ai-usage). Os tokens consumidos são deduzidos da sua key OpenAI direto (gateway não cobra markup).