Skip to main content

Visão Geral

Quando você cria um relatório, a API retorna uma URL assinada do S3 (uploadUrl) que permite fazer upload de arquivos diretamente para o armazenamento da Base39.

Exemplo Simples

# 1. Criar relatório e obter uploadUrl
curl -X POST https://api.base39.com.br/v1/reports \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "target": {
      "document": "12345678901"
    },
    "template": "tpl_abc123"
  }'

# Resposta:
# {
#   "id": "rep_xyz789",
#   "uploadUrl": "https://s3.amazonaws.com/bucket/path?signature=...",
#   ...
# }

# 2. Fazer upload do arquivo usando a uploadUrl
curl -X PUT "https://s3.amazonaws.com/bucket/path?signature=..." \
  -H "Content-Type: application/pdf" \
  --data-binary @/caminho/do/arquivo.pdf

Upload com JavaScript/TypeScript

Exemplo Básico com Fetch

// 1. Criar relatório
const response = await fetch('https://api.base39.com.br/v1/reports', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_TOKEN'
  },
  body: JSON.stringify({
    target: { document: '12345678901' },
    template: 'tpl_abc123'
  })
});

const { uploadUrl } = await response.json();

// 2. Fazer upload do arquivo
const file = document.querySelector('input[type="file"]').files[0];

await fetch(uploadUrl, {
  method: 'PUT',
  headers: {
    'Content-Type': file.type
  },
  body: file
});

Exemplo com FormData e Múltiplos Arquivos

async function uploadMultipleFiles(files) {
  for (const file of files) {
    // Criar um relatório para cada arquivo
    const reportResponse = await fetch('https://api.base39.com.br/v1/reports', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer YOUR_TOKEN'
      },
      body: JSON.stringify({
        target: { document: '12345678901' },
        template: 'tpl_abc123'
      })
    });

    const { uploadUrl } = await reportResponse.json();

    // Upload do arquivo
    await fetch(uploadUrl, {
      method: 'PUT',
      headers: {
        'Content-Type': file.type
      },
      body: file
    });
  }
}

// Uso:
const fileInput = document.querySelector('input[type="file"]');
await uploadMultipleFiles(fileInput.files);

Upload com Python

import requests

# 1. Criar relatório
response = requests.post(
    'https://api.base39.com.br/v1/reports',
    headers={
        'Authorization': 'Bearer YOUR_TOKEN',
        'Content-Type': 'application/json'
    },
    json={
        'target': {'document': '12345678901'},
        'template': 'tpl_abc123'
    }
)

upload_url = response.json()['uploadUrl']

# 2. Fazer upload do arquivo
with open('/caminho/do/arquivo.pdf', 'rb') as file:
    requests.put(
        upload_url,
        headers={'Content-Type': 'application/pdf'},
        data=file
    )

Opções Avançadas

Monitoramento de Progresso

async function uploadWithProgress(file, uploadUrl, onProgress) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();

    xhr.upload.addEventListener('progress', (e) => {
      if (e.lengthComputable) {
        const percentComplete = (e.loaded / e.total) * 100;
        onProgress(percentComplete);
      }
    });

    xhr.addEventListener('load', () => {
      if (xhr.status === 200) {
        resolve();
      } else {
        reject(new Error(`Upload failed with status ${xhr.status}`));
      }
    });

    xhr.addEventListener('error', () => reject(new Error('Upload failed')));

    xhr.open('PUT', uploadUrl);
    xhr.setRequestHeader('Content-Type', file.type);
    xhr.send(file);
  });
}

// Uso:
await uploadWithProgress(file, uploadUrl, (percent) => {
  console.log(`Upload progress: ${percent.toFixed(2)}%`);
});

Upload com Retry Automático

async function uploadWithRetry(file, uploadUrl, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(uploadUrl, {
        method: 'PUT',
        headers: {
          'Content-Type': file.type
        },
        body: file
      });

      if (response.ok) {
        return true;
      }

      throw new Error(`Upload failed with status ${response.status}`);
    } catch (error) {
      if (attempt === maxRetries) {
        throw error;
      }

      // Aguardar antes de tentar novamente (exponential backoff)
      await new Promise(resolve =>
        setTimeout(resolve, Math.pow(2, attempt) * 1000)
      );
    }
  }
}

Validação de Arquivo Antes do Upload

function validateFile(file, options = {}) {
  const {
    maxSize = 10 * 1024 * 1024, // 10MB padrão
    allowedTypes = ['application/pdf', 'image/jpeg', 'image/png']
  } = options;

  // Validar tamanho
  if (file.size > maxSize) {
    throw new Error(`Arquivo muito grande. Máximo: ${maxSize / 1024 / 1024}MB`);
  }

  // Validar tipo
  if (!allowedTypes.includes(file.type)) {
    throw new Error(`Tipo de arquivo não permitido. Permitidos: ${allowedTypes.join(', ')}`);
  }

  return true;
}

// Uso:
try {
  validateFile(file, {
    maxSize: 5 * 1024 * 1024, // 5MB
    allowedTypes: ['application/pdf']
  });

  await uploadFile(file, uploadUrl);
} catch (error) {
  console.error('Erro na validação:', error.message);
}

Limitações e Boas Práticas

Tamanho dos Arquivos

  • Tamanho máximo recomendado: 10MB por arquivo
  • Para arquivos maiores, considere dividir ou comprimir

Expiração da URL

  • URLs assinadas expiram após 15 minutos
  • Se o upload falhar, crie um novo relatório para obter uma nova URL

Content-Type

  • Sempre defina o Content-Type correto no header do upload
  • Tipos comuns:
    • application/pdf - Documentos PDF
    • image/jpeg - Imagens JPEG
    • image/png - Imagens PNG
    • text/csv - Arquivos CSV
    • application/json - Arquivos JSON

Segurança

  • Nunca compartilhe URLs assinadas publicamente
  • URLs são de uso único e temporárias
  • Validate sempre os arquivos no lado do cliente antes do upload

Verificação do Upload

Após o upload, você pode verificar se o arquivo foi processado consultando o relatório:
const reportResponse = await fetch(
  `https://api.base39.com.br/v1/reports/${reportId}`,
  {
    headers: {
      'Authorization': 'Bearer YOUR_TOKEN'
    }
  }
);

const report = await reportResponse.json();
console.log('Status do relatório:', report.status);
console.log('Seções:', report.sections);
I