Pular para o conteúdo principal

Enviar arquivos por bucket S3

O canal por bucket é o envio automatizado de arquivos para a Zabit. O seu sistema grava o ZIP em uma pasta exclusiva sua, num bucket S3 da Zabit, usando uma identidade da sua conta AWS. A Zabit detecta o arquivo, verifica com antivírus, processa e grava um recibo em JSON dizendo o que aconteceu.

Disponível mediante habilitação

O canal é habilitado por cliente. Os valores entre <...> neste guia (bucket, identificador do espaço de trabalho e chave de criptografia) são fornecidos pela Zabit na habilitação.

Como funciona a confiança​

A confiança vai da Zabit para a sua identidade, e não o contrário:

  • você cria uma role na sua conta AWS e nos informa o ARN dela;
  • a Zabit autoriza essa role a gravar só na sua pasta do bucket, por política do bucket e da chave de criptografia;
  • a Zabit não assume nenhuma role na sua conta, não usa ExternalId e não entrega access key, usuário, senha, Client ID nem chave de API para o envio.

Quem pode assumir a sua role (o seu pipeline, um servidor, um agendador) é decisão sua, dentro da sua conta. As credenciais vêm do STS, são temporárias e a AWS as renova.

Prefira role a usuário IAM com access key

Um usuário IAM com access key fixa funciona, mas a chave não expira sozinha: se vazar, vale até alguém perceber e revogar. Com uma role, as credenciais são temporárias. Recomendamos a role.

1. O que você cria do seu lado​

Crie uma role IAM na sua conta com a policy abaixo. Ela tem só o necessário para enviar:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnviarArquivosParaAZabit",
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::<BUCKET>/ws/<WSID>/entrada/*"
},
{
"Sid": "CifrarComAChaveDoCanal",
"Effect": "Allow",
"Action": ["kms:GenerateDataKey", "kms:Decrypt"],
"Resource": "<KMS_KEY_ARN>"
}
]
}
PermissãoPara quê
s3:PutObject em ws/<WSID>/entrada/*Gravar o arquivo. É a única ação de S3 necessária para enviar.
kms:GenerateDataKey na chave do canalO envio é obrigatoriamente cifrado com a chave do canal (veja a seção 3). O S3 pede uma chave de dados ao KMS em nome da sua role.
kms:Decrypt na chave do canalExigida pelo S3 nos envios em várias partes (multipart), que o AWS CLI e os SDKs usam automaticamente em arquivos grandes. Se você só envia com put-object de uma parte, pode omitir.

Ler o retorno no bucket (opcional)​

Se você quer ler o recibo e as pastas de retorno direto no bucket, acrescente os dois statements abaixo e mantenha o kms:Decrypt. Se você acompanha o envio pelo webhook ou pela API de status, não precisa deles: a role fica só com a escrita.

{
"Sid": "LerORetornoDaZabit",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": [
"arn:aws:s3:::<BUCKET>/ws/<WSID>/concluido/*",
"arn:aws:s3:::<BUCKET>/ws/<WSID>/erro/*",
"arn:aws:s3:::<BUCKET>/ws/<WSID>/recibo/*"
]
},
{
"Sid": "ListarORetornoDaZabit",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::<BUCKET>",
"Condition": {
"StringLike": {
"s3:prefix": [
"ws/<WSID>/concluido/*",
"ws/<WSID>/erro/*",
"ws/<WSID>/recibo/*"
]
}
}
}

A leitura vale só para concluido/, erro/ e recibo/ da sua pasta. Não há leitura de entrada/, processando/ ou quarentena/, nem permissão para apagar nada.

2. O que você nos informa e o que recebe​

Você nos informaA Zabit informa a você
O ID da sua conta AWS (12 dígitos).<BUCKET>: o nome do bucket de recepção.
O ARN da role que fará os envios, por exemplo arn:aws:iam::<ACCOUNT_ID>:role/<NOME_DA_ROLE>.<WSID>: o identificador da sua pasta (o seu espaço de trabalho).
<KMS_KEY_ARN>: o ARN da chave de criptografia do canal.
A região: sa-east-1 (São Paulo).

A Zabit autoriza o ARN informado na política do bucket, restrito à sua pasta, e na política da chave. Se você informar só o ID da conta, sem a role, a autorização vale para a conta inteira e o recorte de quem pode enviar passa a depender só das policies IAM do seu lado. Por isso pedimos o ARN da role.

Enquanto o canal não está habilitado para o seu espaço de trabalho, o arquivo é recusado com canal.desabilitado. Depois da habilitação, basta reenviar o mesmo arquivo.

3. Como enviar​

Com as credenciais da sua role:

aws s3api put-object \
--region sa-east-1 \
--bucket <BUCKET> \
--key ws/<WSID>/entrada/diario-20261028.zip \
--body diario-20261028.zip \
--server-side-encryption aws:kms \
--ssekms-key-id <KMS_KEY_ARN> \
--checksum-algorithm SHA256
ParâmetroSituaçãoO que acontece sem ele
HTTPS (TLS)ObrigatórioO bucket recusa qualquer requisição sem TLS. O AWS CLI e os SDKs usam TLS por padrão.
--server-side-encryption aws:kmsObrigatórioAccessDenied. Envio sem criptografia ou com SSE-S3 (AES256) é recusado.
--ssekms-key-id <KMS_KEY_ARN>ObrigatórioAccessDenied. A política do bucket exige a chave do canal. A chave padrão da conta ou qualquer outra chave é recusada.
--key ws/<WSID>/entrada/<arquivo>ObrigatórioAccessDenied fora da sua pasta entrada/. Grave direto em entrada/, sem subpastas.
--checksum-algorithm SHA256RecomendadoO S3 calcula e confere o SHA-256 no recebimento e recusa o envio se o conteúdo chegou corrompido. A Zabit confere o mesmo valor de novo antes de processar (integridade.checksum_divergente). O S3 não permite exigir o checksum por política, por isso ele não é obrigatório.

Uma resposta 200 com ETag quer dizer que o S3 recebeu o arquivo. O resultado do processamento vem no recibo.

Nome do bucket e acesso virtual-hosted

O nome do bucket não tem pontos. O acesso no estilo virtual-hosted (<BUCKET>.s3.sa-east-1.amazonaws.com), que é o padrão do AWS CLI e dos SDKs, funciona com o certificado TLS da AWS. Não é preciso usar path-style.

Arquivos grandes (multipart)​

Um único put-object vai até 5 GB. Acima disso, ou para enviar arquivos grandes mais rápido, use aws s3 cp, que divide o envio em partes automaticamente:

aws s3 cp completo-20261028.zip s3://<BUCKET>/ws/<WSID>/entrada/completo-20261028.zip \
--region sa-east-1 \
--sse aws:kms --sse-kms-key-id <KMS_KEY_ARN> \
--checksum-algorithm SHA256

No envio em partes, o S3 confere o checksum de cada parte. A conferência do SHA-256 do arquivo inteiro pela Zabit vale para envios de uma parte só. Envios em partes interrompidos e nunca concluídos são descartados depois de 1 dia.

Com SDK​

Python (boto3):

import boto3

s3 = boto3.client("s3", region_name="sa-east-1")

with open("diario-20261028.zip", "rb") as arquivo:
s3.put_object(
Bucket="<BUCKET>",
Key="ws/<WSID>/entrada/diario-20261028.zip",
Body=arquivo,
ServerSideEncryption="aws:kms",
SSEKMSKeyId="<KMS_KEY_ARN>",
ChecksumAlgorithm="SHA256",
)

Para arquivos grandes, s3.upload_file(...) faz o multipart, com os mesmos parâmetros em ExtraArgs={"ServerSideEncryption": "aws:kms", "SSEKMSKeyId": "<KMS_KEY_ARN>", "ChecksumAlgorithm": "SHA256"}.

Node.js (AWS SDK v3):

import { readFile } from "node:fs/promises";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({ region: "sa-east-1" });

await s3.send(
new PutObjectCommand({
Bucket: "<BUCKET>",
Key: "ws/<WSID>/entrada/diario-20261028.zip",
Body: await readFile("diario-20261028.zip"),
ServerSideEncryption: "aws:kms",
SSEKMSKeyId: "<KMS_KEY_ARN>",
ChecksumAlgorithm: "SHA256",
}),
);

Não envie ACL: o objeto passa a pertencer à Zabit no momento do envio.

4. Nome do arquivo e parâmetros do lote​

No upload pelo portal, uma pessoa escolhe o tipo do processamento, a data e as opções. No bucket não há ninguém para escolher; por isso a Zabit decide os parâmetros do lote campo a campo, nesta ordem (o primeiro que informar o campo vale):

  1. o sidecar .manifest.json enviado junto do ZIP (opcional);
  2. o nome do arquivo, pela convenção abaixo;
  3. o padrão do seu espaço de trabalho, combinado na habilitação;
  4. se nada decidiu e a escolha seria ambígua, a Zabit não adivinha: recusa e diz o motivo no recibo.

Convenção de nome​

{tipo}-AAAAMMDD[-n].zip
  • tipo: completo ou diario, em letras minúsculas;
  • AAAAMMDD: a data de referência do movimento;
  • -n: contador opcional, de 1 a 3 dígitos, para mais de um arquivo no mesmo dia.

Exemplos: completo-20261028.zip, diario-20261028.zip, diario-20261028-2.zip.

Um nome fora dessa forma não é erro: ele só não informa nada, e o tipo vem do sidecar ou do padrão do espaço de trabalho. Já um nome na forma certa com data inexistente (diario-20261340.zip) é recusado com parametro.data_invalida.

Sidecar .manifest.json (opcional)​

Para escolher os parâmetros explicitamente, envie um arquivo com o mesmo nome do ZIP mais .manifest.json, também em entrada/ e com os mesmos parâmetros de criptografia:

ws/<WSID>/entrada/diario-20261028.zip.manifest.json

Envie o sidecar antes do ZIP. A Zabit lê o sidecar no momento em que admite o ZIP; se o ZIP chegar antes, ele pode ser admitido sem o sidecar.

{
"process_type": "diario",
"data_referencia": "2026-10-28",
"routine_code": "<CODIGO_DA_ROTINA>",
"reporting_entity_id": "<ID_DA_INSTITUICAO>",
"livro_edicao_id": "<ID_DA_EDICAO>",
"max_alerts_per_run": 5000
}
CampoValor aceitoSe ausente
process_typecompleto ou diariovem do nome; se o espaço de trabalho só libera um tipo, vale esse; senão, parametro.tipo_ausente
data_referenciadata AAAA-MM-DDvem do nome; sem nome na convenção, é obtida do conteúdo do arquivo
routine_codecódigo de uma rotina de ingestão ativaa rotina padrão do espaço de trabalho; com mais de uma ativa e nenhuma padrão, admissao.rotina_ambigua
reporting_entity_ididentificador de uma instituição ativa do espaço de trabalhoa padrão; com mais de uma ativa e nenhuma padrão, admissao.entidade_ambigua
livro_edicao_ididentificador de uma edição do Livro de Regras do espaço de trabalhoa padrão, ou a edição vigente
max_alerts_per_runinteiro maior que zeroo padrão do espaço de trabalho, ou o limite geral
  • Todos os campos são opcionais; null equivale a não informar.
  • O arquivo tem no máximo 16 KiB e é um objeto JSON em UTF-8. Textos não podem ser vazios nem passar de 100 caracteres.
  • Campo desconhecido, tipo de valor errado ou JSON ilegível recusam o lote (parametro.sidecar_invalido). Quem escreve um sidecar quer controle total, e ignorar parte dele seria adivinhar.
  • No caso comum (uma instituição e uma rotina), nenhum sidecar é necessário.

Um lote por vez​

Envie um ZIP por vez por espaço de trabalho: espere o recibo final do anterior (concluido, erro ou duplicado) antes de gravar o próximo. Um ZIP que chega enquanto outro do mesmo espaço de trabalho ainda está sendo processado pode ser recusado com processamento.falhou. Nesse caso, reenvie quando o anterior terminar.

5. Pastas de retorno e recibo​

Tudo fica sob a sua pasta ws/<WSID>/. O arquivo muda de pasta conforme o estado:

ws/<WSID>/entrada/<arquivo> ← você só escreve aqui
ws/<WSID>/processando/<id>/<arquivo> ← durante o processamento (só a Zabit lê)
ws/<WSID>/concluido/AAAA/MM/<id>/<arquivo> ← terminou com sucesso, ou era duplicado
ws/<WSID>/erro/AAAA/MM/<id>/<arquivo> ← recusado ou falhou
ws/<WSID>/quarentena/AAAA/MM/<id>/<arquivo> ← o antivírus encontrou ameaça (só a Zabit lê)
ws/<WSID>/recibo/AAAA/MM/<arquivo>.json ← o recibo, para todo arquivo

AAAA/MM é o mês (UTC) em que a Zabit recebeu o arquivo; <id> é o identificador do envio. Enquanto o antivírus examina o arquivo, ele continua em entrada/. Depois disso, todo arquivo admitido sai de entrada/.

O recibo​

Um JSON pequeno, gravado para todo arquivo, inclusive os bem-sucedidos. Ele é reescrito a cada mudança de estado; a versão final é a que tem estado igual a concluido, erro ou duplicado.

{
"versao": 1,
"arquivo": "ws/<WSID>/entrada/diario-20261028.zip",
"etag": "\"9b2cf535f27731c974343645a3985328\"",
"sha256": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
"tamanho": 184372123,
"recebido_em": "2026-10-28T19:02:11Z",
"ordem": "2026-10-28T19:02:11.482915Z",
"atualizado_em": "2026-10-28T19:05:40Z",
"estado": "concluido",
"objeto_id": "0b6f1c1e-3f6e-4a53-9f43-2c1f1d5f9a10",
"execution_id": "7c1d0b56-0f0e-4a53-9f43-2c1f1d5f9a10",
"destino": "ws/<WSID>/concluido/2026/10/0b6f1c1e-3f6e-4a53-9f43-2c1f1d5f9a10/diario-20261028.zip",
"motivo": null,
"contagens": { "registros": 1200, "alertas": 7, "casos": 2 },
"proximo_passo": null,
"antivirus": "limpo"
}

Um recibo de recusa traz o motivo e o proximo_passo (trecho):

{
"estado": "erro",
"motivo": {
"codigo": "parametro.tipo_ausente",
"detalhe": "o tipo de processamento não foi indicado: o espaço de trabalho aceita completo e diario"
},
"proximo_passo": "nomear o arquivo completo-AAAAMMDD.zip ou diario-AAAAMMDD.zip, ou indicar process_type no .manifest.json, e reenviar"
}
CampoSignificado
estadorecebido, processando, concluido, erro ou duplicado (veja Estados)
motivo.codigocódigo estável da recusa ou do duplicado. Programe sobre ele. Lista em Códigos de motivo
motivo.detalheexplicação curta para leitura humana. Pode mudar: não programe sobre ela
proximo_passoo que fazer, em uma frase
contagensno concluido: registros, alertas e casos do lote
antiviruso resultado da verificação (veja Antivírus)
arquivo / destinoonde você gravou o arquivo / onde ele está agora
objeto_ido identificador do envio
execution_ido identificador do processamento; informe-o ao suporte se precisar de ajuda
sha256, etag, tamanhoa identificação do que foi recebido
recebido_em, atualizado_em, ordemhorários em UTC; ordem tem microssegundos e serve para ordenar envios

O recibo não traz dado pessoal: CPF, CNPJ e sequências longas de dígitos são mascarados no detalhe.

Se você enviar de novo um arquivo com o mesmo nome, o recibo mais recente fica em <arquivo>.json e o anterior é preservado como <arquivo>.<8 caracteres do id>.json. Nenhum recibo é sobrescrito sem cópia.

6. Estados​

O mesmo vocabulário vale para o recibo, os webhooks e a API de status:

EstadoQuandoOnde o arquivo estáEvento (webhook)
recebidoo envio foi registrado e está na fila, ou esperando o antivírusentrada/ ou processando/—
processandoo processamento está em andamentoprocessando/—
concluidoterminou com sucessoconcluido/monitoring.batch.processado
errorecusado ou falhou; o motivo diz por quêerro/ (ou quarentena/, se o antivírus encontrou ameaça)monitoring.batch.rejeitado
duplicadoo mesmo conteúdo já foi processado ou está em processamento. Não é erro.concluido/monitoring.batch.duplicado

7. Reenvio e idempotência​

Reenviar é seguro. Se o envio ficou na dúvida (timeout, queda de rede, recibo que não apareceu), reenvie. A Zabit nunca processa duas vezes o mesmo conteúdo.

A deduplicação é pelo conteúdo, com o SHA-256 do arquivo, e não pelo nome nem pelo ETag. Com a criptografia SSE-KMS, o S3 gera um ETag diferente a cada envio, mesmo para bytes idênticos. Por isso cada envio é um envio novo, com recibo próprio, e é o SHA-256 que decide:

  • se já existe no seu espaço de trabalho um processamento do mesmo conteúdo, concluído ou em andamento, o novo envio termina em duplicado com duplicado.conteudo_ja_processado, sem processar de novo. O execution_id do recibo aponta para o processamento original. Isso vale para qualquer nome de arquivo;
  • processamentos que falharam ou foram cancelados não contam: reenviar o mesmo conteúdo depois deles processa de novo.

Quando o arquivo foi recusado, o reenvio é avaliado do começo:

  • se a causa não está no arquivo (parametro.*, admissao.entidade_ambigua, admissao.entidade_ausente, admissao.rotina_ambigua, canal.desabilitado, workspace.padrao_invalido, processamento.*, admissao.falha_persistente, objeto.*, integridade.checksum_divergente, antivirus.indisponivel): corrija o sidecar, ou aguarde o suporte, e reenvie o mesmo arquivo;
  • se o defeito está no arquivo (arquivo.*, admissao.recusada, antivirus.nao_suportado, antivirus.ameaca_detectada): corrija o arquivo e reenvie. O mesmo arquivo sem correção é recusado de novo pelo mesmo motivo; com ameaça, volta para a quarentena.

Você pode reenviar com o mesmo nome ou com outro. O proximo_passo de alguns recibos sugere nome novo para facilitar a conciliação, mas isso não é exigido: o recibo anterior é sempre preservado (O recibo).

O que fazer em cada código está em Códigos de motivo.

Não regrave um arquivo logo depois de enviá-lo. Se ele mudar antes de ser admitido, o envio é recusado com objeto.mudou.

8. Limites​

LimiteValor
Formatoum ZIP não vazio, gravado direto em entrada/
Conteúdo do ZIPsó arquivos .csv e .txt (veja Formato do arquivo)
ZIP dentro de ZIPnão aceito: o lote é recusado com arquivo.membro_nao_permitido
ZIP com senhanão aceito (o antivírus não consegue examinar: antivirus.nao_suportado)
Arquivos dentro do ZIPaté 200
Pastas dentro do ZIPaté 3 níveis; nome de cada arquivo com até 255 caracteres
Tamanho descompactadoaté 80 GiB no total e 48 GiB por arquivo
Taxa de compressãoaté 200:1 por arquivo e 150:1 no total (acima de 256 MiB descompactados)
Tamanho do ZIP enviadosem limite próprio do canal; acima de 5 GB, envio em partes (seção 3)
Sidecaraté 16 KiB
Início do processamentoaté 5 minutos depois do envio. Nesse intervalo o arquivo é detectado, ganha o recibo recebido e passa pelo antivírus. O tempo até o desfecho depende do tamanho do lote

Arquivos de sistema que o macOS acrescenta ao ZIP (__MACOSX/, ._*, .DS_Store) são ignorados, não recusados. Se um lote legítimo passa dos limites, divida-o em ZIPs menores ou fale com o suporte.

9. Antivírus​

Todo arquivo recebido pelo canal é examinado por antivírus antes de qualquer processamento, com o serviço de detecção de malware gerenciado da AWS (Amazon GuardDuty Malware Protection). O arquivo só é aberto pela Zabit depois do veredito.

  • Enquanto a verificação não termina, o arquivo fica em entrada/ e o recibo diz estado: recebido com antivirus: em_verificacao.
  • Ameaça encontrada: o arquivo vai para quarentena/, não é processado e o recibo diz estado: erro com antivirus.ameaca_detectada. A quarentena não é legível por você e é apagada automaticamente depois de 30 dias. Reenviar o mesmo arquivo leva ao mesmo resultado.
  • Arquivo que não pode ser examinado (ZIP com senha ou compressão extrema): recusado com antivirus.nao_suportado. A regra é recusar o que não foi examinado, nunca processar sem exame.
  • Verificação indisponível: a Zabit tenta de novo; se não houver veredito depois de algumas tentativas (até 6 horas), o arquivo é recusado com antivirus.indisponivel e você pode reenviar o mesmo arquivo.

O campo antivirus do recibo:

ValorSignificado
em_verificacaoo exame está em andamento
limponenhuma ameaça encontrada
ameaca_detectadaameaça encontrada; o arquivo está em quarentena
nao_suportadoo arquivo não pôde ser examinado
indisponivela verificação não respondeu a tempo
nao_escaneadoo recibo não registra veredito: o envio foi encerrado por outro motivo (por exemplo, duplicado, ZIP inválido ou parâmetro) antes do processamento

10. Retenção​

Cada pasta tem um prazo, aplicado automaticamente por regras de ciclo de vida do bucket:

PastaPor quanto tempo fica
entrada/até a admissão (até 5 minutos)
processando/durante o processamento
concluido/365 dias por padrão; o prazo é configurável por cliente na habilitação (mínimo de 60 dias)
erro/90 dias
recibo/365 dias
quarentena/30 dias
envios em partes não concluídos1 dia

Ao fim do prazo o arquivo é apagado. O bucket é versionado para proteger contra exclusão acidental, e a versão anterior de um arquivo apagado é eliminada definitivamente 14 dias depois. Esses prazos são do arquivo enviado. O tratamento dos dados processados está na Central de confiança.

11. Problemas no envio (antes do recibo)​

Se o comando de envio falha, o arquivo não chegou à Zabit e não haverá recibo:

SintomaCausa provável
AccessDenied no enviofaltou --server-side-encryption aws:kms; a chave informada não é <KMS_KEY_ARN>; a chave do objeto está fora de ws/<WSID>/entrada/; ou a sua role ainda não foi autorizada
AccessDenied mencionando kms:a policy da sua role não tem kms:GenerateDataKey (ou kms:Decrypt, no envio em partes) na chave do canal, ou o ARN da chave está errado
BadDigest ou erro de checksumo arquivo mudou ou se corrompeu durante o envio; envie de novo
o envio funcionou e o recibo não apareceaguarde até 5 minutos; passado esse prazo, fale com o suporte informando o nome do arquivo e o horário
AccessDenied ao ler ou listar o retornoa listagem precisa de --prefix dentro de concluido/, erro/ ou recibo/ da sua pasta; ou faltou kms:Decrypt