Metadata-Version: 2.4
Name: autotouch-cli
Version: 0.2.123
Summary: Autotouch Smart Table CLI
Project-URL: Homepage, https://app.autotouch.ai
Project-URL: Documentation, https://github.com/nicolonic/autotouch_main/tree/main/docs/research-table/reference
Project-URL: Source, https://github.com/nicolonic/autotouch_main
Keywords: autotouch,smart-table,cli,automation,api
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.31.0
Requires-Dist: python-dotenv>=1.0.0
Provides-Extra: mongo
Requires-Dist: pymongo>=4.7; extra == "mongo"

# Autotouch CLI

This is the package README published with `autotouch-cli`. For the full
application repository map, see the repository-level `README.md`.

Installable CLI for the Smart Table developer API.

Use it when you want command-driven access to search, tables, columns, runs, jobs, leads, sequences, and task workflows without hand-writing raw HTTP requests.

The CLI talks to the same API the product uses. For automation, prefer `--output json`.

For a machine-readable local command contract, use `autotouch cli-manifest --output json`.
For a shipped human-readable reference generated from the installed parser, use `autotouch cli-reference`.

## Product Model

Autotouch helps GTM teams source records, land them in Smart Table, research and operate on those rows, then turn qualified accounts/leads into outreach actions.

Use the lifecycle to choose commands:

1. Source Records: use `autotouch list-build`, `autotouch search`, imports, or webhooks.
2. Land In Smart Table: use `autotouch list-build sync-to-table`, `autotouch tables`, `autotouch rows`, or import/webhook ingest.
3. Research And Operate: use `autotouch columns recipe`, `autotouch columns run-next`, `autotouch columns run`, `autotouch rows`, and `autotouch cells`; use `autotouch jobs` only for native executor diagnostics or domains that still own bulk jobs.
4. Engage: use `to_leads`, `to_sequence`, `to_outreach`, `to_crm`, `autotouch sequences`, and `autotouch tasks`.

Smart Table is the operating layer. Most workflows should land records there before enrichment, dedupe, routing, or engagement. Run `autotouch workflows list --output json` for lifecycle-grouped plans.

## Codex-Style Agent Use

The CLI is designed to be used like a Codex tool surface: inspect the local contract, plan from explicit state, run one bounded primitive, observe authoritative backend state, then continue from the observed IDs.

Recommended loop:

1. Discover / inspect: `autotouch auth check --output json`, `autotouch capabilities --output json`, `autotouch capabilities operations list --cli-available`, `autotouch cli-manifest --output json`, and the relevant `recipe` / `schema` command.
2. Plan: choose a lifecycle workflow from `autotouch workflows list --output json`; prefer Smart Table as the operating layer.
3. Preview/scaffold: write payloads to files with `recipe` / `schema` / `workflows scaffold` before mutation-heavy runs.
4. Execute a bounded slice: use `--scope firstN`, `--first-n`, `--unprocessed-only`, `--wait`, or small list-build targets when validating.
5. Observe: for Research Table runs, treat the `operation_run` receipt returned by `columns run --wait` as the lifecycle source of truth; use `rows get` and `cells get` for persisted results. Use `autotouch jobs get/watch` only for native executor diagnostics or domains that still own bulk jobs. Do not infer success from terminal text alone.
6. Continue: pass explicit `TABLE_ID`, `ROW_ID`, `COLUMN_ID`, `OPERATION_RUN_ID`, `JOB_ID`, `LEAD_ID`, and `SEQUENCE_ID` values into the next command instead of relying on hidden session state.

For the detailed agent operating model, use `docs/research-table/guides/autotouch-cli-agent-playbook.md`. For maintainer rules, use `docs/development/autotouch-cli-architecture.md`. The local Codex reference checkout at `/private/tmp/openai-codex` may be used as inspiration for operating-model patterns only; never make that temporary repository a package dependency or source of truth.

Discovery is a phase, not a separate top-level command. Use the existing inspection commands:

| Need | Command |
| --- | --- |
| Product operations and whether CLI can run them | `autotouch capabilities operations list --cli-available` |
| Exact operation detail and CLI lifecycle steps | `autotouch capabilities operations get <operation_id>` |
| Goal-level workflow choices | `autotouch workflows list --output json` and `autotouch workflows plan --type <TYPE>` |
| Current command grammar | `autotouch cli-manifest --output json` or `autotouch cli-reference` |
| Connected sources and destinations | `autotouch integrations list` and `autotouch integrations status --provider <provider>` |
| List-build filter IDs and pricing | `autotouch list-build inputs` and `autotouch list-build pricing` |
| Scheduled Chat definitions and occurrences | `autotouch scheduled list`, `autotouch scheduled get <SCHEDULE_ID>`, and `autotouch scheduled runs <SCHEDULE_ID>` (user session) |

## Install

Released package from the configured package index:

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

# upgrade
pipx upgrade autotouch-cli
# or
python -m pip install --upgrade autotouch-cli
```

Current repo branch or pre-publish build:

```bash
pipx install --force "git+ssh://git@github.com/nicolonic/autotouch_main.git@<branch-or-sha>"
# or
python -m pip install -U "git+ssh://git@github.com/nicolonic/autotouch_main.git@<branch-or-sha>"
```

The `autotouch-cli` package name installs only the latest published artifact.
Use the git/source install form when testing a version bump, docs/help changes,
or new command contracts before the release tag has published.

Verify the installed binary and bundled docs:

```bash
autotouch --version
autotouch columns update --help
autotouch cli-reference --source bundled | grep -n "valueSchema"
```

If `pipx upgrade autotouch-cli` or `python -m pip install -U autotouch-cli`
reports success but `which autotouch` still points at an older shim, fix `PATH`
or run the upgraded binary directly before using production commands.

## First Run

Existing developer key:

```bash
read -s AUTOTOUCH_API_KEY
export AUTOTOUCH_API_KEY
autotouch setup --api-key "$AUTOTOUCH_API_KEY" --base-url https://app.autotouch.ai
```

Equivalent manual steps:

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

Fresh account:

```bash
export AUTOTOUCH_CONFIG_PATH=/tmp/autotouch-audit.json
read -s AUTOTOUCH_BOOTSTRAP_PASSWORD
export AUTOTOUCH_BOOTSTRAP_PASSWORD

autotouch auth bootstrap \
  --first-name Ada \
  --last-name Lovelace \
  --email ada+audit@example.com \
  --password "$AUTOTOUCH_BOOTSTRAP_PASSWORD" \
  --organization-name "Audit Org" \
  --save-key

autotouch auth check
```

Demo accounts:

- For a brand-new demo org plus developer key, use `autotouch auth bootstrap` as shown above.
- For a demo login inside an existing seeded demo org, create the org with `apps/api/scripts/create_demo_org.py`, then add the user through the invite/register flow in `docs/operations/demo-accounts.md`.

Config precedence:
- Explicit flags win over environment variables.
- Environment variables win over saved config.
- Saved config wins over built-in defaults.
- For JSON payload inputs, pass either `--data-json` or `--data-file`, not both.

## 5-Minute Flow

```bash
# 0) First-run setup
autotouch setup --api-key "$AUTOTOUCH_API_KEY" --base-url https://app.autotouch.ai

# 1) Inspect machine-readable contract
autotouch capabilities --output json

# 2) Create a table and capture its id
TABLE_ID=$(autotouch tables create --name "CLI Contacts" --output json --select id)

# 3) Add a couple rows with the default lead_finder recipe's expected field
ROW_ID=$(autotouch rows add \
  --table-id "$TABLE_ID" \
  --records-json '[{"domain":"openai.com","companyName":"OpenAI"},{"domain":"stripe.com","companyName":"Stripe"}]' \
  --output json --select rowIds.0)

# 4) Generate a provider-backed column payload
autotouch columns recipe --type lead_finder --out-file column.json

# 5) Create the column and capture its id
COLUMN_ID=$(autotouch columns create --table-id "$TABLE_ID" --data-file column.json --output json --select id)

# 6) Run a small controlled slice and observe the canonical operation receipt
autotouch columns run-next \
  --table-id "$TABLE_ID" \
  --column-id "$COLUMN_ID" \
  --count 2 \
  --show-estimate \
  --wait \
  --output json

# 7) Inspect exactly what changed
autotouch rows get --table-id "$TABLE_ID" --row-id "$ROW_ID" --output json
```

## Cheat Sheet

- Create table: `autotouch tables create`
- Add rows: `autotouch rows add`
- Inspect rows: `autotouch rows list`, `autotouch rows get`
- Inspect one cell: `autotouch cells get`
- Create a workflow column: `autotouch columns recipe`, `autotouch columns create`
- Run Exa Company Search for basic company discovery, competitors, and lookalikes: `autotouch search companies`, `autotouch search similar-companies`
- Inspect list-build inputs: `autotouch list-build inputs`
- Build durable company, lead, and job-posting lists on Smart Table workers: `autotouch list-build companies`, `autotouch list-build leads`, `autotouch list-build jobs`
- Check connected LinkedIn account status/limits: `autotouch linkedin status`, `autotouch linkedin limits`
- Run controlled slices: `autotouch columns run-next`
- Poll Research Table state: `autotouch columns run --wait` (canonical `operation_run` receipt); use `autotouch jobs get/watch` only for native executor diagnostics.
- Create/activate sequences: `autotouch sequences recipe`, `autotouch sequences create`, `autotouch sequences activate`
- Choose Instantly/Smartlead sender pools for bulk sequences: `autotouch sequences sync-accounts`
- Query leads: `autotouch leads query`
- Find a lead by email or phone: `autotouch leads query --search '<email-or-phone>' --limit 10`
- Create table webhook ingest tokens: `autotouch webhooks rotate`, `autotouch webhooks configure-ingest`, `autotouch webhooks ingest`

## Sequence Automation Choices

When a user asks to create a campaign sequence, distinguish these separate decisions:

- Manual AI-draft review: `autotouch sequences recipe --type manual_review` (or `create`) sets `executionMode=MANUAL` and `aiDraft=true`; AI drafts content and a human approves/completes each task.
- Template-based automated send: `autotouch sequences recipe --type automated_template` (or `bulk_automated`) sets `executionMode=AUTOMATED` and `emailSendMode=AUTOMATED` with fixed templates; use this when the user explicitly asks to automate sending without review.
- LinkedIn-assisted outreach: `autotouch sequences recipe --type linkedin_outreach` uses an automated profile visit followed by manual AI-draft LinkedIn connection, connected-only LinkedIn chat, and fallback email tasks. Use it only when LinkedIn touches are part of the campaign. Use `LINKEDIN_MESSAGE` for best-available LinkedIn delivery on paths that are not guaranteed to be connected.
- Research-field routing: `autotouch sequences recipe --type research_field_routing` starts one source-table sequence that routes each enrollment by a research-table column value, such as persona, segment, territory, product line, or campaign path.

If the user says "automate the sequence" without asking for AI-written sends, choose the template-based automated path. Offer manual AI-draft review as the higher-personalization path, but call out that it creates upfront manual tasks while the system learns.

Do not mix `aiDraft=true` with non-empty `subjectTemplate`, `bodyTemplate`, or `scriptTemplate`. Template fields send exact variable-filled copy when `aiDraft=false`; AI-draft steps should leave template fields empty and use sequence/source-table context. The CLI and developer-key API reject the mixed shape.

For bulk Instantly/Smartlead sequences, email sender accounts live on the provider campaign, not on each enrollment. Use `autotouch sequences sync-accounts --sequence-id <SEQUENCE_ID> --account-selection all_eligible` for the org's eligible provider-account group, or `--account-selection selected --selected-account sender@example.com` for specific provider mailboxes. For personal mailbox or LinkedIn steps, use enrollment sender routing only when the user asks for explicit sender pinning or team routing.

Keep `requiredVariables` limited to variables that the sequence actually needs. If a variable is listed there, every provider enrollment must hydrate it from lead context, research-table mappings, or explicit overrides; unused required variables can block an otherwise valid bulk campaign.

Version guard:

- Use `autotouch --version` before relying on recipe defaults. This repo expects `autotouch-cli` `0.2.121` or newer.
- If `python3 -m pip install -U autotouch-cli` upgrades the package but `which autotouch` still points at an older shim, run the upgraded binary directly or fix PATH before creating production sequences.
- Before authoring a LinkedIn sequence, inspect the live recipe:

```bash
autotouch sequences recipe --type linkedin_outreach --output json --compact
```

As of `0.2.115`, that recipe emits:

1. `LINKEDIN_VISIT_PROFILE` - `AUTOMATED`
2. `LINKEDIN_CONNECT` - `MANUAL`, `aiDraft=true`, empty `scriptTemplate`, after 7 minutes
3. outcome wait for `linkedin_connection_accepted` - up to 7 days
4. connected branch -> `LINKEDIN_CHAT_MESSAGE` - `MANUAL`, `aiDraft=true`; chat-only after connection acceptance
5. not-connected branch -> `EMAIL` - `MANUAL`, `emailSendMode=MANUAL`, `aiDraft=true`

For one sequence with multiple paths based on a source-table value, inspect the route recipe:

```bash
autotouch sequences recipe --type research_field_routing --output json --compact
```

Use one `source_field_route` condition with `columnKey`, one route case per expected value, and a default route for blank or unexpected values. Extend the route cases when the user needs more paths. Create separate sequences only when the paths require separate reporting, sender pools, schedules, ownership, or activation controls.

## Neural Search vs Durable List Build

Autotouch has two different company/people discovery paths.

Use `autotouch search companies` for Exa Company Search when the target is basic company discovery, competitors, lookalikes, or a semantic company search. Put company constraints directly in natural language: industry, geography, funding, headcount, technology, founded date, and similarity. Examples: "AI workflow platforms for healthcare", "US education software startups focused on AI math tutoring with 1-50 employees", or "companies like Rippling for HR and workforce management". Exa company search returns up to 100 results per API call and costs 1 credit per 10 returned results.

Use `autotouch search similar-companies` as a convenience wrapper for Exa Company Search similarity. `--query` is required and is sent unchanged; use Exa's documented semantic patterns, such as "companies like Notion", "competitors of Stripe", or "companies working on making space travel cheaper". Optional `--company-name` and `--company-domain` identify the seed only so it can be excluded from results; they never compose or rewrite the query. This command does not use Exa `/findSimilar`.

Use `autotouch search people` for provider-hidden people search when the target is niche, semantic, or hard to express with structured filters. Send `--person-description` / `person_description` as a responsibility, for example "person responsible for endpoint management and SaaS app access", and use only company, domain, or location as optional public-search anchors. The CLI still accepts `--query` as a deprecated alias. Legacy `lead_description`, title, skills, and plural criteria are rejected by the backend people-search contract so the provider does not guess from ambiguous fields. People search currently returns at most 10 results per API call and costs 1 credit per API call.

Use native company and people search for structured durable lists. Inspect `autotouch capabilities operations get lists.build_companies` or `lists.build_people` for the full filter contract. CLI 0.2.122 adds native defaults, JSON filters and saved-list continuation. Exact help comes from the installed CLI; capability metadata comes from the selected API server.

```bash
autotouch list-build companies --country-code US --filters-json '{"max_employees":24}' --num-results 20 --table-name "Small companies" --wait
autotouch list-build leads --title "SDR Manager" --title "Sales Development Manager" --city "New York" --country-code US --num-results 20 --table-name "SDR managers" --wait
autotouch list-build leads --from-job-id lb_prior --num-results 20 --wait
```

`--filters-json` exposes the full source filter vocabulary; `--data-file` accepts the complete API request. Person geography belongs in `people_filters`; employer constraints belong in `company_filters`. No user-owned LinkedIn connection is needed. Read `list-build pricing` for current costs. For more of the same list, resume the completed delivered job with `--from-job-id`; the backend restores its filters, cursor and table. Changed criteria require a fresh search. Broader candidates should remain separate until qualified.

Use `autotouch scheduled ...` for recurring Command Center work. Scheduled Chat
creates a fresh task for each occurrence and uses the owner's user session,
resource references, and reviewed standing authority. Run `autotouch auth login`
first; these commands do not accept developer API keys. Create a draft from the
HTTP request body, review its returned version and authority, then activate it:

```bash
autotouch scheduled create --data-file schedule.json
autotouch scheduled get <SCHEDULE_ID>
autotouch scheduled activate <SCHEDULE_ID> --expected-version <VERSION>
autotouch scheduled runs <SCHEDULE_ID>
```

The draft body contains `name`, `prompt`, and `recurrence: {rrule, timezone}`;
optional `resourceRefs` identify exact host resources. Updating, activating,
pausing, archiving, and running now use the current `expectedVersion`. Run now
also requires a stable `idempotencyKey` of 8-200 characters. An occurrence's
`operationRunId` and eventual `threadId` identify its durable execution.

The bounded search adapters include `search catalog-companies`, `search
people-basic`, `search linkedin-posts`, `search linkedin-post-engagers`,
`search role-changes`, and `search board-jobs`. Use their help for limits and `--data-file` for their
exact HTTP request bodies. These return search artifacts and do not create
durable list-build jobs.

`autotouch organization users --query <TEXT> --limit 20 --offset 0` reads the
current organization user catalog (`users:read`). `autotouch sequences
table-readiness --sequence-id <ID> --source-lead-column <KEY>` reads the linked
table's canonical Sync to Leads results without enrolling anyone; developer
keys need `sequences:read`, `tables:read`, and `leads:read`.

`autotouch calendar` is available only when the target deployment enables
Calendar. Reads require `calendar:read`, writes require `calendar:write`, and
the backend checks the caller's connected account and event access. Agenda
reads accept explicit timezone-aware `--start`/`--end` ranges and pagination;
`get-agenda-event` reads an exact provider event. Event mutation commands act
on the Lead-associated event contract. `send-lead-invite` requires explicit CRM
Lead IDs and a caller-supplied UUID `request_id` in its body.

Exact company actions use `companies promote`, `companies
refresh-firmographics`, and `companies refresh-firmographics-status`, all with a
user session. The refresh returns a canonical `operation_run_id`; the status
command accepts the returned native `job_id` for provider result inspection.

Use `tables enrich-and-score-preflight --table-id <ID> --data-file plan.json`
to obtain the server's reviewed plan. `tables enrich-and-score` accepts that
plan's inputs, returned `planId`/`planVersion`, and a stable `idempotencyKey`.
Use `tables enrich-and-score-retry` with the exact terminal parent operation
ID to retry only unfinished rows. These commands print durable admission
receipts; inspect the canonical operation run to establish completion.

## Workflow Plans

Use workflow plans for user goals, and recipes for individual payloads. The clean mental model is:

```text
discover -> plan -> scaffold -> run primitives -> inspect
```

- `autotouch integrations list` discovers connected sources and destinations.
- `autotouch workflows plan --type <TYPE>` explains the recommended path for a goal.
- `autotouch workflows scaffold --type <TYPE> --out-dir <DIR>` writes payload files and a command runbook.
- `autotouch columns recipe` and `autotouch sequences recipe` remain leaf-level templates.

Goal-level workflow types include `bulk_outreach`, `company_rows_to_people_table`, `table_to_outreach`, `research_to_sequence`, `sequence_launch`, `table_to_routed_sequence`, `account_research`, `signal_outreach`, `website_visitors`, `list_build_to_table`, `company_discovery_to_contacts`, `external_api_to_table`, and `system_to_table`. These keep CRM, outreach providers, Autotouch Leads, Autotouch Sequences, webhooks, visitor sources, external intent sources, and existing Autotouch workspace records as sources/destinations inside one organized workflow layer instead of creating one-off top-level commands.

## Sources And Destinations

Use `autotouch integrations list` before wiring data movement. It returns connected sources and destinations with the directional verbs agents should use:

- `from_crm`: pull HubSpot or Attio records into a research table. Start with `autotouch integrations recipe --type from_crm --provider hubspot`.
- `to_crm`: push research-table rows to an external CRM such as HubSpot or Attio. Start with `autotouch columns recipe --type to_crm`.
- `to_outreach`: push research-table rows to an existing Instantly or Smartlead campaign. Start with `autotouch columns recipe --type to_outreach`.
- `to_leads`: sync research-table rows into Autotouch Leads before sequencing. Start with `autotouch columns recipe --type to_leads`.
- `to_sequence`: enroll Autotouch Leads into an Autotouch Sequence and assign generated tasks. Start with `autotouch columns recipe --type to_sequence`.

Use the direction in the user's wording. "Pull from HubSpot" means `from_crm`.
"Push to HubSpot" means `to_crm`. For a request involving Autotouch Leads and
Sequences, inspect the current table/resource state and choose the typed
operation or recipe that matches the missing effect; the CLI does not impose a
fixed operation order.

For sequence handoffs, keep the common path simple: set `assignedToUserId` and omit sender routing fields. Autotouch uses that user's default connected email and LinkedIn sender accounts. Use `autotouch sender-accounts list` only when the user wants explicit sender pinning, round-robin/team routing, or wants to manage sender accounts on behalf of users. Sender-account default/delegation changes are owner/admin user-session operations.

## Table Webhook Ingest

Use table webhooks when an external source should push records directly into a Smart Table.

```bash
WEBHOOK_TOKEN=$(autotouch webhooks rotate --table-id "$TABLE_ID" --output json --select token)

autotouch webhooks configure-ingest \
  --table-id "$TABLE_ID" \
  --match-rule-id linkedin_url \
  --match-strategy linkedin_url \
  --duplicate-mode upsert

autotouch webhooks ingest \
  --table-id "$TABLE_ID" \
  --webhook-token "$WEBHOOK_TOKEN" \
  --records-json '{"records":[{"first_name":"Jane","last_name":"Doe","linkedin_url":"https://www.linkedin.com/in/jane-doe"}]}'
```

For external providers, prefer the `X-Autotouch-Token: <WEBHOOK_TOKEN>` header. If the provider cannot send custom headers and only accepts one webhook URL, use the compatibility URL:

```text
https://app.autotouch.ai/api/webhooks/tables/<TABLE_ID>/ingest?token=<WEBHOOK_TOKEN>
```

Use query-token URLs only for no-header providers because URLs may appear in provider logs or history. Rotate the table token if it is exposed.

Webhook ingest stores the raw provider object in the `Webhook Payload` JSON column. For website visitor and external intent provider workflows, use `autotouch workflows plan --type website_visitors` first. The recommended shape is usually two starter tables: one for company-level events and one for person-level events. After a sample payload lands, use `autotouch columns projections` to split provider-specific JSON into stable columns such as `company_domain`, `linkedin_url`, `email`, `page_url`, `event_type`, and `last_seen_at`, then continue with enrichment, email finding, and the chosen destination.

CRM round trip:

```bash
# 1. Confirm the org has the right source/destination connections.
autotouch integrations list --output json
autotouch integrations status --provider hubspot

# 2. Pull CRM records into a research table. This prints the API path/body to use;
# it does not execute the import by itself.
autotouch integrations recipe --type from_crm --provider hubspot --output json

# 3. Add enrichment columns to the research table.
autotouch columns recipe --type llm_enrichment --out-file enrich.json
autotouch columns recipe --type email_finder --out-file email-finder.json

# 4. Push enriched rows back to the CRM, then run the created column.
autotouch columns recipe --type to_crm --out-file push-to-crm.json
autotouch columns create --table-id <TABLE_ID> --data-file push-to-crm.json
autotouch columns run --table-id <TABLE_ID> --column-id <COLUMN_ID> --scope filtered --wait
```

## More

For automation or agent-driven setup, use:
- `autotouch cli-manifest --output json` for the local machine-readable command contract
- `autotouch cli-reference` for the shipped parser-generated reference
- `autotouch capabilities --output json` for provider/workflow contracts
- `autotouch capabilities operations list --surface research_table --cli-available` and `autotouch capabilities operations get research_table.email_finder` to map an exact product operation to the current CLI command path
- Check `autotouch --version`, then inspect the installed manifest for the exact supported command set; repository source does not establish the version installed from PyPI.
- `autotouch capabilities --output json --select list_builds` for source-specific list-build metadata; use operation detail for the full native filter schema
- `autotouch integrations list` and `autotouch integrations status --provider <provider>` before choosing source/destination workflows
- `autotouch workflows plan --type bulk_outreach` and `autotouch workflows scaffold --type bulk_outreach --out-dir workflow` for goal-level workflow planning
- `autotouch workflows plan --type website_visitors` and `autotouch workflows scaffold --type website_visitors --out-dir workflow` for website visitor or external intent provider webhook workflows
- `autotouch integrations recipe --type from_crm --provider hubspot` for CRM-to-research-table imports
- `autotouch columns recipe --type to_crm|to_outreach|to_leads|to_sequence` for research-table destination workflows
- `autotouch list-build inputs` and `autotouch list-build pricing` before creating durable list-build jobs
- `autotouch list-build companies`, `autotouch list-build leads`, `autotouch list-build jobs`, and `autotouch list-build sync-to-table --type companies|leads|jobs` for durable company/lead/job list builds and the first-class Smart Table handoff
- `autotouch linkedin status` and `autotouch linkedin limits` for connected-account diagnostics
- `autotouch sequences recipe --type linkedin_outreach` only when the campaign should include LinkedIn touches; the emitted branch waits for connection acceptance before LinkedIn chat and uses email fallback after the wait window
- `autotouch rows list` / `autotouch rows get` / `autotouch cells get` for read-back and audit
- `autotouch sequences ...` and `autotouch tasks ...` for sequence/task workflows
- `pip install 'autotouch-cli[mongo]'` if you need the Mongo-backed `status` / `watch` commands

## Realtime Table Updates

Research-table cell updates are persistence-first. Writers update Mongo `cells`; the API-side Mongo change-stream listener emits table-scoped `cells_update_batch` events. Workers should not emit per-cell socket events directly, and `/api/events/emit` is not a cell-update transport.

CSV import/export and long-running workflows still emit low-rate lifecycle/progress events such as `table_update`, but saved cell state is the source of truth for table rendering. For the full contract, see `docs/platform/realtime-events.md` and `docs/infra/azure/runtime-v2.md`.

## LLM Columns

For `llm_enrichment` in either `basic` or `agent` mode, the recommended path is:
- provide `config.instructions`
- let the API compile and persist the canonical `config.advancedPrompt`
- keep `config.useAutoSchema = true`

When exact prompt text is required, send `config.advancedPrompt` directly and
set `config.promptSource = "manual"`. Runtime reads only the materialized
`advancedPrompt`; removed `config.prompt` and `config.advanced_prompt` aliases
are rejected. For controller-backed research, keep tool protocol, source
progression, evidence memory, coverage, and finalization in the host/controller
instead of embedding them in the column prompt.

Only send `user_schema` / `response_schema` when you intentionally want to override the generated schema and keep it aligned yourself. The installed recipe surface at `autotouch columns recipe --type llm_enrichment` follows this contract.

Schema ownership rules:
- Accepted generated schemas and explicit user schemas are the saved output contract.
- Row execution must not add fields, rename fields, or replace a valid locked schema.
- Agent-mode evidence/scored state decides which values may fill the schema; the finalizer formats those values and schema validation gates persistence.

Prompt variables in authored prompts support nested JSON access:
- Use flat row variables like `{{company_name}}` for scalar columns.
- Use `{{company_name_clean}}` in outreach copy when you want a suffix-stripped readable company name (`Acme, Inc.` → `Acme`).
- Do not use `{{company_name_normalized}}` in outreach copy; it is an internal company match key for fallback lookup/debug use.
- Use dotted placeholders like `{{linkedin_lookup.linkedin_url}}` when the source column stores JSON or stringified JSON.
- For arrays, use numeric indexes such as `{{contacts.0.email}}`.
- This is different from config payload mappings, which use objects like `{ "column": "linkedin_lookup", "path": "linkedin_url" }`.

## Docs

- Full CLI reference: https://github.com/nicolonic/autotouch_main/blob/main/docs/research-table/reference/autotouch-cli.md
- Agent playbook: https://github.com/nicolonic/autotouch_main/blob/main/docs/research-table/guides/autotouch-cli-agent-playbook.md
- Tables/API reference: https://github.com/nicolonic/autotouch_main/blob/main/docs/research-table/reference/tables-api.md
- Authentication/scopes: https://github.com/nicolonic/autotouch_main/blob/main/docs/platform/authentication.md
- Instantly/Smartlead external sending accounts: https://github.com/nicolonic/autotouch_main/blob/main/docs/integrations/email-automation-platforms.md
