Metadata-Version: 2.4
Name: autotouch-cli
Version: 0.2.21
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 Quickstart (`autotouch`)

This is the package-facing quickstart for agents and humans using the installable CLI.

## Install

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

## Bootstrap account + API key (optional, recommended for new agents)

If you already have a developer API key, skip this and go to Configure auth.

```bash
curl -X POST "https://app.autotouch.ai/api/auth/agent-bootstrap" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Ada",
    "last_name": "Lovelace",
    "email": "ada@yourcompany.com",
    "password": "use-a-strong-random-password",
    "organization_name": "Your Company",
    "key_name": "Agent bootstrap key"
  }'
```

Response includes:
- `apiKey` (returned once),
- `userData`,
- `key` metadata,
- `credits` snapshot.

Starter credits:
- New orgs created via signup/bootstrap start with `50` credits.

Identity linking:
- Human sign-in with the same normalized email lands in the same user/org account.

## Configure auth

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

Developer keys support the same scope model as raw HTTP API calls.
Workflow-related scopes now include:
- `sequences:read`
- `sequences:write`
- `sequences:run`
- `tasks:read`
- `tasks:write`

Reference:
- `docs/platform/authentication.md`

## Preflight (recommended before writes)

```bash
# 1) Verify target table exists and copy the exact id
autotouch tables list --view-mode org --output human

# 2) Validate server-side CSV parse/shape (no write)
autotouch rows import-csv \
  --table-id <TABLE_ID> \
  --confirm-table-id <TABLE_ID> \
  --file contacts.csv \
  --validate-only \
  --output json
```

Why this matters:
- Prevents wrong-table imports.
- Shows parsed row count and keys before spend/write.

## Import CSV (default: optimized async)

```bash
autotouch rows import-csv \
  --table-id <TABLE_ID> \
  --confirm-table-id <TABLE_ID> \
  --file contacts.csv \
  --checkpoint-file .autotouch-import.json \
  --wait \
  --output json
```

Notes:
- `--transport optimized` is default (`/import-optimized`).
- `--wait` polls import status to terminal state.
- `--checkpoint-file` stores state and blocks accidental duplicate re-imports.
- `--validate-only` validates CSV parse/shape on server without writing rows.
- Safety assertions:
  - `--expected-rows <N>`
  - `--require-columns col_a,col_b`
  - `--duplicate-key col_a,col_b`
  - `--require-non-empty key:ratio`
- For async imports with `--wait`, assertions are rechecked postflight via task manifest verification.
- Use `--allow-reimport` if you intentionally want to run the same file again.

## Import modes

1. `optimized` (default)
- Uses `/api/tables/{table_id}/import-optimized`.
- Best for larger files and resilient async handling.

2. `direct`
- Uses row + cell API loops.
- Useful for debugging/smaller imports.

```bash
autotouch rows import-csv \
  --table-id <TABLE_ID> \
  --confirm-table-id <TABLE_ID> \
  --file contacts.csv \
  --transport direct \
  --output json
```

## Auto-run trigger contract

Auto-run is configured per column (`autoRun`), not per table.

- `never`: no automatic runs.
- `onInsert`: runs on newly inserted rows (for example CSV import/webhook ingest).
- `onSourceUpdate`: runs only when source values are updated (for example cell patch/update).

Important:
- Insert events do not run `onSourceUpdate` columns.
- Imports may queue auto-run dispatch evaluation, but only columns whose `autoRun` policy matches the event are queued.

## Create a column

Recipe-first flow (recommended):

```bash
autotouch columns recipe --type add_to_crm --output human
autotouch columns recipe --type sync_to_table --output human
autotouch columns recipe --type add_to_sequence --output human
```

Then create from the generated payload:

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

Notes:
- `add_to_crm` is optional and non-billable.
- Email/phone enrichment does not require creating/running `add_to_crm`.
- For `add_to_crm`, required mapping keys are `linkedinUrl` and `companyDomain`.
- `sync_to_table` supports both:
  - single destination: `destinationTableId` + `columnMappings`
  - router mode: `routes[]` (first matching route wins)
- `add_to_sequence` requires:
  - `sequenceId`
  - `sourceLeadColumn` pointing to a lead-id producing column (`add_to_crm` or `lead_finder` output)
  - auto-attaches research context defaults during enrollment (`source_table_id`, plus optional table name); favorite fields resolve from current starred columns when explicit `fieldIds` are not provided

If you create custom `llm_enrichment` schemas:
- Use strict field-map schema shape (no root `type/properties` wrapper).
- Arrays must be explicit `{"type":"array","items":...}`.
- Avoid legacy/list schema values like `["string"]` or `[{...}]`.
- Prefer snake_case schema keys in payloads: `response_schema`, `user_schema`, `use_auto_schema`.

## Parse enrichment outputs safely (important for agents)

Use a layered rule:

1. Always inspect raw outputs first (sample at least 3 rows).
2. Parse by column `dataType`:
   - `json`: use key precedence below.
   - scalar types (`text`, `number`, `date`, `boolean`, `email`, `url`): parse direct value (no JSON key paths).
3. Only then publish found/missing counts.

Do not rely on a single key for JSON outputs.

- Phone extraction precedence: `mobile_number` -> `phone_numbers[0].number` -> `primary_phone`
- Email extraction precedence: `response` -> `email` -> `work_email`

If path A is missing, continue to B/C before declaring `not_found`.

JSON split note:
- Optional by default.
- Use only when downstream filters/mappings need stable flat keys.

Reference:
- `docs/research-table/guides/context-first-sequence-playbook.md`
- `docs/research-table/reference/runbooks/context-first-sequence.json`

## Recommended ICP buyer discovery pattern

Use the dedicated guide:

- `docs/research-table/guides/icp-buyer-discovery.md`

This keeps quickstart short while documenting the full strategy:

- when to use `lead_finder` vs agent research,
- responsibility-based targeting (not exact title matching),
- one-person-per-row output contract,
- JSON split + downstream enrichment chaining.

## First-principles shape: intent + context

For most workflows, useful output combines:
- context: source evidence (what happened / what was observed)
- intent: interpretation signal (what to prioritize / do next)

Design guidance:
- preserve full-fidelity context in at least one raw field (for example full post/body text)
- default behavior for agents: write full context/content, not summaries or truncation
- only truncate/summarize when there is a hard limit (storage/model/provider/payload), and explicitly mark truncation
- keep intent separate from raw context so downstream logic can change without losing source data
- keep one entity per row and dedupe on a stable identity key
- keep snippets/summaries optional and derived from raw context, never a replacement for it
- normalize intent labels if you need deterministic automation

Optional field pattern (adapt to your workflow):
- `post_content` or `context_raw`: full long-form context
- `context_url`: source URL
- `context_timestamp`: recency marker
- `intent_label`: normalized intent category
- `intent_reason`: concise rationale

CSV import note for context fields:
- long text fields like `post_content` can contain newlines and commas
- that is valid CSV when fields are properly quoted
- always run `rows import-csv --validate-only` (or `--dry-run`) before write
- for strict guardrails, add assertions: `--expected-rows`, `--require-columns`, `--duplicate-key`, `--require-non-empty`
- import does not intentionally truncate text values; very large single-cell payloads are still bounded by Mongo document limits

Verification + rollback commands:
- `autotouch rows import-verify --table-id <TABLE_ID> --task-id <TASK_ID> --expected-rows <N>`
- `autotouch rows import-rollback --table-id <TABLE_ID> --task-id <TASK_ID>`

Multiline `post_content` examples:

```csv
# bad (unquoted multiline content breaks row shape)
first_name,linkedin_url,post_content
Ada,https://linkedin.com/in/ada,Line 1
Line 2
```

```csv
# good (quoted multiline content is valid CSV)
first_name,linkedin_url,post_content
Ada,https://linkedin.com/in/ada,"Line 1
Line 2"
```

Do not re-import blind (recovery flow):
- stop and keep the original `task_id`
- inspect status: `autotouch rows import-status --table-id <TABLE_ID> --task-id <TASK_ID>`
- prove postflight: `autotouch rows import-verify --table-id <TABLE_ID> --task-id <TASK_ID> ...assertions...`
- if verification fails, preview rollback: `autotouch rows import-rollback --table-id <TABLE_ID> --task-id <TASK_ID> --dry-run`
- then rollback: `autotouch rows import-rollback --table-id <TABLE_ID> --task-id <TASK_ID>`
- fix CSV quoting/mapping and run `--validate-only` before any new import

## Sequences/Tasks workflow APIs (current CLI coverage)

The installable `autotouch` CLI currently provides first-class commands for research-table APIs.

Sequence/task workflow endpoints are available today via HTTP with the same developer key:
- `docs/platform/external-workflows-api.md`

Examples include:
- `POST /api/sequences`
- `PATCH /api/sequences/{sequence_id}/status`
- `POST /api/sequences/{sequence_id}/enroll`
- `POST /api/task-queue/create`
- `POST /api/task-queue/{task_id}/draft`

Important:
- These workflow routes support `actorUserId` query parameter.
- External sequence validation rules (manual vs automated vs AI draft constraints) are documented in the workflow reference above.

### Sequence step types and rules (quick reference)

Supported step kinds:
- `EMAIL`
- `CALL`
- `LINKEDIN`
- `LINKEDIN_CONNECT`
- `LINKEDIN_MESSAGE`
- `CUSTOM`

Email send modes:
- `AUTOMATED`
- `MANUAL`

External API validation rules:
- First email step cannot be reply (`first_email_reply_not_allowed`).
- Bulk mode cannot include manual email steps (`bulk_manual_email_not_allowed`).
- `aiDraft` is only allowed on manual-capable steps:
  - `CALL`, `CUSTOM`, `LINKEDIN`, `LINKEDIN_CONNECT`, `LINKEDIN_MESSAGE`
  - `EMAIL` only when `emailSendMode=MANUAL`
- `defaultAppendSignature` (sequence-level, optional) sets default signature behavior for email steps.
- `steps[].appendSignature` (step-level, optional) overrides signature behavior per email step.

Runtime behavior:
- `EMAIL + AUTOMATED` is sent by scheduler/provider flow.
- Manual/non-automated steps are created as Tasks for rep execution.

Minimal mixed-step example:

```json
{
  "name": "Outbound v1",
  "sourceTableId": "tbl_123",
  "defaultAppendSignature": true,
  "steps": [
    {
      "id": "s1",
      "kind": "EMAIL",
      "emailSendMode": "AUTOMATED",
      "emailIsReply": false,
      "appendSignature": true,
      "subjectTemplate": "Quick intro",
      "bodyTemplate": "Hi {{first_name}}, ..."
    },
    {
      "id": "s2",
      "kind": "CALL",
      "waitDays": 2,
      "aiDraft": true
    },
    {
      "id": "s3",
      "kind": "EMAIL",
      "waitDays": 4,
      "emailSendMode": "MANUAL",
      "emailIsReply": false,
      "appendSignature": false,
      "aiDraft": true
    }
  ]
}
```

Canonical contract:
- `docs/platform/external-workflows-api.md`

## Outbound webhook subscriptions

Use webhook subscriptions to receive customer-facing event notifications.

```bash
autotouch webhooks subscriptions list

autotouch webhooks subscriptions create \
  --url https://example.com/webhooks/smart-table \
  --events bulk_job.* lead.status_changed sequence_enrollment.created task.created

autotouch webhooks subscriptions test \
  --subscription-id <SUBSCRIPTION_ID> \
  --event-type lead.created \
  --data-json '{"ping":"ok"}'
autotouch webhooks deliveries list --subscription-id <SUBSCRIPTION_ID>
autotouch webhooks deliveries attempts --delivery-id <DELIVERY_ID>
```

Developer key scopes:
- `webhooks:read`
- `webhooks:write`

Retention note:
- Webhook event cache and delivery-attempt logs default to 7 days
  (`WEBHOOK_EVENTS_TTL_DAYS`, `WEBHOOK_DELIVERY_ATTEMPTS_TTL_DAYS`).

## Create basic/manual fields (text, number, date, etc.)

Use `kind=manual` for non-enrichment columns.

Supported `dataType` values:
- `text`
- `number`
- `date`
- `boolean`
- `email`
- `url`
- `json`

`manual-column.json`:

```json
{
  "key": "employee_count",
  "label": "Employee Count",
  "kind": "manual",
  "dataType": "number",
  "origin": "api",
  "autoRun": "never"
}
```

```bash
autotouch columns create \
  --table-id <TABLE_ID> \
  --data-file manual-column.json \
  --output json
```

To create rows with values in one step:

`rows.json`:

```json
{
  "records": [
    { "company": "Acme", "employee_count": 125, "founded_on": "2021-05-01" }
  ]
}
```

```bash
autotouch rows add \
  --table-id <TABLE_ID> \
  --records-file rows.json \
  --detect-types \
  --output json
```

To update existing rows/cells:

`updates.json`:

```json
{
  "updates": [
    { "rowId": "<ROW_ID>", "key": "employee_count", "value": 130 },
    { "rowId": "<ROW_ID>", "key": "founded_on", "value": "2021-05-01" }
  ]
}
```

```bash
autotouch cells patch \
  --table-id <TABLE_ID> \
  --updates-file updates.json \
  --detect-types \
  --output json
```

Note:
- If `updates[].key` does not exist, patch can auto-create a manual column (with `--detect-types`).

## Run exactly next N rows (recommended for agents)

```bash
autotouch columns run-next \
  --table-id <TABLE_ID> \
  --column-id <COLUMN_ID> \
  --count 5 \
  --show-estimate \
  --wait \
  --output json
```

Why `run-next`:
- Deterministic exact-count batching.
- Avoids ambiguous `firstN` increments in agent loops.

For `add_to_crm`, prefer bounded runs first:

```bash
autotouch columns run-next \
  --table-id <TABLE_ID> \
  --column-id <ADD_TO_CRM_COLUMN_ID> \
  --count 25 \
  --show-estimate \
  --wait
```

## Credit-safe filtering pattern

Filter out rows that are missing required upstream fields before billable runs.

`filters.json`:

```json
{
  "mode": "and",
  "filters": [
    { "columnKey": "linkedin_url", "operator": "isNotEmpty" },
    { "columnKey": "work_email_address", "operator": "isEmpty" }
  ]
}
```

Run:

```bash
autotouch columns run-next \
  --table-id <TABLE_ID> \
  --column-id <COLUMN_ID> \
  --count 5 \
  --filters-file filters.json \
  --show-estimate \
  --wait
```

## Job truth contract (agent-safe)

- Treat a run as started only when you receive `job_id`.
- Treat a run as done only when `jobs get/watch` returns terminal status.
- `columns run --wait` now emits structured progress events and includes:
  - `job_id`
  - `job_status_url`
  - `watch_command`

```bash
autotouch jobs get --job-id <JOB_ID> --output json
autotouch jobs watch --job-id <JOB_ID> --interval 2 --output json
# Recover latest job id if local output lost it:
autotouch jobs list --table-id <TABLE_ID> --column-id <COLUMN_ID> --limit 1 --output json
```

Terminal statuses:
- `completed`
- `partial`
- `error`
- `cancelled`

Non-terminal statuses:
- `queued`
- `distributing`
- `processing`

Recommended status fields to inspect while waiting:
- `processed_rows`
- `error_rows`
- `skipped_rows`
- `total_rows`
- `pending_batches`
- `terminal_reason`

CLI-protected failure statuses:
- `not_found`
- `unknown_not_found`

If either failure status appears, treat the run as unconfirmed/inconsistent, verify row state, and rerun.

## Troubleshooting

1. Import appears to run but rows are missing:
- Verify `table_id` in output matches intended target.
- Use `--confirm-table-id` on all mutating CSV imports.

2. Duplicate imports:
- Use `--checkpoint-file`.
- Do not pass `--allow-reimport` unless intentional.

3. Async import interrupted:
- Re-run with same `--checkpoint-file` and `--wait`.
- CLI can resume/poll an in-flight task from checkpoint state.

4. Need proof before saying "fixed":
- Run `autotouch rows import-verify --table-id <TABLE_ID> --task-id <TASK_ID> ...assertions...`
- Treat `passed=false` as a failed import outcome.

5. Run queued but job id missing from local output:
- Recover from backend history:
  - `autotouch jobs list --table-id <TABLE_ID> --column-id <COLUMN_ID> --limit 5 --output json`
- Then poll the returned `job_id` with `autotouch jobs get` / `autotouch jobs watch`.

6. Need to stop a running column job:

```bash
autotouch columns stop --table-id <TABLE_ID> --column-id <COLUMN_ID>
```

---

Full reference (all commands/payload recipes):
`docs/research-table/reference/autotouch-cli.md`
