Metadata-Version: 2.4
Name: dattos
Version: 0.4.2
Summary: CLI for the Dattos platform
Project-URL: Homepage, https://dattos.com.br
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial
Requires-Python: >=3.11
Requires-Dist: httpx<1,>=0.27
Requires-Dist: typer<1,>=0.12
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Description-Content-Type: text/markdown

# dattos

CLI for the [Dattos](https://dattos.com.br) reconciliation platform — designed as an **agent-native interface** for AI agents (Claude Code, custom agents) to operate Dattos programmatically.

All output is **JSON to stdout**, errors go to **stderr**, making it trivially parseable by both humans and machines.

## Installation

Requires **Python 3.11+**.

```bash
pip install dattos

# Verify
dattos version
# {"version": "0.3.0"}
```

### From source (development)

```bash
git clone git@bitbucket.org:dattos/dattos-cli.git
cd dattos-cli
pip install -e .
```

## Authentication

The CLI authenticates via API key + organization ID. Configure using **environment variables** or the **config file** (`~/.dattos/config.json`).

### Environment Variables

```bash
export DATTOS_API_URL="https://your-instance.dattos.com.br/dattos.api"
export DATTOS_API_KEY="api-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
export DATTOS_ORG_ID="1"
export DATTOS_FOLDER_ID="2473"  # optional, default folder for all commands
```

### Config File

```bash
dattos config set api-url "https://your-instance.dattos.com.br/dattos.api"
dattos config set api-key "api-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
dattos config set org-id "1"
```

Saved to `~/.dattos/config.json`. Environment variables take precedence over the config file.

### Folder ID

Most Dattos API endpoints require a **folder context**. There are three ways to set it (in priority order):

```bash
# 1. Per-command flag (highest priority)
dattos analyses list --folder-id 2473

# 2. Environment variable
export DATTOS_FOLDER_ID="2473"
dattos analyses list

# 3. Config file (persistent, recommended)
dattos config set folder-id "2473"
dattos analyses list
```

To discover available folder IDs, use `dattos folders list`:

```bash
# Full tree (raw JSON)
dattos folders list

# Flat list with id, name, and full path (easier to read)
dattos folders list --flat
```

## Commands

### `dattos analyses` — Manage analyses

```bash
# List analyses (with pagination and search)
dattos analyses list --page-size 10
# {"data": [{"analysisId": 5192, "analysisCode": "PRD-7728", "analysisName": "Reconciliação Bancária", ...}], "totalItems": 42}

dattos analyses list --search "RECON-001" --folder-id 2473
dattos analyses list --inactive  # include inactive/archived

# Get analysis by ID
dattos analyses get 5192 --folder-id 2473
# {"analysisId": 5192, "analysisCode": "PRD-7728", "analysisName": "Reconciliação Bancária", "isActive": true, ...}

# Get analysis by code
dattos analyses get-by-code "ETL-1" --folder-id 2473
# {"analysisId": 4137, "analysisCode": "ETL-1", ...}

# List data sources and connector info
dattos analyses datasources 5192 --folder-id 2473
# [{"id": 101, "name": "Extrato Bancário", "connectorType": "Excel", ...}]

# Check execution status at a reference date
dattos analyses execution-status 5192 --date 2025-12-20 --folder-id 2473
# {"executionJobId": "89625", "status": "Loaded", "completedPercent": 100.0, ...}
# Status values: NoLoad, Loading, Loaded, Error, Inactive, InvalidPreparation

# Start an analysis execution (default: replace mode)
dattos analyses start 5192 --date 2026-03-08
# {"status": "started", "analysisId": 5192, "referenceDate": "2026-03-08"}

# Start with file upload (auto-uploads, then starts)
dattos analyses start 5192 --date 2026-03-08 --file data.csv
# Supported: .csv, .edi, .ofx, .pdf, .rem, .ret, .txt, .xlsx, .xls, .xlsb, .xml

# Start and wait for completion (polls every 5s)
dattos analyses start 5192 --date 2026-03-08 --wait

# Incremental import (preserves existing data, adds new)
dattos analyses start 5192 --date 2026-03-08 --incremental
```

### `dattos loads` — Query analysis loads

```bash
# Get load details for a reference date (returns load ID + dataset IDs)
dattos loads get 5192 2025-12-20 --folder-id 2473
# {"id": 3248, "referenceDate": "2025-12-20", "datasets": [{"id": 2476, "name": "Extrato", "columns": [...]}]}

# List all reference dates with loaded data
dattos loads list-dates 4137 --from 2025-01-01 --to 2026-12-31 --folder-id 2473
# ["2025-12-09T00:00:00", "2025-12-10T00:00:00", "2025-12-11T00:00:00"]

# List available views for a load
dattos loads views 5192 3248 --folder-id 2473
# [{"id": 1, "name": "Summary"}, {"id": 2, "name": "Detail"}]
```

### `dattos datasets` — Query dataset rows

```bash
# Search rows in a dataset (requires analysis ID, load ID, dataset ID)
dattos datasets search 5192 3248 2476 --page-size 10 --folder-id 2473
# {"rows": {"data": [{"id": 1, "valor": 1500.00, "data": "2025-12-20", ...}]}, "totalRows": 1523}

# With pagination (default page-size is 50)
dattos datasets search 5192 3248 2476 --page-size 50 --page 2

# With filter (JSON string)
dattos datasets search 5192 3248 2476 --filter '{"column": "status", "value": "active"}'
```

### `dattos matching` — Reconciliation results

```bash
# Get matching execution info
dattos matching get 5192 3248 2475 --folder-id 2473
# {"id": 1, "status": "Completed", "matchedCount": 1523, ...}

# Get totals by status (matched, unmatched, pending)
dattos matching status 5192 3248 2475 --folder-id 2473
# {"Matched": 1523, "Unmatched": 47, "Pending": 0}

# Get matching summary report
dattos matching summary 5192 3248 2475 --folder-id 2473
# [{"column": "Account", "matched": 100, "unmatched": 5}, ...]
```

### `dattos reports` — Generate and download reports

```bash
# Generate a report (async — returns report ID to poll)
dattos reports generate 5192 --load-id 3248 --dataset-id 2476 --format csv
# {"id": 99, "status": "Processing"}

dattos reports generate 5192 --load-id 3248 --dataset-id 2476 --format xlsx
# Note: >1M rows only supports CSV format

# Check report generation status
dattos reports status 5192 --load-id 3248 --dataset-id 2476
# {"id": 99, "status": "Completed", "downloadUrl": "..."}

# Download a generated report
dattos reports download 5192 99 --output report.csv
# {"status": "downloaded", "file": "report.csv"}
```

### `dattos workflows` — Manage and execute workflows

```bash
# List workflows
dattos workflows list --page-size 10
dattos workflows list --name "Daily Recon"
# {"data": [{"Workflow": {"Id": 1, "Name": "Daily Recon"}, "ExecutionWorkflow": {...}}, ...], "totalItems": 5}

# Get workflow details
dattos workflows get 1
# {"Workflow": {"Id": 1, "Name": "Daily Recon", "IsActive": true}, "ExecutionWorkflow": {"Status": "Done", ...}}

# Start a workflow for a reference date
dattos workflows start 1 --date 2026-03-08
# {"status": "started", "workflowId": 1, "referenceDate": "2026-03-08"}

# Stop a running workflow execution
dattos workflows stop 10  # uses execution ID, not workflow ID
# {"status": "stopped", "executionId": 10}

# View execution history
dattos workflows history 1 --page-size 20
# {"data": [{"Id": 10, "Status": "Done", "ReferenceDate": "2026-03-07"}, ...], "totalItems": 15}

# View task logs
dattos workflows logs 55  # uses task ID from execution
# [{"message": "Starting import", "timestamp": "2026-03-07T10:00:00"}, ...]
```

### `dattos imports` — Monitor and manage imports

```bash
# List imports (with optional filters)
dattos imports list
dattos imports list --from 2026-01-01 --to 2026-03-31
dattos imports list --status Error
dattos imports list --include-canceled
# {"data": [{"Id": 1, "Status": "Success", "TotalSuccess": 100, ...}], "totalItems": 25}

# Get import details
dattos imports get 1
# {"Id": 1, "Status": "Success", "TotalSuccess": 100, "TotalRejections": 0, ...}

# View rejection details
dattos imports results 1
# {"data": [{"line": 5, "reason": "Invalid date format"}], "totalItems": 1}

# View import logs
dattos imports logs 1
# {"data": [{"message": "Processing started", "timestamp": "..."}], "totalItems": 3}

# Cancel an import
dattos imports cancel 1
# {"status": "canceled", "importId": 1}
```

### `dattos folders` — List and inspect folders

```bash
# List all folders as a tree (raw JSON)
dattos folders list

# Flat list — easier to find folder IDs
dattos folders list --flat
# [{"id": 2473, "code": "SIR", "name": "sirio", "fullPath": "Engenharia / sirio", "depth": 1}, ...]
```

### `dattos users` — Manage users

```bash
# List all users (flat array)
dattos users list
dattos users list --search "john" --page-size 20
# [{"Id": 4, "UserCode": "david.barouh", "FullName": "David Barouh", "EmailAddress": "...", "Blocked": false, ...}]

# Get a user by ID
dattos users get 4
# {"Id": 4, "UserCode": "david.barouh", "FullName": "David Barouh", ...}

# List available Privacy Profiles (roles)
dattos users list-roles
# [{"Id": 1, "Name": "Administradores", ...}, {"Id": 2, "Name": "Engenharia", ...}]

# Get roles assigned to a user
dattos users roles 4
# [{"Id": 1, "Name": "Administradores", ...}, ...]

# Create a new user — Dattos sends an invitation email automatically
dattos users create --code "JDOE" --name "John Doe" --email "john.doe@company.com"
dattos users create --code "JDOE" --name "John Doe" --email "john.doe@company.com" --culture "en-US"
# {"Id": 80, "UserCode": "JDOE", "FullName": "John Doe", ...}

# Update a user (read-modify-write — only provided fields change)
dattos users update 80 --name "John P. Doe"
dattos users update 80 --culture "en-US"
dattos users update 80 --blocked   # block the user
dattos users update 80 --active    # unblock the user

# Assign Privacy Profiles — REPLACES all existing roles
dattos users set-roles 80 --role-id 1 --role-id 2
# {} (success)

# Delete a user — requires --yes (irreversible)
dattos users delete 80 --yes
# {"status": "deleted", "userId": 80}
```

### `dattos config` — Manage configuration

```bash
# Set config values (saved to ~/.dattos/config.json)
dattos config set api-url "https://your-instance.dattos.com.br/dattos.api"
dattos config set api-key "api-xxxxxxxx"
dattos config set org-id "1"
dattos config set folder-id "2473"
# {"key": "folder-id", "status": "saved"}

# Show current resolved configuration (API key is masked)
dattos config show
# {"api_url": "https://your-instance.dattos.com.br/dattos.api", "api_key": "api-xxxx...", "org_id": "1", "folder_id": "2473"}
```

### `dattos version`

```bash
dattos version
# {"version": "0.3.0"}
```

## Usage Examples

### Example 1: First-time setup

```bash
# Configure credentials
dattos config set api-url "https://your-instance.dattos.com.br/dattos.api"
dattos config set api-key "api-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
dattos config set org-id "1"

# Discover your folders
dattos folders list --flat | jq '.[] | "\(.id) — \(.fullPath)"'
# "2473 — Engenharia / sirio"
# "2474 — Engenharia / delta"

# Set a default folder
dattos config set folder-id "2473"

# Verify everything works
dattos config show
dattos analyses list --page-size 5
```

### Example 2: Inspect reconciliation results

```bash
# Find the analysis by code
dattos analyses get-by-code "RECON-BANK"
# Note the analysisId (e.g., 5192)

# Check if data is loaded for a date
dattos analyses execution-status 5192 --date 2026-03-01
# {"status": "Loaded", "completedPercent": 100.0, ...}

# Get the load (returns load ID and dataset IDs)
dattos loads get 5192 2026-03-01
# Note loadId (3248) and datasetId (2476)

# Browse the data
dattos datasets search 5192 3248 2476 --page-size 10

# Check matching results
dattos matching status 5192 3248 2475
# {"Matched": 1523, "Unmatched": 47, "Pending": 0}
```

### Example 3: Export data for external analysis

```bash
# Generate a CSV report
dattos reports generate 5192 --load-id 3248 --dataset-id 2476 --format csv
# {"id": 99, "status": "Processing"}

# Wait for it to complete
dattos reports status 5192 --load-id 3248 --dataset-id 2476
# {"id": 99, "status": "Completed"}

# Download the file
dattos reports download 5192 99 --output reconciliation_2026-03-01.csv
# {"status": "downloaded", "file": "reconciliation_2026-03-01.csv"}
```

### Example 4: Find loaded dates for an analysis

```bash
# Which dates have data loaded?
dattos loads list-dates 4137 --from 2026-01-01 --to 2026-03-31 | jq -r '.[]'
# 2026-01-15T00:00:00
# 2026-02-15T00:00:00
# 2026-03-15T00:00:00
```

### Example 5: Composing with jq

```bash
# List analysis names and codes
dattos analyses list --page-size 100 | jq '.data[] | {code: .analysisCode, name: .analysisName}'

# Count unmatched items
dattos matching status 5192 3248 2475 | jq '.Unmatched'

# Get dataset column names
dattos loads get 5192 2026-03-01 | jq '.datasets[].columns[].name'

# Check if execution is done (useful in scripts)
STATUS=$(dattos analyses execution-status 5192 --date 2026-03-01 | jq -r '.status')
if [ "$STATUS" = "Loaded" ]; then echo "Ready"; fi
```

## Typical Agent Workflow

A typical AI agent workflow to inspect reconciliation results:

```bash
# 0. First time: discover folders and set default
dattos folders list --flat
dattos config set folder-id "2473"

# 1. List analyses
dattos analyses list --page-size 10

# 2. Check execution status
dattos analyses execution-status 5192 --date 2025-12-20

# 3. Get load details (returns load ID + dataset IDs)
dattos loads get 5192 2025-12-20

# 4. Search dataset rows
dattos datasets search 5192 3248 2476 --page-size 50

# 5. Check matching results
dattos matching status 5192 3248 2475

# 6. Export a report
dattos reports generate 5192 --load-id 3248 --dataset-id 2476 --format csv
```

## Output Format

All commands output **JSON to stdout**. Errors output structured JSON to **stderr**.

```bash
# Success — JSON to stdout
dattos analyses get 5192
# {"analysisId": 5192, "analysisCode": "PRD-7728", ...}

# Error — JSON to stderr, non-zero exit code
dattos analyses get 99999
# {"error": "not_found", "message": "A entidade procurada não existe.", "status": 404}
```

### Exit Codes

| Code | Meaning |
|------|---------|
| `0` | Success |
| `1` | API error (4xx/5xx) |
| `2` | Auth/config error (missing API key, invalid credentials) |

### Piping and Filtering

Because output is JSON, it composes naturally with `jq`:

```bash
# Get just the analysis names
dattos analyses list --folder-id 2473 | jq '.data[].analysisName'

# Get loaded dates as plain text
dattos loads list-dates 4137 --from 2025-01-01 --to 2026-12-31 --folder-id 2473 | jq -r '.[]'

# Check if execution is complete
dattos analyses execution-status 5192 --date 2025-12-20 | jq -r '.status'
```

## Error Handling

The CLI handles common API errors with descriptive messages:

| HTTP Status | Error Type | Description |
|-------------|------------|-------------|
| `401` | `auth_error` | Invalid or expired API key |
| `403` | `forbidden` | Insufficient permissions |
| `404` | `not_found` | Resource does not exist |
| `429` | `rate_limited` | Too many requests (auto-retried up to 3 times) |
| Other | `api_error` | General API error |

Rate limiting (HTTP 429) is handled automatically with exponential backoff (5s, 10s, 20s, max 30s).

## Project Structure

```
dattos-cli/
├── src/dattos_cli/
│   ├── __init__.py          # Version
│   ├── main.py              # Typer app, client factory, error handling
│   ├── client.py            # HTTP client (httpx) with retry logic
│   ├── config.py            # Config loading (env vars + ~/.dattos/config.json)
│   ├── output.py            # JSON output helpers (stdout/stderr)
│   └── commands/
│       ├── analyses.py      # dattos analyses *
│       ├── loads.py          # dattos loads *
│       ├── datasets.py       # dattos datasets *
│       ├── matching.py       # dattos matching *
│       ├── reports.py        # dattos reports *
│       ├── folders.py        # dattos folders *
│       ├── workflows.py      # dattos workflows *
│       ├── imports.py         # dattos imports *
│       ├── users.py           # dattos users *
│       ├── etl.py             # dattos etl *
│       └── config_cmd.py     # dattos config *
├── tests/                   # 67 unit + integration tests
├── docs/plans/              # Design and implementation docs
├── pyproject.toml
└── .gitignore
```

## Development

### Branch Workflow

All development happens on `develop`. The `main` branch is protected — changes require a PR.

```bash
git checkout develop
# make changes...
git push origin develop
# create PR: develop → main
```

### Setup

```bash
pip install -e .
pip install pytest

# Run tests
pytest -v

# Run a specific test
pytest tests/test_analyses.py -v
```

### Tech Stack

- **Python 3.11+**
- **[Typer](https://typer.tiangolo.com/)** — CLI framework with type hints and auto-generated help
- **[httpx](https://www.python-httpx.org/)** — HTTP client with MockTransport for testing
- **[hatchling](https://hatch.pypa.io/)** — Build backend

## Roadmap

- ~~**v0.2** — Execution commands~~ ✅ Done (analyses start, workflows, imports)
- ~~**v0.3** — User management~~ ✅ Done (users list/get/create/update/delete/set-roles)
- **v0.4** — Configuration commands (create/edit analyses, datasources, connectors)
- **Future** — Database connection info, interactive login, shell completion, table output format
