Pular para o conteúdo

Armazenamento próprio (BYO Storage)

Com o armazenamento próprio (BYO Storage, de “Bring Your Own”), o Turbo Notify guarda as mídias recebidas e as fotos de perfil de contatos diretamente no seu bucket. Sem essa configuração, as mídias recebidas não ficam disponíveis para download.

Como funciona o armazenamento próprio

Quando um contato envia uma imagem, vídeo, áudio, documento ou figurinha para o seu número de WhatsApp, o Turbo Notify:

  1. Recebe a mídia do WhatsApp.
  2. Envia o arquivo para o seu bucket (pela API S3).
  3. Devolve uma URL pré-assinada diretamente no objeto de mídia (image.url, video.url, …) quando você consulta GET /v1/numbers/{ref}/messages/{id}.
  4. Envia o webhook message.received com os campos de mídia disponíveis.

Sem armazenamento próprio configurado, o campo kind retorna "unavailable" com reason: "no_storage". O conteúdo da mídia não é guardado.

Provedores suportados

O Turbo Notify usa a API HTTP do S3, o mesmo protocolo suportado pelos três provedores:

ProvedorNotas
Cloudflare R2Sem custo de tráfego de saída; boa escolha quando as mídias são baixadas com frequência.
Supabase StorageÚtil se você já usa Supabase no seu projeto.
AWS S3Há custo de tráfego de saída.

Como configurar

O caminho é o mesmo nos três provedores: um bucket privado, uma chave de acesso e o formulário em Armazenamento (abre em nova aba) . Abaixo está o caminho em cada provedor. As telas deles mudam com o tempo: se um nome não bater, procure o equivalente.

O bucket deve ser privado, sem acesso público. O Turbo Notify acessa o conteúdo com as credenciais que você informar, não por URLs públicas. A chave de acesso precisa destas permissões. No R2 e na AWS, restrinja a chave a esse bucket; a chave do Supabase não pode ser restringida a um bucket:

s3:PutObject
s3:GetObject
s3:DeleteObject
s3:ListBucket

Cloudflare R2

  1. Entre em dash.cloudflare.com e abra R2 object storage. Na primeira vez, ative o R2: a Cloudflare pede um cartão e o aceite dos termos antes de liberar o R2.
  2. Logo depois de ativar, o endereço S3 da conta pode levar alguns minutos para responder. Se o teste de conexão falhar nesse intervalo, espere e tente de novo.
  3. Clique em Create bucket, escolha o nome, deixe a localização em Automatic e não ligue o acesso público. Não use jurisdição específica (UE ou FedRAMP): o Turbo Notify só aceita o endereço padrão da conta.
  4. Volte para R2 object storage > Overview. Em API Tokens, clique em Manage e depois em Create Account API token (o Create User API token também serve). Escolha a permissão Object Read & Write e limite o token ao bucket.
  5. A tela do token mostra três valores. Use o Access Key ID e o Secret Access Key. O Token value (o texto longo que começa com cfat_ ou cfut_) não é usado no Turbo Notify. O secret aparece uma vez só: copie antes de sair da tela.
  6. O Account ID tem 32 caracteres e fica em R2 object storage > Overview, em Account details. É o mesmo valor que aparece no endereço S3 da conta (https://<Account ID>.r2.cloudflarestorage.com).

Supabase Storage

  1. Abra o projeto em supabase.com/dashboard, entre em Storage e crie o bucket com New bucket, deixando Public bucket desligado.
  2. Em Storage, abra a página S3 (em alguns painéis ela fica em Settings), procure S3 Access keys e clique em New access key. Copie o Access key ID e o Secret access key: o secret aparece uma vez só. A chave S3 do Supabase vale para todos os buckets do projeto, não só para este.
  3. O Project Ref são as 20 letras minúsculas do endereço do projeto (https://<Project Ref>.supabase.co), também no endereço do painel (supabase.com/dashboard/project/<Project Ref>) e na Project URL do diálogo Connect.

AWS S3

  1. Entre em console.aws.amazon.com (se ainda não tiver conta, crie uma: a AWS pede um cartão), abra S3 e clique em Create bucket, escolha o nome e a região, e mantenha Block all public access ligado.
  2. Em IAM > Users, crie um usuário sem acesso ao console e anexe esta política (troque NOME-DO-BUCKET):
{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::NOME-DO-BUCKET" },
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::NOME-DO-BUCKET/*"
}
]
}
  1. No usuário, abra Security credentials e clique em Create access key. Escolha Application running outside AWS e copie o Access key ID e o Secret access key. O secret aparece uma vez só.

Preencha no dashboard

Acesse Armazenamento (abre em nova aba) e preencha:

CampoValor
ProvedorAWS S3, Cloudflare R2 ou Supabase Storage
BucketNome do bucket criado
RegiãoRegião do bucket (por exemplo us-east-1). No R2 é sempre auto e no Supabase, us-east-1
Access Key IDA chave de acesso criada no provedor (no formulário aparece como R2 Access Key ID ou Supabase S3 Access Key ID; no R2, é o Access Key ID, nunca o Token value)
Secret Access KeyA chave secreta correspondente
Cloudflare Account ID / Supabase Project RefSó para R2 e Supabase, respectivamente: o endereço S3 é montado a partir desse valor

Teste a conexão e salve

Antes de salvar, clique em Testar conexão. O Turbo Notify verifica o bucket (HEAD), grava um objeto temporário (PUT), lê esse objeto de volta (HEAD) e o remove (DELETE), validando as 4 permissões necessárias (ListBucket/PutObject/GetObject/DeleteObject). Cada verificação aparece numa lista com o seu resultado, e o botão de salvar só é liberado quando as quatro passam. Depois de salvo, as mídias passam a ser armazenadas imediatamente.

Se alguma verificação falhar:

  • Chave recusada: no R2, confira se você usou o Access Key ID e não o Token value.
  • Bucket não encontrado: o nome precisa ser exato e o bucket tem de estar na conta da chave.
  • R2 recém-ativado: espere alguns minutos e teste de novo.
  • Falha em uma só permissão: revise a política ou a permissão do token, que deve incluir leitura, escrita, exclusão e listagem.

Comportamento após a configuração

Mídias recebidas

Toda mídia recebida após a ativação é copiada automaticamente para o seu bucket. Os campos de mídia em GET /v1/numbers/{ref}/messages/{id} passam a retornar kind: "available", por exemplo:

{
"kind": "available",
"url": "https://<bucket>.r2.cloudflarestorage.com/...?X-Amz-...",
"expires_at": "2026-04-21T11:00:00Z",
"mime_type": "image/jpeg",
"size_bytes": 184320
}

O acesso à mídia expira no expires_at. Para obter uma nova URL, consulte a mensagem novamente dentro do prazo de retenção de conteúdo do seu plano. Depois desse prazo, o Turbo Notify apaga a referência ao arquivo, e o arquivo continua no seu bucket.

Quando as fotos aparecem

As fotos de perfil só são guardadas com o armazenamento ativo. Elas não são baixadas todas de uma vez, para não sobrecarregar o seu número no WhatsApp:

Foto deQuando é buscada
Seus númerosNa hora, quando o número conecta ou quando você configura o armazenamento. Se a primeira tentativa não der certo, ela se repete em até cerca de 6 horas. Aparece no dashboard.
ContatosNa próxima mensagem trocada com o contato (recebida ou enviada, inclusive pela API). Se já houve consulta nas últimas 6 horas, só depois disso.
GruposNa próxima mensagem de um membro do grupo ou enviada pelo próprio celular. Mensagens enviadas só pela API não disparam a busca. Vale a mesma janela de 6 horas.
Foto trocadaUma foto salva é conferida de novo a cada 7 dias: a dos números sozinha, a de contatos e grupos na primeira mensagem depois disso.
Sem fotoQuem não tem foto, ou a esconde nas configurações de privacidade do WhatsApp, não aparece.

Quando a foto chega ao seu bucket, o webhook contact.picture_changed (contatos) ou group.picture_changed (grupos) é enviado, e GET /v1/numbers/{ref}/contacts/{id}/profile-picture responde com um redirecionamento 302 para a URL pré-assinada. Até lá, a resposta é unavailable.

Mídias anteriores à configuração

Mídias recebidas antes da ativação do armazenamento próprio continuam com kind: "unavailable". Elas não são recuperadas depois: apenas as novas mídias são armazenadas.

Solução de problemas

kind: "unavailable" com reason: "upload_failed"

O envio para o bucket falhou depois de novas tentativas. Possíveis causas:

  • Credenciais expiradas ou com permissões insuficientes.
  • Bucket apagado ou com política de acesso alterada.
  • Cota do bucket atingida (verifique no console do provedor).

Acesse Armazenamento (abre em nova aba) e clique em Testar conexão para diagnosticar.

kind: "unavailable" com reason: "storage_circuit_open"

O seu armazenamento falhou várias vezes seguidas e o envio de mídia para ele foi suspenso temporariamente. O Turbo Notify volta a tentar automaticamente. Quando a conexão for restabelecida, você recebe o webhook storage.recovered.

kind: "pending"

A mídia ainda está sendo enviada para o bucket. Aguarde alguns segundos e consulte GET /v1/numbers/{ref}/messages/{id} novamente. Esse estado só aparece enquanto o envio está em andamento: o webhook message.received só é emitido depois que a mídia fica disponível ou indisponível.

Segurança

  • As credenciais são guardadas criptografadas (AES-256-GCM), nunca em texto claro.
  • O Turbo Notify nunca expõe credenciais em logs ou respostas.
  • URLs pré-assinadas têm validade configurável (padrão: 15 minutos; até 1 hora).
  • Recomendamos o privilégio mínimo: no R2 e na AWS, credenciais com acesso restrito ao bucket do Turbo Notify. A chave S3 do Supabase vale para o projeto inteiro: se isso for um problema, use um projeto só para o Turbo Notify.

Relacionado