Processar Arquivo de Retorno
Processar Arquivo de Retorno
Seção intitulada “Processar Arquivo de Retorno”Processa um arquivo CNAB de retorno enviado via upload e retorna um JSON com as ocorrências dos boletos (pagamentos, confirmações de registro, baixas, etc.).
O Que Faz
Seção intitulada “O Que Faz”Este endpoint:
- Recebe o arquivo de retorno CNAB via upload (multipart/form-data)
- Processa o conteúdo extraindo as ocorrências dos títulos
- Cria o registro do arquivo no sistema (evita reprocessamento)
- Retorna JSON com todas as ocorrências encontradas
Quando Usar
Seção intitulada “Quando Usar”Cenários Recomendados
Seção intitulada “Cenários Recomendados”| Cenário | Exemplo |
|---|---|
| Processamento manual | Conta bancária com troca de arquivos via Internet Banking |
| Plano gratuito | Não possui integração automática via VAN ou API |
| Arquivo avulso | Necessidade de processar um arquivo específico |
| Conciliação | Verificar pagamentos de um período específico |
Pré-requisitos
Seção intitulada “Pré-requisitos”- Possuir uma conta bancária cadastrada com os mesmos dados do arquivo (convênio, agência, conta)
- Arquivo de retorno no formato CNAB 240 ou CNAB 400
Posição no Ciclo de Vida
Seção intitulada “Posição no Ciclo de Vida” ┌──────────────────┐ │ Banco processa │ Pagamentos, registros, │ os boletos │ baixas, protestos └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ Banco gera │ Arquivo CNAB de retorno │ arquivo retorno │ disponível no Internet Banking └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ Você baixa o │ Download do Internet Banking │ arquivo │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ ★ PROCESSAR │ ◄── VOCÊ ESTÁ AQUI │ RETORNO POST │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ Atualizar seu │ Pagamentos, baixas, │ sistema │ status dos boletos └──────────────────┘Fluxo de Requisição
Seção intitulada “Fluxo de Requisição”Endpoint
Seção intitulada “Endpoint”POST https://sandbox.boletocloud.com/api/v1/arquivos/cnab/retornosProdução:
POST https://app.boletocloud.com/api/v1/arquivos/cnab/retornosHeaders da Requisição
Seção intitulada “Headers da Requisição”| Header | Valor | Obrigatório | Descrição |
|---|---|---|---|
Content-Type | multipart/form-data | Sim | Tipo do conteúdo (upload de arquivo) |
Accept | application/json | Sim | Formato da resposta desejada |
Authorization | Basic {credenciais} | Sim | Autenticação HTTP Basic com API Key |
Corpo da Requisição (Multipart)
Seção intitulada “Corpo da Requisição (Multipart)”| Campo | Tipo | Obrigatório | Tamanho Máximo | Descrição |
|---|---|---|---|---|
arquivo | file | Sim | 5 MB | Arquivo de retorno CNAB (.ret, .txt) |
Validações
Seção intitulada “Validações”| Validação | Regra | Código | Mensagem |
|---|---|---|---|
| Arquivo obrigatório | Campo arquivo deve conter um arquivo | 400 | Arquivo de retorno é obrigatório |
| Tamanho máximo | Arquivo não pode exceder 5 MB | 400 | Arquivo excede o tamanho máximo permitido |
| Formato válido | Arquivo deve ser CNAB válido | 400 | Formato de arquivo inválido |
| Conta existente | Dados bancários devem corresponder a uma conta cadastrada | 500 | Conta bancária não encontrada |
| Arquivo duplicado | Arquivo já foi processado anteriormente | 409 | Arquivo já processado |
| Autenticação | API Key válida | 401 | Não autorizado |
Respostas
Seção intitulada “Respostas”Sucesso: 201 Created
Seção intitulada “Sucesso: 201 Created”Indica que o arquivo foi processado com sucesso.
Headers de Resposta
Seção intitulada “Headers de Resposta”| Header | Exemplo | Descrição |
|---|---|---|
X-BoletoCloud-Token | m1pKbdCyI9T9qJPC4n... | Token identificador do arquivo processado |
Location | /api/v1/arquivos/cnab/retornos/m1pK... | URL para consulta posterior |
Content-Type | application/json; charset=utf-8 | Tipo do conteúdo retornado |
X-BoletoCloud-Version | 1.x.x | Versão da plataforma |
Corpo da Resposta
Seção intitulada “Corpo da Resposta”{ "arquivo": { "meta": { "token": "Rk5pQdCyI9T9qJPC4nUSE8-qLu0UbwWRQv6xcQqMa98=", "criado": "2024-01-15T10:30:45.123Z" }, "protocolo": { "banco": { "codigo": "237", "nome": "BRADESCO" }, "numero": 54321, "gravacao": "2024-01-14" }, "titulos": [ { "token": "ulBx9quRyPkogs6rkvjO7SjqJb8ZnFmdyM0B5rasoDA=", "numero": "00000000001-0", "documento": "FAT-2024-001", "valor": 250.00, "vencimento": "2024-01-20", "ocorrencias": [ { "situacao": "REGISTRO_CONFIRMADO", "codigo": 2, "data": "2024-01-14", "descricao": "Registro Confirmado", "motivos": [], "info": null } ] }, { "token": "2mnFyTda_eamDnzS_lU24q7ZFHmBjGuKilqUSc45mlo=", "numero": "00000000002-0", "documento": "FAT-2024-002", "valor": 180.50, "vencimento": "2024-01-15", "ocorrencias": [ { "situacao": "LIQUIDACAO", "codigo": 6, "data": "2024-01-14", "descricao": "Liquidação Normal", "motivos": [], "info": { "valorPago": 182.00, "jurosMora": 1.50, "dataDePagamento": "2024-01-14", "dataDeCredito": "2024-01-16", "situacao": "LIQUIDACAO" } } ] } ] }}Conflito: 409 Conflict
Seção intitulada “Conflito: 409 Conflict”Indica que o arquivo já foi processado anteriormente.
{ "erro": { "status": 409, "mensagem": "Arquivo já foi processado anteriormente" }}| Código | Status | Causa | Solução |
|---|---|---|---|
400 | Bad Request | Arquivo ausente ou formato inválido | Verifique o arquivo enviado |
401 | Unauthorized | API Key inválida ou ausente | Verifique as credenciais |
409 | Conflict | Arquivo já processado | Use GET para consultar o resultado |
500 | Internal Server Error | Conta bancária não encontrada | Verifique se a conta está cadastrada |
Estrutura do JSON de Resposta
Seção intitulada “Estrutura do JSON de Resposta”Objeto arquivo.meta
Seção intitulada “Objeto arquivo.meta”| Campo | Tipo | Descrição |
|---|---|---|
token | string | Token identificador do arquivo processado |
criado | datetime | Data/hora do processamento (ISO 8601) |
Objeto arquivo.protocolo
Seção intitulada “Objeto arquivo.protocolo”| Campo | Tipo | Descrição |
|---|---|---|
banco.codigo | string | Código do banco (3 dígitos) |
banco.nome | string | Nome do banco |
numero | integer | Número sequencial do arquivo |
gravacao | date | Data de gravação do arquivo pelo banco |
Objeto arquivo.titulos[]
Seção intitulada “Objeto arquivo.titulos[]”| Campo | Tipo | Descrição |
|---|---|---|
token | string|null | Token do boleto na plataforma Boleto Cloud. Será null se o título não corresponder a um boleto cadastrado (apenas plano personalizado) |
numero | string | Nosso Número do boleto |
documento | string | Número do documento/fatura |
valor | decimal | Valor nominal do boleto |
vencimento | date | Data de vencimento original |
ocorrencias | array | Lista de ocorrências do arquivo |
Objeto arquivo.titulos[].ocorrencias[]
Seção intitulada “Objeto arquivo.titulos[].ocorrencias[]”| Campo | Tipo | Descrição |
|---|---|---|
situacao | string | Tipo da ocorrência (ver tabela abaixo) |
codigo | integer | Código numérico da ocorrência |
data | date | Data da ocorrência |
descricao | string | Descrição textual da ocorrência |
motivos | array | Lista de motivos (para rejeições) |
info | object | Informações adicionais (para liquidações) |
Objeto info (Liquidação)
Seção intitulada “Objeto info (Liquidação)”| Campo | Tipo | Descrição |
|---|---|---|
valorPago | decimal | Valor efetivamente pago |
jurosMora | decimal | Valor de juros/mora cobrado |
dataDePagamento | date | Data em que o pagamento foi efetuado |
dataDeCredito | date | Data do crédito na conta |
situacao | string | Sempre LIQUIDACAO |
Objeto motivos[] (Rejeição)
Seção intitulada “Objeto motivos[] (Rejeição)”| Campo | Tipo | Descrição |
|---|---|---|
codigo | string | Código do motivo de rejeição |
descricao | string | Descrição do motivo |
Tipos de Ocorrência
Seção intitulada “Tipos de Ocorrência”| Situação | Descrição |
|---|---|
LIQUIDACAO | Boleto foi pago/liquidado |
REGISTRO_CONFIRMADO | Registro confirmado pelo banco |
REGISTRO_REJEITADO | Registro rejeitado (verificar motivos) |
BAIXA | Boleto foi baixado |
PROTESTO | Boleto foi protestado |
CANCELAMENTO_PROTESTO | Protesto foi cancelado |
ABATIMENTO | Abatimento concedido |
CANCELAMENTO_ABATIMENTO | Abatimento cancelado |
ALTERACAO_VENCIMENTO | Vencimento foi alterado |
Exemplos de Código
Seção intitulada “Exemplos de Código”curl -X POST "https://sandbox.boletocloud.com/api/v1/arquivos/cnab/retornos" \ -H "Accept: application/json" \ -u "api-key_SUA-API-KEY:token" \ -F "arquivo=@/caminho/para/seu-arquivo-retorno.ret"Salvando a resposta em arquivo:
curl -X POST "https://sandbox.boletocloud.com/api/v1/arquivos/cnab/retornos" \ -H "Accept: application/json" \ -u "api-key_SUA-API-KEY:token" \ -F "arquivo=@/caminho/para/seu-arquivo-retorno.ret" \ -o retorno-processado.jsonimport javax.ws.rs.client.ClientBuilder;import javax.ws.rs.client.Entity;import javax.ws.rs.core.Response;import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;import org.glassfish.jersey.media.multipart.*;import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;import java.io.File;import static javax.ws.rs.core.MediaType.APPLICATION_JSON;import static javax.ws.rs.core.Response.Status.CREATED;
public class ProcessarRetorno { public static void main(String[] args) throws Exception { // Arquivo de retorno a ser processado File arquivoRetorno = new File("/caminho/para/arquivo-retorno.ret");
final FileDataBodyPart filePart = new FileDataBodyPart("arquivo", arquivoRetorno); final FormDataMultiPart multipart = new FormDataMultiPart().bodyPart(filePart);
Response response = ClientBuilder.newClient() .target("https://sandbox.boletocloud.com/api/v1/arquivos/cnab/retornos") .register(HttpAuthenticationFeature.basic( "api-key_SUA-API-KEY", "token" )) .register(MultiPartFeature.class) .request(APPLICATION_JSON) .post(Entity.entity(multipart, multipart.getMediaType()));
System.out.println("HTTP Status: " + response.getStatus()); System.out.println("Token: " + response.getHeaderString("X-BoletoCloud-Token"));
if (response.getStatus() == CREATED.getStatusCode()) { String json = response.readEntity(String.class); System.out.println("Retorno processado: " + json); } else if (response.getStatus() == 409) { System.out.println("Arquivo já processado anteriormente."); } else { System.err.println("Erro: " + response.readEntity(String.class)); } }}import okhttp3.*import okhttp3.MediaType.Companion.toMediaTypeimport okhttp3.RequestBody.Companion.asRequestBodyimport java.io.File
fun main() { val client = OkHttpClient() val credential = Credentials.basic("api-key_SUA-API-KEY", "token") val arquivoRetorno = File("/caminho/para/arquivo-retorno.ret")
val body = MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart( "arquivo", arquivoRetorno.name, arquivoRetorno.asRequestBody("application/octet-stream".toMediaType()) ) .build()
val request = Request.Builder() .url("https://sandbox.boletocloud.com/api/v1/arquivos/cnab/retornos") .header("Authorization", credential) .header("Accept", "application/json") .post(body) .build()
client.newCall(request).execute().use { response -> println("Token: " + response.header("X-BoletoCloud-Token"))
when (response.code) { 201 -> println("Retorno processado: " + response.body?.string()) 409 -> println("Arquivo já processado. Use GET para consultar.") else -> println("Erro: " + response.code) } }}using System;using System.IO;using System.Net.Http;using System.Net.Http.Headers;using System.Text;using System.Threading.Tasks;
class Program{ static async Task Main(string[] args) { var arquivoPath = "/caminho/para/arquivo-retorno.ret";
using var client = new HttpClient(); var credentials = Convert.ToBase64String( Encoding.ASCII.GetBytes("api-key_SUA-API-KEY:token")); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials); client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json"));
using var content = new MultipartFormDataContent(); var fileBytes = await File.ReadAllBytesAsync(arquivoPath); var fileContent = new ByteArrayContent(fileBytes); content.Add(fileContent, "arquivo", Path.GetFileName(arquivoPath));
var response = await client.PostAsync( "https://sandbox.boletocloud.com/api/v1/arquivos/cnab/retornos", content);
var body = await response.Content.ReadAsStringAsync();
if ((int)response.StatusCode == 201) Console.WriteLine("Retorno processado: " + body); else if ((int)response.StatusCode == 409) Console.WriteLine("Arquivo já processado."); else Console.WriteLine("Erro: " + (int)response.StatusCode); }}const https = require('https');const fs = require('fs');const path = require('path');
const arquivoPath = '/caminho/para/arquivo-retorno.ret';const boundary = '----FormBoundary' + Date.now();const fileContent = fs.readFileSync(arquivoPath);const fileName = path.basename(arquivoPath);
const body = Buffer.concat([ Buffer.from( '--' + boundary + '\r\n' + 'Content-Disposition: form-data; name="arquivo"; filename="' + fileName + '"\r\n\r\n' ), fileContent, Buffer.from('\r\n--' + boundary + '--\r\n')]);
const options = { hostname: 'sandbox.boletocloud.com', path: '/api/v1/arquivos/cnab/retornos', method: 'POST', auth: 'api-key_SUA-API-KEY:token', headers: { 'Content-Type': 'multipart/form-data; boundary=' + boundary, 'Accept': 'application/json', 'Content-Length': body.length }};
const req = https.request(options, (res) => { console.log('Token:', res.headers['x-boletocloud-token']);
let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { if (res.statusCode === 201) console.log('Processado:', JSON.parse(data)); else if (res.statusCode === 409) console.log('Arquivo já processado.'); else console.log('Erro:', res.statusCode); });});
req.write(body);req.end();package main
import ( "bytes" "fmt" "io" "mime/multipart" "net/http" "os" "path/filepath")
func main() { arquivoPath := "/caminho/para/arquivo-retorno.ret"
file, _ := os.Open(arquivoPath) defer file.Close()
var body bytes.Buffer writer := multipart.NewWriter(&body) part, _ := writer.CreateFormFile("arquivo", filepath.Base(arquivoPath)) io.Copy(part, file) writer.Close()
req, _ := http.NewRequest("POST", "https://sandbox.boletocloud.com/api/v1/arquivos/cnab/retornos", &body) req.SetBasicAuth("api-key_SUA-API-KEY", "token") req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Accept", "application/json")
resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close()
fmt.Println("Token:", resp.Header.Get("X-BoletoCloud-Token"))
respBody, _ := io.ReadAll(resp.Body) if resp.StatusCode == 201 { fmt.Println("Processado:", string(respBody)) } else { fmt.Println("Erro:", resp.StatusCode) }}<?php$url = 'https://sandbox.boletocloud.com/api/v1/arquivos/cnab/retornos';$api_key = 'api-key_SUA-API-KEY';$arquivoPath = '/caminho/para/arquivo-retorno.ret';
$data = array('arquivo' => new CURLFile($arquivoPath));
$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_POST, true);curl_setopt($ch, CURLOPT_POSTFIELDS, $data);curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json'));curl_setopt($ch, CURLOPT_USERPWD, "$api_key:token");curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);curl_setopt($ch, CURLOPT_HEADER, true);
$response = curl_exec($ch);$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);$body = substr($response, $header_size);curl_close($ch);
if ($http_code == 201) { $retorno = json_decode($body, true); echo "Processado! Títulos: " . count($retorno['arquivo']['titulos']) . "\n"; foreach ($retorno['arquivo']['titulos'] as $titulo) { echo "Boleto: {$titulo['numero']}\n"; foreach ($titulo['ocorrencias'] as $oc) { echo " - {$oc['situacao']}: {$oc['descricao']}\n"; } }} elseif ($http_code == 409) { echo "Arquivo já processado.\n";} else { echo "Erro ($http_code): $body\n";}?>import requestsfrom requests.auth import HTTPBasicAuth
arquivo_path = '/caminho/para/arquivo-retorno.ret'
with open(arquivo_path, 'rb') as f: response = requests.post( 'https://sandbox.boletocloud.com/api/v1/arquivos/cnab/retornos', auth=HTTPBasicAuth('api-key_SUA-API-KEY', 'token'), headers={'Accept': 'application/json'}, files={'arquivo': f} )
print('Token:', response.headers.get('X-BoletoCloud-Token'))
if response.status_code == 201: retorno = response.json() print('Processado! Títulos:', len(retorno['arquivo']['titulos'])) for titulo in retorno['arquivo']['titulos']: print(f"Boleto: {titulo['numero']}") for oc in titulo['ocorrencias']: print(f" - {oc['situacao']}: {oc['descricao']}")elif response.status_code == 409: print('Arquivo já processado.')else: print('Erro:', response.status_code)require 'net/http'require 'uri'require 'json'
arquivo_path = '/caminho/para/arquivo-retorno.ret'uri = URI('https://sandbox.boletocloud.com/api/v1/arquivos/cnab/retornos')
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http| request = Net::HTTP::Post.new(uri) request.basic_auth('api-key_SUA-API-KEY', 'token') request['Accept'] = 'application/json' request.set_form([['arquivo', File.open(arquivo_path)]], 'multipart/form-data')
response = http.request(request) puts "Token: #{response['X-BoletoCloud-Token']}"
case response.code when '201' retorno = JSON.parse(response.body) puts "Processado! Títulos: #{retorno['arquivo']['titulos'].length}" when '409' puts 'Arquivo já processado.' else puts "Erro: #{response.code}" endend(require '[clj-http.client :as client])
(let [response (client/post "https://sandbox.boletocloud.com/api/v1/arquivos/cnab/retornos" {:basic-auth ["api-key_SUA-API-KEY" "token"] :headers {"Accept" "application/json"} :multipart [{:name "arquivo" :content (clojure.java.io/file "/caminho/arquivo.ret")}] :as :json :throw-exceptions false})] (case (:status response) 201 (println "Processado:" (get-in response [:body :arquivo :titulos])) 409 (println "Arquivo já processado.") (println "Erro:" (:status response))))Veja Também
Seção intitulada “Veja Também” Visão Geral Retorno Conceitos sobre arquivos de retorno
Obter Retorno Consultar retorno já processado
Listar Retornos Listar retornos disponíveis
Arquivos de Remessa Gerar e enviar remessas ao banco