Metadata-Version: 2.4
Name: autotouch-cli
Version: 0.2.5
Summary: Autotouch Smart Table CLI
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.31.0
Requires-Dist: python-dotenv>=1.0.0

# Autotouch CLI Reference (`autotouch`)

This page documents the installable CLI for the Smart Table developer API.
Use it when you want less boilerplate than raw HTTP/cURL while keeping the same API behavior, auth, and scopes.

## Install

```bash
pipx install autotouch-cli
# or
pip install autotouch-cli
```

## Configure auth

Developer keys and scopes are identical to raw API usage (`stk_...`, same scope checks).

```bash
autotouch auth set-key --api-key stk_... --base-url https://app.autotouch.ai
autotouch auth check
autotouch auth show
```

Credentials are stored in `~/.config/autotouch/config.json` by default.
Override path with `AUTOTOUCH_CONFIG_PATH`.

## API endpoint -> CLI command map

| API endpoint | CLI command |
| --- | --- |
| `GET /api/capabilities` | `autotouch capabilities` |
| `POST /api/tables` | `autotouch tables create --name "My Table"` |
| `GET /api/tables?view_mode=org` | `autotouch tables list --view-mode org` |
| `POST /api/tables/{table_id}/rows` | `autotouch rows add --table-id <TABLE_ID> --records-file rows.json` |
| `POST /api/tables/{table_id}/import-optimized` | `autotouch rows import-csv --table-id <TABLE_ID> --file contacts.csv` |
| `GET /api/tables/{table_id}/import-status/{task_id}` | `autotouch rows import-status --table-id <TABLE_ID> --task-id <TASK_ID>` |
| `PATCH /api/tables/{table_id}/cells` | `autotouch cells patch --table-id <TABLE_ID> --updates-file updates.json` |
| `GET /api/tables/{table_id}/columns` | `autotouch columns list --table-id <TABLE_ID>` |
| `POST /api/tables/{table_id}/columns` | `autotouch columns create --table-id <TABLE_ID> --data-file column.json` |
| `PATCH /api/tables/{table_id}/columns/{column_id}` | `autotouch columns update --table-id <TABLE_ID> --column-id <COLUMN_ID> --data-file column-update.json` |
| `POST /api/tables/{table_id}/columns/projections` | `autotouch columns projections --table-id <TABLE_ID> --data-file projections.json` |
| `POST /api/tables/{table_id}/columns/{column_id}/estimate` | `autotouch columns estimate --table-id <TABLE_ID> --column-id <COLUMN_ID> --scope all` |
| `POST /api/tables/{table_id}/columns/{column_id}/run` | `autotouch columns run --table-id <TABLE_ID> --column-id <COLUMN_ID> --scope all` |
| `GET /api/bulk-jobs/{job_id}` | `autotouch jobs get --job-id <JOB_ID>` |
| `GET /api/tables/{table_id}/webhook` | `autotouch webhooks get --table-id <TABLE_ID>` |
| `POST /api/tables/{table_id}/webhook` | `autotouch webhooks rotate --table-id <TABLE_ID>` |
| `POST /api/webhooks/tables/{table_id}/ingest` | `autotouch webhooks ingest --table-id <TABLE_ID> --records-file records.json --webhook-token <WEBHOOK_TOKEN>` |

## Column create payload recipes (CLI-ready)

All examples below are used with:

```bash
autotouch columns create --table-id <TABLE_ID> --data-file <payload.json>
```

### 1) `email_finder`

`email-finder.json`:

```json
{
  "key": "work_email",
  "label": "Work Email",
  "kind": "enrichment",
  "dataType": "json",
  "origin": "email_finder",
  "autoRun": "never",
  "config": {
    "provider": "smart_email_finder",
    "strategy": "cost_optimized",
    "lookupStrategy": "linkedin",
    "linkedinOnly": true,
    "enableProfileFallback": false,
    "linkedinUrl": "linkedin_url"
  }
}
```

### 2) `phone_finder`

`phone-finder.json`:

```json
{
  "key": "mobile_phone",
  "label": "Mobile Phone",
  "kind": "enrichment",
  "dataType": "json",
  "origin": "phone_finder",
  "autoRun": "never",
  "config": {
    "provider": "smart_phone_finder",
    "firstName": "first_name",
    "lastName": "last_name",
    "company": "company",
    "linkedinUrl": "linkedin_url"
  }
}
```

### 3) `lead_finder`

`lead-finder.json`:

```json
{
  "key": "lead_contacts",
  "label": "Lead Contacts",
  "kind": "enrichment",
  "dataType": "json",
  "origin": "ai",
  "autoRun": "never",
  "config": {
    "provider": "lead_finder",
    "sourceMode": "bulk_companies",
    "companyDomain": "domain",
    "strictness": "flexible_roles",
    "storeAsLeads": true,
    "autoEnrichEmails": true,
    "autoEnrichPhones": false,
    "jobTitles": ["Head of Sales", "VP Sales"],
    "locations": ["United States"],
    "maxResults": 10
  }
}
```

### 4) `llm_enrichment`

`llm-enrichment.json`:

```json
{
  "key": "company_research",
  "label": "Company Research",
  "kind": "enrichment",
  "dataType": "json",
  "origin": "ai",
  "autoRun": "never",
  "config": {
    "prompt": "Research this company and return JSON with icp_fit, summary, and risks.",
    "mode": "agent",
    "temperature": 0.7,
    "batchSize": 50,
    "promptSource": "manual",
    "structuredOutput": true,
    "useAutoSchema": true
  }
}
```

### 5) `add_to_crm`

`add-to-crm.json`:

```json
{
  "key": "add_to_leads",
  "label": "Add to Leads",
  "kind": "enrichment",
  "dataType": "json",
  "origin": "manual",
  "autoRun": "onSourceUpdate",
  "config": {
    "provider": "add_to_crm",
    "leadSource": "research_table_export",
    "fieldMappings": {
      "mode": "single",
      "linkedinUrl": "linkedin_url",
      "companyDomain": "domain",
      "firstName": "first_name",
      "lastName": "last_name",
      "title": "title",
      "companyName": "company",
      "emailAddresses": [
        { "column": "work_email", "type": "work" }
      ],
      "phoneNumbers": [
        { "column": "mobile_phone", "type": "mobile" }
      ]
    },
    "sourceColumns": [
      "linkedin_url",
      "domain",
      "first_name",
      "last_name",
      "title",
      "company",
      "work_email",
      "mobile_phone"
    ]
  }
}
```

Add-to-Leads note: mapped LinkedIn URL + company domain are required; run is non-billable.

## Common workflow

```bash
autotouch capabilities
autotouch tables create --name "CLI Contacts"
autotouch rows import-csv --table-id <TABLE_ID> --file contacts.csv
autotouch columns create --table-id <TABLE_ID> --data-file column.json
autotouch columns run --table-id <TABLE_ID> --column-id <COLUMN_ID> --scope filtered --filters-file filters.json --unprocessed-only --show-estimate --wait
autotouch jobs get --job-id <JOB_ID>
```

## Safe run patterns (`firstN` + `--unprocessed-only`)

Use this pattern to avoid paying twice for the same top rows.

```bash
# Pilot first 10 rows
autotouch columns run \
  --table-id <TABLE_ID> \
  --column-id <COLUMN_ID> \
  --scope firstN \
  --first-n 10 \
  --unprocessed-only \
  --show-estimate \
  --wait

# Extend to first 15 rows (processes the next 5 if first 10 are already done)
autotouch columns run \
  --table-id <TABLE_ID> \
  --column-id <COLUMN_ID> \
  --scope firstN \
  --first-n 15 \
  --unprocessed-only \
  --show-estimate \
  --wait
```

Notes:
- `firstN` without `--unprocessed-only` can re-run already-processed rows.
- `--wait` polls `/api/bulk-jobs/{job_id}` until terminal status.
- If a job stays `queued`, workers for that provider queue may be scaled to `0`.

## CSV import (agent-safe, async-first)

`rows import-csv` defaults to optimized import transport (`/import-optimized`) so large files do not fail on a single long request.

```bash
# Queue background import and return task_id quickly
autotouch rows import-csv \
  --table-id <TABLE_ID> \
  --file contacts.csv

# Wait for completion
autotouch rows import-status \
  --table-id <TABLE_ID> \
  --task-id <TASK_ID> \
  --wait
```

Notes:
- Use `--sync` only when you explicitly want synchronous behavior.
- Legacy direct path is still available with `--transport direct`.
- Optimized import emits progressive events while processing, and starts with a small first batch for fast initial row visibility.

## Capabilities for agents

Use capabilities as the source of truth before generating payloads:

```bash
autotouch capabilities
```

Agent expectations:

- `column_types` tells you which column types are runnable and which are non-billable transforms (`json_split`, `formatter_formula`).
- `filtering` describes valid scope/filter semantics for estimate/run.
- `automation.auto_run` describes supported auto-run modes + config field names.
- `execution_policies.llm.output_contract` describes output behavior by mode:
  - `agent` => JSON-oriented structured output
  - `basic` => text or JSON (`dataType=json` for structured JSON output)
- `webhooks.table_ingest` describes webhook ingest auth contract (metadata only; no secret tokens).

## JSON output pipeline pattern

For enrichment responses that return structured JSON, use this chain:

1. Run enrichment into a JSON column.
2. Split JSON into projection columns.
3. Optional formatter normalization.
4. Feed extracted/normalized keys into downstream enrichment columns.

Important mode distinction:

- Agent mode is JSON-oriented and is intended for structured outputs.
- Basic mode can return plain text or JSON depending on your column `dataType`/schema setup.

```bash
# Create projections from a JSON source column
autotouch columns projections \
  --table-id <TABLE_ID> \
  --data-file projections.json

# Optional: update downstream column config to reference projected keys
autotouch columns update \
  --table-id <TABLE_ID> \
  --column-id <DOWNSTREAM_COLUMN_ID> \
  --data-file column-update.json
```

`projections.json`:

```json
{
  "items": [
    {
      "key": "person_name",
      "label": "Person Name",
      "sourceColumnId": "<JSON_COLUMN_ID>",
      "path": "person_name",
      "dataType": "text"
    },
    {
      "key": "school_domain",
      "label": "School Domain",
      "sourceColumnId": "<JSON_COLUMN_ID>",
      "path": "school_domain",
      "dataType": "text"
    }
  ]
}
```

## Filtering (credit control)

Use `scope=filtered` to run only matching rows.

`filters.json`:

```json
{
  "mode": "and",
  "filters": [
    { "columnKey": "linkedin_url", "operator": "isNotEmpty" },
    { "columnKey": "country", "operator": "equals", "value": "United States" }
  ]
}
```

```bash
# Estimate first (non-billable)
autotouch columns estimate \
  --table-id <TABLE_ID> \
  --column-id <COLUMN_ID> \
  --scope filtered \
  --filters-file filters.json \
  --unprocessed-only

# Run same payload with rollout cap
autotouch columns run \
  --table-id <TABLE_ID> \
  --column-id <COLUMN_ID> \
  --scope filtered \
  --filters-file filters.json \
  --unprocessed-only \
  --first-n 200 \
  --show-estimate --wait
```

## Auto-run configuration

Auto-run is set on the column definition (`autoRun`) and can be changed later with `columns update`.

`column-update.json`:

```json
{
  "autoRun": "onInsert",
  "config": {
    "autoRunMode": "conditional",
    "autoRunFilters": {
      "mode": "and",
      "filters": [
        { "columnKey": "linkedin_url", "operator": "isNotEmpty" }
      ]
    }
  }
}
```

```bash
autotouch columns update \
  --table-id <TABLE_ID> \
  --column-id <COLUMN_ID> \
  --data-file column-update.json
```

## Table webhooks (ingest)

Webhook ingestion uses a per-table token, not developer API key scopes.

```bash
# Read current webhook config
autotouch webhooks get --table-id <TABLE_ID>

# Create/rotate token (token is returned once)
autotouch webhooks rotate --table-id <TABLE_ID>
```

`records.json`:

```json
{
  "records": [
    { "first_name": "Ada", "email": "ada@example.com" }
  ]
}
```

```bash
# Send records with webhook token
autotouch webhooks ingest \
  --table-id <TABLE_ID> \
  --records-file records.json \
  --webhook-token <WEBHOOK_TOKEN>
```

## Budget and safety controls

```bash
# Estimate only (no execution)
autotouch columns run --table-id <TABLE_ID> --column-id <COLUMN_ID> --scope filtered --filters-file filters.json --unprocessed-only --dry-run

# Guard against overspend
autotouch columns run --table-id <TABLE_ID> --column-id <COLUMN_ID> --scope filtered --filters-file filters.json --unprocessed-only --max-credits 50

# Poll until terminal status
autotouch jobs watch --job-id <JOB_ID>
```

## Notes

- The CLI is a thin wrapper around the same HTTP endpoints documented in `docs/research-table/reference/tables-api.md`.
- If a key is missing scope, CLI commands fail the same way raw API calls do (`403`).
- Use `--base-url` and `--token` per command for CI/ephemeral environments.
