# Outbound Pipeline

The outbound pipeline handles file uploads from your FluxiQ STA system to BCB STA.

## Architecture Overview

```
┌─────────────────┐
│   FluxiQ STA    │
│   Application   │
└────────┬────────┘
         │
         │ POST /api/v1/files
         ▼
┌─────────────────┐
│  Outbound API   │
│ (Port 8080)     │
└────────┬────────┘
         │
         │ Validate & Store
         ▼
┌─────────────────┐
│  State Store    │
│ (SQLite/PG)     │
└────────┬────────┘
         │
         │ Request Protocol
         ▼
┌─────────────────┐
│  STA Client     │
│ (SOAP/HTTPS)    │
└────────┬────────┘
         │
         │ Upload File
         ▼
┌─────────────────┐
│   BCB STA       │
│   WebService    │
└─────────────────┘
```

## How It Works

### 1. File Submission

Submit files via the REST API:

```bash
curl -X POST http://localhost:8080/api/v1/files \
  -H "Authorization: Bearer ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "system": "CCS",
    "file_type": "GCCS001",
    "data": {
      "content": "SGVsbG8gV29ybGQ=",
      "file_name": "CCS_20260131_001.txt"
    },
    "destination": {
      "ispbs": ["12345678"]
    }
  }'
```

Response:

```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": "sha256:abc123..."
}
```

### 2. Protocol Request

The connector requests an upload protocol from BCB:

```go
// Request protocol for upload
protocolResp, err := staClient.RequestProtocol(ctx, &ProtocolRequest{
    NomeArquivo:   fileName,
    TipoArquivo:   fileType,
    ISPBsDestino:  []string{"12345678"},
})
// Returns: protocol_number for the upload
```

### 3. File Upload

With the protocol number, the file is uploaded:

```go
// Upload file content to BCB
err := staClient.UploadFile(ctx, protocolNumber, content)
```

### 4. Status Tracking

Your system can poll for upload status or receive webhook notifications.

## API Reference

### Submit File

```http
POST /api/v1/files
Authorization: Bearer {api_key}
Content-Type: application/json

{
  "system": "CCS",
  "file_type": "GCCS001",
  "data": {
    "content": "base64-encoded-content",
    "file_name": "optional_filename.txt"
  },
  "destination": {
    "ispbs": ["12345678", "87654321"]
  },
  "metadata": {
    "reference": "your-internal-id"
  }
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `system` | string | Yes | BCB system code (CCS, SPI, etc.) |
| `file_type` | string | Yes | BCB file type identifier |
| `data.content` | string | Yes | Base64-encoded file content |
| `data.file_name` | string | No | Custom file name (auto-generated if omitted) |
| `destination.ispbs` | array | No | Destination ISPB codes |
| `metadata` | object | No | Custom metadata for tracking |

### Get File Status

```http
GET /api/v1/files/{id}/status
Authorization: Bearer {api_key}
```

Response:

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "system": "CCS",
  "file_name": "CCS_20260131_001.txt",
  "file_type": "GCCS001",
  "file_size": 12345,
  "checksum": "sha256:abc123...",
  "status": "completed",
  "protocol": "2026013100001234",
  "sta_status": "PROCESSADO",
  "sta_message": null,
  "created_at": "2026-01-31T10:30:00Z",
  "updated_at": "2026-01-31T10:30:15Z"
}
```

### Health Check

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

Response:

```json
{
  "status": "healthy",
  "sta_connection": "ok",
  "database": "ok",
  "uptime_seconds": 86400,
  "version": "1.0.0"
}
```

## File States

Files move through the following states:

```
┌─────────┐    ┌────────────┐    ┌───────────┐
│ pending │───▶│ uploading  │───▶│ uploaded  │
└─────────┘    └─────┬──────┘    └─────┬─────┘
                     │                  │
                     ▼                  ▼
               ┌──────────┐      ┌───────────┐
               │  failed  │      │ completed │
               └──────────┘      └───────────┘
```

| Status | Description |
|--------|-------------|
| `pending` | Queued for processing |
| `uploading` | Protocol received, uploading content |
| `uploaded` | Successfully uploaded to BCB |
| `processing` | BCB is processing the file |
| `completed` | BCB accepted the file |
| `failed` | Upload or processing failed |

## Configuration

### Outbound API Setup

```yaml
outbound_api:
  enabled: true
  host: "0.0.0.0"
  port: 8080
  auth:
    type: "api_key"
    api_key: "${OUTBOUND_API_KEY}"
```

### Authentication Types

#### API Key

```yaml
outbound_api:
  auth:
    type: "api_key"
    api_key: "${OUTBOUND_API_KEY}"
```

Usage:

```bash
curl -H "Authorization: Bearer ${API_KEY}" ...
# or
curl -H "X-API-Key: ${API_KEY}" ...
```

#### No Authentication (Development Only)

```yaml
outbound_api:
  auth:
    type: "none"
```

## File Validation

### Built-in Validation

The connector validates files before submission:

| Check | Description |
|-------|-------------|
| Content size | Maximum 50 MB |
| Base64 encoding | Must be valid base64 |
| System ID | Must be valid BCB system |
| File type | Must be valid for the system |

### Validation Error

```json
{
  "error": "validation_error",
  "message": "data.content must be valid base64"
}
```

## Error Handling

### Common Errors

| Error Code | HTTP Status | Description | Resolution |
|------------|-------------|-------------|------------|
| `invalid_request` | 400 | Malformed request | Check request body format |
| `validation_error` | 400 | Validation failed | Fix validation errors |
| `sta_auth_error` | 502 | STA authentication failed | Check credentials |
| `sta_rate_limit` | 429 | BCB rate limit exceeded | Wait and retry |
| `sta_error` | 502 | STA service error | Check BCB status |
| `sta_connection_error` | 502 | Cannot connect to STA | Check network |
| `internal_error` | 500 | Unexpected error | Check logs |

### Error Response

```json
{
  "error": "sta_rate_limit",
  "message": "STA rate limit exceeded"
}
```

## Retry Handling

### Automatic Retries

The connector automatically retries on transient failures:

- Network timeouts
- STA temporary errors (5xx)
- Rate limit with Retry-After header

Retry behavior:

| Setting | Value |
|---------|-------|
| Max retries | 3 (configurable) |
| Backoff | Exponential (1s, 2s, 4s) |

### Manual Retry

Retry a failed upload via Admin API:

```bash
curl -u admin:password -X POST \
  http://localhost:8081/admin/retry/{file_id}
```

## Status Tracking

### Polling

Poll for status updates:

```bash
# Check status
curl -H "Authorization: Bearer ${API_KEY}" \
  http://localhost:8080/api/v1/files/{id}/status
```

### Status Refresh

The connector refreshes status from BCB when you query files in `uploaded` or `processing` status.

## Monitoring

### Metrics

View outbound metrics via Admin API:

```bash
curl -u admin:password http://localhost:8081/admin/metrics
```

```json
{
  "timestamp": "2026-01-31T10:30:00Z",
  "outbound": {
    "files_submitted": 5000,
    "files_uploaded": 4995,
    "files_failed": 3,
    "files_pending": 2
  }
}
```

### Key Metrics

| Metric | Description |
|--------|-------------|
| `files_submitted` | Total files submitted |
| `files_uploaded` | Successfully uploaded to BCB |
| `files_failed` | Failed uploads |
| `files_pending` | Awaiting upload |

## Code Examples

### cURL

```bash
# Submit file
curl -X POST http://localhost:8080/api/v1/files \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "system": "CCS",
    "file_type": "GCCS001",
    "data": {
      "content": "'$(base64 -w0 file.txt)'"
    }
  }'

# Check status
curl http://localhost:8080/api/v1/files/550e8400-.../status \
  -H "Authorization: Bearer your-api-key"
```

### Python

```python
import requests
import base64

API_URL = "http://localhost:8080/api/v1"
API_KEY = "your-api-key"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

# Read and encode file
with open("file.txt", "rb") as f:
    content = base64.b64encode(f.read()).decode()

# Submit file
response = requests.post(
    f"{API_URL}/files",
    headers=headers,
    json={
        "system": "CCS",
        "file_type": "GCCS001",
        "data": {
            "content": content,
            "file_name": "CCS_20260131_001.txt"
        }
    }
)

result = response.json()
file_id = result["id"]
print(f"Submitted: {file_id}, Protocol: {result['protocol']}")

# Check status
status_response = requests.get(
    f"{API_URL}/files/{file_id}/status",
    headers=headers
)

print(f"Status: {status_response.json()['status']}")
```

### Node.js

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

const API_URL = 'http://localhost:8080/api/v1';
const API_KEY = 'your-api-key';

const headers = {
  Authorization: `Bearer ${API_KEY}`,
  'Content-Type': 'application/json'
};

async function uploadFile() {
  // Read and encode file
  const content = fs.readFileSync('file.txt').toString('base64');

  // Submit file
  const response = await axios.post(
    `${API_URL}/files`,
    {
      system: 'CCS',
      file_type: 'GCCS001',
      data: {
        content,
        file_name: 'CCS_20260131_001.txt'
      }
    },
    { headers }
  );

  const { id, protocol, status } = response.data;
  console.log(`Submitted: ${id}, Protocol: ${protocol}`);

  // Poll for completion
  let currentStatus = status;
  while (currentStatus === 'uploaded' || currentStatus === 'processing') {
    await new Promise(r => setTimeout(r, 2000));

    const statusResponse = await axios.get(
      `${API_URL}/files/${id}/status`,
      { headers }
    );

    currentStatus = statusResponse.data.status;
    console.log(`Status: ${currentStatus}`);
  }

  console.log(`Final status: ${currentStatus}`);
}

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

### Go

```go
package main

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

const (
    apiURL = "http://localhost:8080/api/v1"
    apiKey = "your-api-key"
)

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

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

type FileSubmitResponse struct {
    ID       string `json:"id"`
    Protocol string `json:"protocol"`
    Status   string `json:"status"`
}

func main() {
    // Read and encode file
    content, _ := os.ReadFile("file.txt")
    encoded := base64.StdEncoding.EncodeToString(content)

    // Prepare request
    req := FileSubmitRequest{
        System:   "CCS",
        FileType: "GCCS001",
        Data: FileData{
            Content:  encoded,
            FileName: "CCS_20260131_001.txt",
        },
    }

    body, _ := json.Marshal(req)

    // Submit file
    httpReq, _ := http.NewRequest("POST", apiURL+"/files", bytes.NewReader(body))
    httpReq.Header.Set("Authorization", "Bearer "+apiKey)
    httpReq.Header.Set("Content-Type", "application/json")

    resp, _ := http.DefaultClient.Do(httpReq)
    defer resp.Body.Close()

    respBody, _ := io.ReadAll(resp.Body)

    var result FileSubmitResponse
    json.Unmarshal(respBody, &result)

    fmt.Printf("Submitted: %s, Protocol: %s, Status: %s\n",
        result.ID, result.Protocol, result.Status)
}
```

## Best Practices

1. **Use unique file names** - Include timestamp and sequence number
2. **Store the file ID** - For status tracking and troubleshooting
3. **Handle rate limits gracefully** - Implement backoff in your client
4. **Monitor queue depth** - Alert if pending files grow
5. **Implement idempotency** - Use metadata for deduplication
6. **Validate before sending** - Check file format locally

## BCB Rate Limits

BCB enforces strict rate limits:

| Limit | Value |
|-------|-------|
| Max Concurrent Uploads | 10 |
| Max Queries per Minute | 120 |

The connector automatically queues uploads when limits are reached.

## Next Steps

- [Inbound Pipeline](/guide/inbound) - Receiving files from BCB
- [Admin API](/api/admin) - Management and monitoring
- [Outbound API Reference](/api/outbound) - Full API documentation
