# API de Saida

A API de Saida gerencia uploads de arquivos para o BCB STA (Sistema de Transferencia de Arquivos).

## URL Base

```
http://localhost:8080/api/v1
```

## Autenticacao

Todos os endpoints da API de Saida requerem autenticacao por chave API:

```bash
curl -H "Authorization: Bearer ${API_KEY}" http://localhost:8080/api/v1/health
```

Ou usando header:

```bash
curl -H "X-API-Key: sua-chave-api" http://localhost:8080/api/v1/health
```

## Endpoints

### Health Check

Verificar saude da API e do servico:

```http
GET /api/v1/health
```

Resposta (200 OK):

```json
{
  "status": "healthy",
  "sta_connection": "ok",
  "database": "ok",
  "uptime_seconds": 86400,
  "version": "1.0.0",
  "details": {
    "database_latency_ms": 5,
    "sta_latency_ms": 120,
    "last_sta_check": "2026-01-31T10:30:00Z",
    "pending_files": 3
  }
}
```

| Campo | Descricao |
|-------|-----------|
| `status` | Status geral: `healthy` ou `unhealthy` |
| `sta_connection` | Conectividade do WebService STA: `ok` ou `error` |
| `database` | Conectividade do banco de dados: `ok` ou `error` |
| `uptime_seconds` | Uptime do servico em segundos |
| `version` | Versao do connector |

---

### Enviar Arquivo

Enviar um arquivo para upload ao BCB STA:

```http
POST /api/v1/files
Content-Type: application/json
Authorization: Bearer sua-chave-api

{
  "system": "CCS",
  "file_type": "ARQC001",
  "data": {
    "content": "SGVsbG8gV29ybGQgQkNCIEZpbGU=",
    "file_name": "CCS_20260131_001.txt"
  },
  "destination": {
    "ispb": "00000000"
  }
}
```

Corpo da Requisicao:

| Campo | Tipo | Obrigatorio | Descricao |
|-------|------|-------------|-----------|
| `system` | string | Sim | Codigo do sistema BCB (CCS, SPI, DICT, etc.) |
| `file_type` | string | Sim | Codigo do tipo de arquivo BCB |
| `data.content` | string | Sim | Conteudo do arquivo codificado em Base64 |
| `data.file_name` | string | Nao | Nome do arquivo opcional (gerado automaticamente se omitido) |
| `destination.ispb` | string | Sim* | ISPB de destino unico |
| `destination.ispbs` | array | Sim* | Multiplos ISPBs de destino |

*`ispb` ou `ispbs` e obrigatorio.

Resposta (201 Created):

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "protocol": "2026013100001234",
  "status": "uploaded",
  "created_at": "2026-01-31T10:30:00Z",
  "file_name": "CCS_20260131_001.txt",
  "file_size": 12345,
  "checksum": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
}
```

Campos da Resposta:

| Campo | Descricao |
|-------|-----------|
| `id` | ID interno do arquivo (UUID) |
| `protocol` | Numero de protocolo BCB |
| `status` | Status inicial (tipicamente `uploaded`) |
| `created_at` | Timestamp de submissao (UTC) |
| `file_name` | Nome do arquivo atribuido |
| `file_size` | Tamanho do arquivo em bytes |
| `checksum` | Checksum SHA-256 |

---

### Obter Status do Arquivo

Verificar o status de um arquivo enviado:

```http
GET /api/v1/files/{id}/status
Authorization: Bearer sua-chave-api
```

Resposta (200 OK):

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "protocol": "2026013100001234",
  "file_name": "CCS_20260131_001.txt",
  "file_type": "ARQC001",
  "file_size": 12345,
  "status": "completed",
  "source_system": "CCS",
  "sta_state": {
    "sta_status": "ENTREGUE",
    "sta_message": "Arquivo entregue com sucesso",
    "sta_timestamp": "2026-01-31T10:30:15Z"
  },
  "created_at": "2026-01-31T10:30:00Z",
  "updated_at": "2026-01-31T10:30:15Z",
  "completed_at": "2026-01-31T10:30:15Z"
}
```

Valores de Status:

| Status | Descricao |
|--------|-----------|
| `pending` | Enfileirado para processamento |
| `uploading` | Upload em progresso |
| `uploaded` | Enviado ao BCB, aguardando processamento |
| `processing` | BCB esta processando |
| `completed` | Processado com sucesso |
| `failed` | Processamento falhou |
| `retrying` | Agendado para retry |

Campos do Estado STA:

| Campo | Descricao |
|-------|-----------|
| `sta_status` | Codigo de status BCB |
| `sta_message` | Mensagem de status BCB |
| `sta_timestamp` | Ultima atualizacao BCB |

---

## Validacao

### IDs de Sistema

Codigos de sistema BCB validos:

| Sistema | Nome |
|---------|------|
| `CCS` | Sistema de Consulta ao Cliente |
| `CIR` | Central de Informacoes de Credito |
| `CMP` | Controle de Meios de Pagamento |
| `STR` | Sistema de Transferencia de Reservas |
| `SPI` | Sistema de Pagamentos Instantaneos (PIX) |
| `CAM` | Sistema de Cambio |
| `LDL` | Liquidacao Diferida |
| `DICT` | Diretorio de Identificadores de Contas Transacionais |

### Validacao de Conteudo

| Verificacao | Requisito |
|-------------|-----------|
| Tamanho maximo do arquivo | 50 MB |
| Codificacao do conteudo | Base64 valido |
| Comprimento do conteudo | Nao vazio |
| ID do Sistema | Deve ser sistema BCB valido |
| ISPB | Deve ter 8 digitos |

---

## Respostas de Erro

Todas as respostas de erro seguem este formato:

```json
{
  "error": "codigo_erro",
  "message": "Descricao legivel por humanos"
}
```

### Codigos de Status HTTP

| Codigo | Descricao |
|--------|-----------|
| 200 | Sucesso |
| 201 | Criado |
| 400 | Requisicao Invalida (erro de validacao) |
| 401 | Nao Autorizado (chave API invalida) |
| 404 | Nao Encontrado |
| 429 | Rate Limited |
| 500 | Erro Interno do Servidor |
| 502 | Bad Gateway (erro STA) |
| 503 | Servico Indisponivel |

### Codigos de Erro

| Codigo | Descricao |
|--------|-----------|
| `invalid_request` | Corpo da requisicao malformado |
| `validation_error` | Validacao da requisicao falhou |
| `invalid_content` | Conteudo base64 invalido |
| `not_found` | Arquivo nao encontrado |
| `sta_error` | Erro do WebService STA |
| `sta_auth_error` | Autenticacao STA falhou |
| `sta_rate_limit` | Limite de taxa STA excedido |
| `sta_connection_error` | Falha ao conectar ao STA |
| `internal_error` | Erro interno do servidor |

---

## Exemplos de Codigo

### cURL

```bash
# Health check
curl -H "Authorization: Bearer ${API_KEY}" \
  http://localhost:8080/api/v1/health

# Enviar arquivo
curl -X POST http://localhost:8080/api/v1/files \
  -H "Authorization: Bearer ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "system": "CCS",
    "file_type": "ARQC001",
    "data": {
      "content": "'$(base64 -w0 arquivo.txt)'",
      "file_name": "CCS_20260131_001.txt"
    },
    "destination": {
      "ispb": "00000000"
    }
  }'

# Verificar status
curl -H "Authorization: Bearer ${API_KEY}" \
  http://localhost:8080/api/v1/files/550e8400-.../status
```

### Python

```python
import requests
import base64
from typing import Optional

class STAConnectorClient:
    def __init__(self, base_url: str, api_key: str):
        self.base_url = base_url.rstrip('/')
        self.headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        }

    def health(self) -> dict:
        """Verificar saude do servico."""
        response = requests.get(
            f'{self.base_url}/api/v1/health',
            headers=self.headers
        )
        response.raise_for_status()
        return response.json()

    def submit_file(
        self,
        system: str,
        file_type: str,
        content: bytes,
        ispb: str,
        file_name: Optional[str] = None
    ) -> dict:
        """Enviar um arquivo para o BCB STA."""
        payload = {
            'system': system,
            'file_type': file_type,
            'data': {
                'content': base64.b64encode(content).decode('utf-8')
            },
            'destination': {
                'ispb': ispb
            }
        }

        if file_name:
            payload['data']['file_name'] = file_name

        response = requests.post(
            f'{self.base_url}/api/v1/files',
            headers=self.headers,
            json=payload
        )
        response.raise_for_status()
        return response.json()

    def get_status(self, file_id: str) -> dict:
        """Obter status do upload do arquivo."""
        response = requests.get(
            f'{self.base_url}/api/v1/files/{file_id}/status',
            headers=self.headers
        )
        response.raise_for_status()
        return response.json()

    def wait_for_completion(
        self,
        file_id: str,
        timeout: int = 300,
        poll_interval: int = 5
    ) -> dict:
        """Aguardar conclusao do processamento do arquivo."""
        import time
        start = time.time()

        while time.time() - start < timeout:
            status = self.get_status(file_id)

            if status['status'] in ('completed', 'failed'):
                return status

            time.sleep(poll_interval)

        raise TimeoutError(f'Arquivo {file_id} nao completou em {timeout}s')


# Exemplo de uso
if __name__ == '__main__':
    client = STAConnectorClient(
        base_url='http://localhost:8080',
        api_key='sua-chave-api'
    )

    # Verificar saude
    health = client.health()
    print(f"Status: {health['status']}")

    # Enviar arquivo
    with open('arquivo_CCS.txt', 'rb') as f:
        content = f.read()

    result = client.submit_file(
        system='CCS',
        file_type='ARQC001',
        content=content,
        ispb='00000000',
        file_name='CCS_20260131_001.txt'
    )

    print(f"Protocolo: {result['protocol']}")
    print(f"ID do Arquivo: {result['id']}")

    # Aguardar conclusao
    final_status = client.wait_for_completion(result['id'])
    print(f"Status final: {final_status['status']}")
```

### Node.js

```javascript
const axios = require('axios');
const fs = require('fs');

class STAConnectorClient {
  constructor(baseUrl, apiKey) {
    this.client = axios.create({
      baseURL: baseUrl,
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json'
      }
    });
  }

  async health() {
    const response = await this.client.get('/api/v1/health');
    return response.data;
  }

  async submitFile({ system, fileType, content, ispb, fileName }) {
    const base64Content = Buffer.isBuffer(content)
      ? content.toString('base64')
      : content;

    const payload = {
      system,
      file_type: fileType,
      data: {
        content: base64Content,
        ...(fileName && { file_name: fileName })
      },
      destination: {
        ispb
      }
    };

    const response = await this.client.post('/api/v1/files', payload);
    return response.data;
  }

  async getStatus(fileId) {
    const response = await this.client.get(`/api/v1/files/${fileId}/status`);
    return response.data;
  }

  async waitForCompletion(fileId, { timeout = 300000, pollInterval = 5000 } = {}) {
    const start = Date.now();

    while (Date.now() - start < timeout) {
      const status = await this.getStatus(fileId);

      if (['completed', 'failed'].includes(status.status)) {
        return status;
      }

      await new Promise(resolve => setTimeout(resolve, pollInterval));
    }

    throw new Error(`Arquivo ${fileId} nao completou em ${timeout}ms`);
  }
}

// Exemplo de uso
async function main() {
  const client = new STAConnectorClient(
    'http://localhost:8080',
    'sua-chave-api'
  );

  // Verificar saude
  const health = await client.health();
  console.log('Status:', health.status);

  // Enviar arquivo
  const content = fs.readFileSync('arquivo_CCS.txt');

  const result = await client.submitFile({
    system: 'CCS',
    fileType: 'ARQC001',
    content,
    ispb: '00000000',
    fileName: 'CCS_20260131_001.txt'
  });

  console.log('Protocolo:', result.protocol);
  console.log('ID do Arquivo:', result.id);

  // Aguardar conclusao
  const finalStatus = await client.waitForCompletion(result.id);
  console.log('Status final:', finalStatus.status);
}

main().catch(console.error);
```

### Go

```go
package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"
)

type STAConnectorClient struct {
	BaseURL string
	APIKey  string
	Client  *http.Client
}

type SubmitRequest struct {
	System      string      `json:"system"`
	FileType    string      `json:"file_type"`
	Data        FileData    `json:"data"`
	Destination Destination `json:"destination"`
}

type FileData struct {
	Content  string `json:"content"`
	FileName string `json:"file_name,omitempty"`
}

type Destination struct {
	ISPB string `json:"ispb"`
}

type SubmitResponse struct {
	ID        string    `json:"id"`
	Protocol  string    `json:"protocol"`
	Status    string    `json:"status"`
	CreatedAt time.Time `json:"created_at"`
	FileName  string    `json:"file_name"`
	FileSize  int64     `json:"file_size"`
	Checksum  string    `json:"checksum"`
}

type StatusResponse struct {
	ID           string     `json:"id"`
	Protocol     string     `json:"protocol"`
	FileName     string     `json:"file_name"`
	FileType     string     `json:"file_type"`
	Status       string     `json:"status"`
	SourceSystem string     `json:"source_system"`
	CreatedAt    time.Time  `json:"created_at"`
	UpdatedAt    time.Time  `json:"updated_at"`
	CompletedAt  *time.Time `json:"completed_at,omitempty"`
}

func NewClient(baseURL, apiKey string) *STAConnectorClient {
	return &STAConnectorClient{
		BaseURL: baseURL,
		APIKey:  apiKey,
		Client:  &http.Client{Timeout: 30 * time.Second},
	}
}

func (c *STAConnectorClient) SubmitFile(system, fileType string, content []byte, ispb, fileName string) (*SubmitResponse, error) {
	reqBody := SubmitRequest{
		System:   system,
		FileType: fileType,
		Data: FileData{
			Content:  base64.StdEncoding.EncodeToString(content),
			FileName: fileName,
		},
		Destination: Destination{ISPB: ispb},
	}

	jsonBody, err := json.Marshal(reqBody)
	if err != nil {
		return nil, fmt.Errorf("marshal request: %w", err)
	}

	req, err := http.NewRequest("POST", c.BaseURL+"/api/v1/files", bytes.NewReader(jsonBody))
	if err != nil {
		return nil, fmt.Errorf("create request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+c.APIKey)
	req.Header.Set("Content-Type", "application/json")

	resp, err := c.Client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("do request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusCreated {
		body, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("status inesperado %d: %s", resp.StatusCode, body)
	}

	var result SubmitResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, fmt.Errorf("decode response: %w", err)
	}

	return &result, nil
}

func (c *STAConnectorClient) GetStatus(fileID string) (*StatusResponse, error) {
	req, err := http.NewRequest("GET", c.BaseURL+"/api/v1/files/"+fileID+"/status", nil)
	if err != nil {
		return nil, fmt.Errorf("create request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+c.APIKey)

	resp, err := c.Client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("do request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("status inesperado %d: %s", resp.StatusCode, body)
	}

	var result StatusResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, fmt.Errorf("decode response: %w", err)
	}

	return &result, nil
}

func main() {
	client := NewClient("http://localhost:8080", "sua-chave-api")

	// Ler conteudo do arquivo
	content, err := os.ReadFile("arquivo_CCS.txt")
	if err != nil {
		panic(err)
	}

	// Enviar arquivo
	result, err := client.SubmitFile("CCS", "ARQC001", content, "00000000", "CCS_20260131_001.txt")
	if err != nil {
		panic(err)
	}

	fmt.Printf("Protocolo: %s\n", result.Protocol)
	fmt.Printf("ID do Arquivo: %s\n", result.ID)

	// Fazer polling ate conclusao
	for {
		status, err := client.GetStatus(result.ID)
		if err != nil {
			panic(err)
		}

		fmt.Printf("Status: %s\n", status.Status)

		if status.Status == "completed" || status.Status == "failed" {
			break
		}

		time.Sleep(5 * time.Second)
	}
}
```

---

## Limites de Taxa

O connector aplica os limites de taxa do BCB:

- **Maximo 10** uploads simultaneos por instituicao
- **Maximo 120** consultas por minuto por instituicao

Quando rate limited, voce recebera uma resposta 429:

```json
{
  "error": "sta_rate_limit",
  "message": "Limite de taxa STA excedido"
}
```

Boas praticas:

1. Implemente backoff exponencial
2. Monitore metricas de rate limit via API Admin
3. Enfileire uploads durante periodos de alto trafego
4. Use o header `Retry-After` quando fornecido

---

## Proximos Passos

- [API Admin](/pt/api/admin) - Endpoints de gerenciamento e monitoramento
- [Configuracao](/pt/guide/configuration) - Opcoes de configuracao
- [Solucao de Problemas](/pt/troubleshooting) - Problemas comuns
