> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.paywithatoa.co.uk/llms.txt
> Use this file to discover all available pages before exploring further.

# Atoa CLI | Payments from Your Terminal

> The Atoa CLI manages payments, customers and webhooks from your terminal. Install it, authenticate and run commands, with examples.

The **Atoa CLI** is a command-line tool for interacting with the Atoa payment API from your terminal. Create payments, manage customers, test webhooks, and inspect transactions — without leaving the shell, and without writing a script.

It's scriptable for CI/CD, automated test pipelines, support workflows, and one-off operations.

**Source code:** [github.com](https://github.com/ATOAPaymentsLimited/Atoa-CLI)

<CardGroup cols={2}>
  <Card title="Log in with your browser" icon="browser" href="#first-time-setup">
    `atoa login` opens the dashboard, you approve, done. No token to copy-paste.
  </Card>

  <Card title="Test webhooks locally" icon="bell" href="#trigger-test-webhook-events">
    `atoa webhooks trigger PAYMENTS_STATUS` fires a fake event at your sandbox URL — no real payment needed.
  </Card>

  <Card title="Two credential types" icon="layer-group" href="#the-dual-credential-model">
    Your browser login runs account commands; an SDK key runs payments/data commands.
  </Card>

  <Card title="Shell completion" icon="keyboard" href="#shell-completion">
    TAB-completes commands, flags, profile names, and key IDs across bash, zsh, and PowerShell.
  </Card>
</CardGroup>

***

## Prerequisites

* **Node.js 20 or later** — verify with `node --version`
* An **Atoa merchant account** and a **desktop browser** on the same machine (login is browser-based). No account yet? Run `atoa signup`, or see the [Getting Started](/introduction#step-1-sign-up-for-developer-access) guide.

***

## Installation

<Tabs>
  <Tab title="npm">
    ```bash theme={null}
    npm install -g @atoapayments/atoa-cli
    ```
  </Tab>

  <Tab title="yarn">
    ```bash theme={null}
    yarn global add @atoapayments/atoa-cli
    ```
  </Tab>

  <Tab title="pnpm">
    ```bash theme={null}
    pnpm add -g @atoapayments/atoa-cli
    ```
  </Tab>
</Tabs>

Verify the install:

```bash theme={null}
atoa --version
```

<Note>
  The CLI stores credentials locally under `~/.atoa/auth/`, readable only by
  your user account.
</Note>

***

## First-time setup

Pair the CLI with your Atoa account through your browser:

```bash theme={null}
atoa login
```

Your default browser opens the Atoa dashboard, you approve the request, and the CLI is logged in. If your account has multiple businesses, you pick one (or run `atoa business use <id>` afterwards). A **profile** is created, named after your business.

<Note>
  `atoa login` requires an interactive terminal (TTY) and a desktop browser
  on the same machine — there is **no headless/CI login path** (see
  [CI / automation](#using-the-cli-in-ci)).
</Note>

No account yet? `atoa signup` walks you through account creation (email + one-time code) and business onboarding — no prior login needed.

### Confirm you're logged in

```bash theme={null}
atoa whoami
```

```
Signed in as Acme Ltd
  Email     jane@example.com
  Profile   acme
```

***

## The dual-credential model

The CLI holds **two independent credential types** per profile:

```
atoa login (browser)
      │
      ├─► Login session ──► account-management commands
      │                                  business, sessions, keys, staff, roles,
      │                                  kyb, payment-links, bank, stores
      │
      └─► SDK API key (optional, via `atoa keys create`)
              ──► payments / data commands
                  payments, refunds, customers, payment-methods,
                  card-on-file, webhooks, bank-feed, payouts, institutions
```

A single `atoa login` always creates the **login session**. The **SDK key** is optional — add it whenever you need the payments/data commands:

```bash theme={null}
atoa keys create --env sandbox    # mints a revocable SDK key
```

`atoa keys create` requires an admin role on the business. The `apiSecret` is stored locally under `~/.atoa/auth/`, so the CLI picks it up automatically on later commands.

### Which login does each command need?

| Auth required                    | Commands                                                                                                                  |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **Login session** (`atoa login`) | `business`, `sessions`, `keys create/list`, `kyb`, `staff`, `roles`, `payment-links`, `bank`, `stores`                    |
| **SDK key** (`atoa keys create`) | `payments`, `refunds`, `customers`, `payment-methods`, `card-on-file`, `webhooks`, `bank-feed`, `payouts`, `institutions` |

***

## Environments and profiles

A single machine can hold credentials for **many businesses** (profiles) **and both environments** (sandbox + production). The CLI keeps them separate so you can't accidentally run a production refund while testing locally.

```bash theme={null}
atoa profile list          # see every profile and which envs are configured
atoa profile use acme      # switch active profile
atoa whoami                # confirm which account and profile are active
```

<Warning>
  SDK keys **are** per-env and **not interchangeable** — a sandbox key won't
  work against production. Mint one per env with `atoa keys create --env <env>`.
</Warning>

***

## Per-profile settings

CLI state is stored entirely under profiles. To change which env a profile defaults to, use `atoa profile set`. To wipe everything, use `atoa reset`.

### Inspect state

```bash theme={null}
atoa profile list                 # every profile + which envs each one has
atoa profile show                 # full metadata for the active profile
atoa profile show acme            # metadata for a specific profile
```

### Change the default env on a profile

```bash theme={null}
atoa profile set env=production   # active profile; prompts to confirm
atoa profile set env=sandbox --profile acme --yes   # scripted / CI
```

The keys you can set:

| Key   | Allowed values            | Notes                                                                                                                                                                            |
| ----- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `env` | `sandbox` \| `production` | Sets which env commands target by default. Prompts for confirmation; pass `--yes` to skip. Mint the env's SDK key with `atoa keys create --env <env>` if you don't have one yet. |

### Reset everything

```bash theme={null}
atoa reset --yes              # wipe local profiles + tokens (local only)
atoa reset --revoke --yes     # additionally revoke server-side keys (best-effort)
```

<Warning>
  `atoa reset` is destructive — it removes every profile and clears
  every stored credential for this CLI. Use `--revoke` only when you also want the
  server-side keys deactivated (e.g. when offboarding a shared device).
</Warning>

***

## Core workflows

### Create a payment

```bash theme={null}
atoa payments create \
  --amount 25.00 \
  --orderId order-001 \
  --customerId cust_123
```

Returns a `paymentRequestId`, the customer-facing `paymentLink`, and a `qrCodeUrl`.

### Check payment status (poll until terminal)

```bash theme={null}
atoa payments status b7c8e3a1-4d29-4f5a-8e6b-1f3a2c4d5e7f --poll
```

`--poll` keeps polling every 5 seconds until the status is no longer `PENDING`, up to 3 minutes.

### List recent transactions

```bash theme={null}
atoa payments transactions --from 2026-01-01 --to 2026-01-31 --status COMPLETED
```

### Refund a payment

```bash theme={null}
atoa refunds create \
  --paymentRequestId b7c8e3a1-4d29-4f5a-8e6b-1f3a2c4d5e7f \
  --amount 5.00 \
  --currency GBP \
  --notes "duplicate charge" \
  --yes
```

### Manage customers and saved cards

```bash theme={null}
atoa customers create --fullName "Jane Doe" --email jane@example.com --type INDIVIDUAL
atoa customers list --search jane
atoa payment-methods list --customer cust_123
atoa card-on-file charge \
  --customerId cust_123 \
  --paymentMethodId card_456 \
  --amount 10.50 \
  --orderId order-007
```

***

## Account management

These run on your **browser login** — no SDK key needed.

### Switch the active business

If your account owns several businesses, account commands operate on one at a time:

```bash theme={null}
atoa business list              # list businesses; marks the active one
atoa business use <businessId>  # switch the active business (validated server-side)
```

### Manage login sessions

Every `atoa login` registers a device session server-side. Audit and revoke them:

```bash theme={null}
atoa sessions list                 # active CLI sessions for this account
atoa sessions revoke <deviceId>    # revoke one (use --yes to skip the prompt)
```

<Warning>
  Revoking the **current** device's session logs this CLI out — you'll need to
  `atoa login` again.
</Warning>

### SDK key lifecycle

```bash theme={null}
atoa keys create --name "CI server"   # mint a key with a label
atoa keys list                        # list keys for this account (never shows secrets)
atoa keys regenerate --env sandbox --yes   # rotate the active env's key
atoa keys revoke <sdkAccessId> --yes       # revoke a specific key (or omit the id to pick interactively)
```

***

## Team, onboarding & business setup

### Staff and roles

```bash theme={null}
atoa roles list                 # role IDs available on this business
atoa staff list                 # current staff members
atoa staff invite \
  --firstName Alice --lastName Smith \
  --email alice@example.com \
  --role <roleId>               # role ID from `atoa roles list`
atoa staff invite … --store <storeId>   # restrict to store(s); repeat --store for several
```

### KYB (Know Your Business) verification

```bash theme={null}
atoa kyb status          # verification status for the active business
atoa kyb link            # print the KYB dashboard deep-link
atoa kyb link --open     # …and open it in your default browser
```

### Bank accounts

```bash theme={null}
atoa bank list                          # bank accounts on the active business
atoa bank add                           # interactive: pick bank, enter details, verify via OTP
atoa bank add --sortCode 040004 --accountNumber 12345678 \
  --accountHolderName "Acme Ltd" --setPrimary
atoa bank get <bankAccountId>
atoa bank delete <bankAccountId> --yes
```

### Stores

All `stores` subcommands need a browser login:

```bash theme={null}
atoa stores list                                        # list merchant stores
atoa stores get <storeId>                               # store details
atoa stores link-bank <storeId> --bank <bankAccountId>  # attach a bank account to a store
atoa stores image ./logo.png --storeId <storeId>        # upload/replace the store logo (PNG/JPG, ≤6MB)
```

### Onboard a brand-new account

```bash theme={null}
atoa signup                 # interactive: email + one-time code, then a guided onboarding wizard
```

<Note>
  `atoa signup` is the one command that needs **no prior login** — it creates
  the account and logs you in as part of the flow.
</Note>

***

## Payment links

Create a hosted checkout link (browser login; `--store-id` is required):

```bash theme={null}
atoa payment-links create --amount 10.50 --store-id <storeId>
atoa payment-links create --amount 10.50 --store-id <storeId> --notes "Invoice #42"   # notes max 30 chars
atoa payment-links get <linkId> --store-id <storeId>       # fetch a link + its status
atoa payment-links delete <linkId> --store-id <storeId>    # remove a link
```

<Note>
  `--amount` is in **GBP** (e.g. `10.50` for £10.50), not pence.
</Note>

***

## Idempotency — duplicate-charge protection

The CLI auto-generates a fresh `Idempotency-Key: <uuidv4>` header on every POST/PUT/PATCH, so a network blip on the response never double-charges a customer. You don't need to think about this for interactive use.

For **CI / scripted retries** — where one logical operation may run more than once (a job restarts) — pass a **stable** `--idempotencyKey` so a re-run produces the same key and the server deduplicates:

```bash theme={null}
# Auto-generated key (default — safe for interactive use)
atoa refunds create --paymentRequestId pr_abc --amount 5.00

# Stable key (CI retry-safe — same key on every re-run of the same job)
atoa refunds create \
  --paymentRequestId pr_abc --amount 5.00 \
  --idempotencyKey "atoa-cli/refund/${RUN_ID}/${PAYMENT_ID}"
```

Available on `atoa payments create`, `atoa refunds create`, `atoa card-on-file charge`, and the generic `atoa post`. Every other POST still sends an auto-generated key, so all retries are safe by default.

***

## Trigger test webhook events

The CLI can fire a **synthetic webhook payload** at your registered sandbox URL — same shape as production — without you actually creating a real payment.

```bash theme={null}
atoa webhooks trigger PAYMENTS_STATUS
```

```
Test webhook triggered successfully.
```

Atoa's webhook service dispatches the body to whatever sandbox URL you registered with `atoa webhooks create`. Inspect the request on your endpoint (e.g. [webhook.site](https://webhook.site)) to verify your signature-check, routing logic, and response shape work end-to-end.

### Supported events

| Event                | Description                                             |
| -------------------- | ------------------------------------------------------- |
| `PAYMENTS_STATUS`    | Payment-status webhook (pay-by-bank or card)            |
| `REFUND_STATUS`      | Refund-status webhook                                   |
| `EXPIRED_STATUS`     | Payment expired (customer didn't pay in time)           |
| `POS_PAYMENT_STATUS` | POS event — choose which event to simulate via `--type` |

### Customising the dispatched body

| Flag              | Use                                                                                                                         |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `--orderId`       | Override the `orderId` in the dispatched payload                                                                            |
| `--amount`        | Override the `amount` in pounds, e.g. `10.05` for £10.05                                                                    |
| `--paymentMethod` | `CARD` or `PAY_BY_BANK`                                                                                                     |
| `--status`        | Force a specific status (`COMPLETED`, `AUTHORIZED`, `FAILED`, `CANCELLED`, `EXPIRED`)                                       |
| `--type`          | `POS_PAYMENT_STATUS` only — which POS event to simulate: `PAYMENTS_STATUS` (default) \| `REFUND_STATUS` \| `EXPIRED_STATUS` |
| `--customFields`  | `POS_PAYMENT_STATUS` only — JSON array of `{value, fieldName}` objects                                                      |

```bash theme={null}
# Successful card payment
atoa webhooks trigger PAYMENTS_STATUS --paymentMethod CARD --status AUTHORIZED

# Failed pay-by-bank
atoa webhooks trigger PAYMENTS_STATUS --status FAILED

# Refund that failed at the issuer
atoa webhooks trigger REFUND_STATUS --status FAILED

# Custom order ID and amount
atoa webhooks trigger PAYMENTS_STATUS --orderId my-test-order --amount 50.00

# POS_PAYMENT_STATUS can simulate several POS events — pick one via --type
atoa webhooks trigger POS_PAYMENT_STATUS --type PAYMENTS_STATUS
atoa webhooks trigger POS_PAYMENT_STATUS --type REFUND_STATUS --status COMPLETED
atoa webhooks trigger POS_PAYMENT_STATUS --type EXPIRED_STATUS
atoa webhooks trigger POS_PAYMENT_STATUS --type PAYMENTS_STATUS \
  --customFields '[{"value":"CUST_001","fieldName":"Customer ID"}]'
```

<Note>
  Test triggers are **sandbox-only by design**. If you pass `--env production`,
  the CLI warns you and proceeds with your active profile's sandbox key
  anyway — this prevents accidentally driving fake events at your live customers.
</Note>

***

## Using the CLI in CI

`atoa login` needs an interactive browser, so a CI runner **cannot log in itself**. Instead, provision credentials on a workstation once, then hand the files to the runner:

1. On a workstation: `atoa login`, plus `atoa keys create` if the job hits SDK/data commands.
2. Copy `~/.atoa/auth/` to a location the runner can read.
3. Point the runner at it with `ATOA_HOME` (credentials live in `$ATOA_HOME/.atoa/auth/`).

```bash theme={null}
# ATOA_HOME → the dir that holds .atoa/auth/. Scope to a named profile, use a stable idempotency key.
atoa --profile ci payments create \
  --amount 10.00 --orderId "$RUN_ID" --customerId cust_123 --redirectUrl https://x \
  --idempotencyKey "ci-payment/$RUN_ID" --dryRun

# Read-only check, machine-readable output
atoa --profile ci payments transactions --output json --status COMPLETED

# Clean up — clears the login session (server revoke is automatic); --purge-key also drops the SDK key
atoa logout --profile ci --purge-key --yes
```

Every command accepts `--output json` (the default) for machine-readable output and `--dryRun` to resolve the request without sending it. `--yes` is required on destructive commands in a non-TTY context.

### Exit codes

The CLI uses POSIX-style exit codes so pipelines can branch on the failure mode:

| Code | Meaning             | Trigger                                                      |
| ---: | ------------------- | ------------------------------------------------------------ |
|  `0` | Success             | Command completed                                            |
|  `1` | Generic failure     | Anything not classified below                                |
|  `2` | Auth / forbidden    | HTTP 401 / 403 — token invalid, revoked, or lacks permission |
|  `3` | Validation error    | HTTP 400, or client-side input rejected                      |
|  `4` | Not found           | HTTP 404                                                     |
|  `5` | Rate limited        | HTTP 429 — back off and retry                                |
|  `6` | Network / TLS / DNS | Couldn't reach the server                                    |

```bash theme={null}
if ! atoa refunds create --paymentRequestId "$PR" --amount 5.00 --idempotencyKey "$RUN_ID" --yes ; then
  case $? in
    2) echo "Token invalid — re-login required" ;;
    3) echo "Bad input — fix the payload" ;;
    5) echo "Rate limited — retry with backoff" ;;
    6) echo "Network blip — re-running with the same idempotency key is safe" ;;
  esac
fi
```

***

## Environment variables

| Variable       | Purpose                                                                                                                                        |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `ATOA_HOME`    | Override where config + credentials live. Defaults to `$HOME`; credentials land in `$ATOA_HOME/.atoa/auth/`. Useful for Docker / ephemeral CI. |
| `ATOA_PROFILE` | Default profile name — same as passing `--profile <name>` on every command. The explicit flag still wins.                                      |

***

## Generic HTTP verbs

When you need to hit an endpoint the CLI doesn't yet wrap (or you want full control over the request body), use the raw verbs:

```bash theme={null}
atoa get /api/payments/stores
atoa get /api/customers -d page=0 -d size=20 --pageAll
atoa post /api/webhook/merchant --data @webhook.json
atoa post /api/something -d amount=1005 -d orderId=order-001
atoa delete /api/customers/cust_123 --yes
```

### How `-d` and `--data` work

These are two different ways to build the request body. Pick whichever fits the call:

| Form                        | What it means                                                             | Example                                      |
| --------------------------- | ------------------------------------------------------------------------- | -------------------------------------------- |
| `-d key=value` (repeatable) | Build the body **one field at a time**. Same flag can be used many times. | `-d amount=1005 -d orderId=order-001`        |
| `--data '{"key":"value"}'`  | Pass the **entire body as an inline JSON string**.                        | `--data '{"orderId":"o-1","amount":1005}'`   |
| `--data @path/to/file.json` | The `@` prefix means **read the body from a file** on disk.               | `--data @webhook.json`                       |
| `--data -`                  | Read the body from **stdin** (pipe-in form for CI scripts).               | `cat body.json \| atoa post /api/x --data -` |

So `atoa post /api/webhook/merchant --data @webhook.json` reads `webhook.json` from the directory you ran the command in, takes its contents as the JSON request body, and POSTs it to `/api/webhook/merchant`. Example file:

```json theme={null}
// webhook.json
{
  "url": "https://your-server.example.com/atoa-hook",
  "event": "PAYMENTS_STATUS"
}
```

<Note>
  `-d` and `--data` are mutually meaningful — they both contribute to the same
  body. If you pass both, `--data` wins and `-d` fields are ignored. Use one or
  the other per command.
</Note>

### Passing nested objects and arrays

Some API fields aren't a single string or number — they're objects (e.g. `authentication`, `consumerDetails`) or arrays (e.g. `paymentMethod`, `storeIds`). How you pass these depends on which kind of command you're using.

<Warning>
  **`-d` and `--data` are flags on the generic verbs only** (`atoa get`, `atoa post`, `atoa delete`). Typed commands like `atoa payments create` and `atoa customers create` accept only their named flags — they do **not** accept `-d key=value` or `--data @file.json`.
</Warning>

#### On typed commands (e.g. `atoa payments create`)

Use the command's own named flags. Quote complex values so the shell passes them to the CLI intact:

| Value shape             | How to pass it             | Concrete example                                                                       |
| ----------------------- | -------------------------- | -------------------------------------------------------------------------------------- |
| **Object**              | Inline JSON, single-quoted | `--consumerDetails '{"firstName":"Jane","lastName":"Doe","email":"jane@example.com"}'` |
| **Array (comma form)**  | Comma-separated string     | `--paymentMethod CARD,PAY_BY_BANK`                                                     |
| **Multi-value filters** | Comma-separated string     | `--status COMPLETED,FAILED` · `--storeIds store_uk_1,store_uk_2`                       |

```bash theme={null}
# Object value via a typed flag (bash / zsh / git-bash)
atoa payments create \
  --amount 25.00 --orderId order-001 --customerId cust_123 \
  --redirectUrl https://shop.example.com/return \
  --consumerDetails '{"firstName":"Jane","lastName":"Doe","email":"jane@example.com","phoneCountryCode":"44","phoneNumber":"7700900000"}'

# Comma-separated array via a typed flag
atoa payments transactions --status COMPLETED,FAILED --storeIds store_uk_1,store_uk_2
```

<Note>
  **PowerShell users:** Windows re-parses arguments when handing them to
  native `.exe` / `.cmd` programs, so the bash-style `'{"key":"value"}'`
  doesn't survive intact. Use **backslash-escaped double quotes inside the
  single quotes** instead:

  ```pwsh theme={null}
  # Object value via a typed flag (PowerShell — note the \" escapes)
  atoa payments create --amount=25.00 --orderId=order-001 --customerId=cust_123 `
    --redirectUrl=https://shop.example.com/return `
    --consumerDetails '{\"firstName\":\"Jane\",\"lastName\":\"Doe\",\"email\":\"jane@example.com\",\"phoneCountryCode\":\"44\",\"phoneNumber\":\"7700900000\"}'
  ```

  If a value contains whitespace (e.g. `"Jane Doe"`), wrap the whole
  backslash-escaped JSON in an extra pair of outer double-quotes so Windows
  doesn't split at the space:

  ```pwsh theme={null}
  atoa payments create --consumerDetails '"{\"firstName\":\"Jane\",\"lastName\":\"Doe\"}"'
  ```

  bash / zsh / git-bash users — the single-quoted plain form (top of
  section) works directly, no escaping needed.
</Note>

If a typed command doesn't expose a flag for the field you need, fall through to the generic verbs below.

#### On generic verbs (e.g. `atoa post /api/...`)

The generic verbs accept any JSON body. Three ways to specify it:

| Value shape               | How to pass it              | Concrete example                                      |
| ------------------------- | --------------------------- | ----------------------------------------------------- |
| **Field by field**        | `-d key=value` (repeatable) | `-d amount=1005 -d orderId=order-001`                 |
| **Object field**          | `-d key=<JSON string>`      | `-d authentication='{"username":"u","password":"p"}'` |
| **Array field (JSON)**    | `-d key=<JSON array>`       | `-d paymentMethods='["CARD","PAY_BY_BANK"]'`          |
| **Whole body inline**     | `--data '<JSON>'`           | `--data '{"orderId":"o-1","amount":1005}'`            |
| **Whole body from file**  | `--data @path/to/file.json` | `--data @webhook.json`                                |
| **Whole body from stdin** | `--data -`                  | `cat body.json \| atoa post /api/x --data -`          |

```bash theme={null}
# Object value via -d (generic verb)
atoa post /api/webhook/merchant \
  -d sandboxUrl=https://example.com/hook \
  -d eventTypes=PAYMENTS_STATUS \
  -d authentication='{"username":"u","password":"p"}'

# Whole body from a file (generic verb)
atoa post /api/payments/process-payment --data @payment.json
```

<Note>
  Wrap JSON values in **single quotes** so `{`, `"`, and spaces pass through
  untouched.PowerShell: same rule, but escape any inner single quote by
  doubling it (`'It''s urgent'` → `It's urgent`).
</Note>

These use your active profile's credentials and respect every common flag (`--env`, `--output`, `--dryRun`, `--verbose`). Useful for:

* **New endpoints** that haven't gained a typed wrapper yet
* **Debugging** — `--verbose` prints the redacted request line so you can see exactly what was sent
* **CI scripts** where you want explicit control over the exact body sent

***

## Shell completion

Enable TAB-completion of commands, flags, profile names, and IDs:

<Tabs>
  <Tab title="bash">
    ```bash theme={null}
    atoa completion bash >> ~/.bashrc && source ~/.bashrc
    ```
  </Tab>

  <Tab title="zsh">
    ```bash theme={null}
    atoa completion zsh > ~/.zsh/completions/_atoa
    # Then in ~/.zshrc above `compinit`:
    #   fpath=(~/.zsh/completions $fpath)
    #   autoload -Uz compinit && compinit
    ```
  </Tab>

  <Tab title="PowerShell">
    ```powershell theme={null}
    # Current session
    atoa completion pwsh | Out-String | Invoke-Expression

    # Persist across sessions
    Add-Content $PROFILE 'atoa completion pwsh | Out-String | Invoke-Expression'
    ```
  </Tab>
</Tabs>

After installing, hit `<TAB>` mid-command:

```
atoa pa<TAB>          → payments, payment-methods, payouts
atoa payments <TAB>   → create, status, cancel, transactions
atoa profile use <TAB> → live profile names from your config
```

***

## Command reference

<AccordionGroup>
  <Accordion title="Authentication & account setup" icon="key">
    | Command       | Purpose                                                                   | Auth |
    | ------------- | ------------------------------------------------------------------------- | ---- |
    | `atoa login`  | Pair the CLI with an account via the browser                              | —    |
    | `atoa logout` | Clear a profile's login session (`--purge-key` also removes its SDK keys) | —    |
    | `atoa whoami` | Show the current account identity and profile                             | —    |
    | `atoa signup` | Create a new account (email + OTP) and onboard a business                 | none |
  </Accordion>

  <Accordion title="Business, sessions & SDK keys" icon="id-card">
    | Command                           | Purpose                                      | Auth  |
    | --------------------------------- | -------------------------------------------- | ----- |
    | `atoa business list`              | List businesses; marks the active one        | login |
    | `atoa business use <id>`          | Switch the active business for account calls | login |
    | `atoa sessions list`              | List active CLI sessions                     | login |
    | `atoa sessions revoke <deviceId>` | Revoke a session                             | login |
    | `atoa keys create`                | Mint an SDK key                              | login |
    | `atoa keys list`                  | List SDK keys (never shows secrets)          | login |
    | `atoa keys regenerate`            | Rotate the active env's SDK key              | login |
    | `atoa keys revoke [<id>]`         | Revoke an SDK key                            | login |
  </Accordion>

  <Accordion title="Team, KYB & bank accounts" icon="users-gear">
    | Command                         | Purpose                                     | Auth  |
    | ------------------------------- | ------------------------------------------- | ----- |
    | `atoa staff list`               | List staff members                          | login |
    | `atoa staff invite`             | Invite a staff member (`--role`, `--store`) | login |
    | `atoa roles list`               | List available roles                        | login |
    | `atoa kyb status`               | KYB verification status                     | login |
    | `atoa kyb link [--open]`        | KYB dashboard deep-link                     | login |
    | `atoa bank list/get/add/delete` | Manage business bank accounts               | login |
  </Accordion>

  <Accordion title="Payment links" icon="link">
    | Command                          | Purpose                                                  | Auth  |
    | -------------------------------- | -------------------------------------------------------- | ----- |
    | `atoa payment-links create`      | Create a hosted checkout link (`--amount`, `--store-id`) | login |
    | `atoa payment-links get <id>`    | Fetch a link and its status (`--store-id`)               | login |
    | `atoa payment-links delete <id>` | Delete a link (`--store-id`)                             | login |
  </Accordion>

  <Accordion title="Profiles" icon="user-gear">
    | Command                           | Purpose                                 |
    | --------------------------------- | --------------------------------------- |
    | `atoa profile list`               | List all profiles on this machine       |
    | `atoa profile use <name>`         | Set the active profile                  |
    | `atoa profile show [name]`        | Show profile metadata (never the token) |
    | `atoa profile set <key>=<value>`  | Change the default `env` on a profile   |
    | `atoa profile rename <old> <new>` | Rename a profile                        |
    | `atoa profile delete <name>`      | Remove a profile and its tokens         |
    | `atoa reset`                      | Wipe all profiles and credentials       |
  </Accordion>

  <Accordion title="Payments" icon="money-bill-wave">
    | Command                                  | Purpose                                         |
    | ---------------------------------------- | ----------------------------------------------- |
    | `atoa payments create`                   | Create a payment request                        |
    | `atoa payments status <id>`              | Check a payment's status (optional `--poll`)    |
    | `atoa payments cancel <id>`              | Cancel a payment request                        |
    | `atoa payments transactions`             | List transactions (filter by date/status/store) |
    | `atoa stores list`                       | List merchant stores (login)                    |
    | `atoa stores get <id>`                   | Store details (login)                           |
    | `atoa stores link-bank <id> --bank <id>` | Attach a bank account to a store (login)        |
    | `atoa stores image <file>`               | Upload/replace a store logo (login)             |
  </Accordion>

  <Accordion title="Customers, Cards & Card-on-File" icon="users">
    | Command                                        | Purpose                              |
    | ---------------------------------------------- | ------------------------------------ |
    | `atoa customers create/list/get/update/delete` | Customer CRUD                        |
    | `atoa payment-methods list/get/delete`         | Saved cards per customer             |
    | `atoa card-on-file charge`                     | Charge a saved card                  |
    | `atoa card-on-file capture <id>`               | Settle a pre-authorized card payment |
    | `atoa card-on-file cancel <id>`                | Void a pre-authorization             |
  </Accordion>

  <Accordion title="Refunds" icon="retweet">
    | Command                    | Purpose                                        |
    | -------------------------- | ---------------------------------------------- |
    | `atoa refunds create`      | Initiate a refund (full or partial)            |
    | `atoa refunds cancel <id>` | Cancel an `INITIATED` refund (production only) |
    | `atoa refunds list`        | List refunds for a payment                     |
  </Accordion>

  <Accordion title="Webhooks" icon="bell">
    | Command                         | Purpose                                      |
    | ------------------------------- | -------------------------------------------- |
    | `atoa webhooks create`          | Register a webhook endpoint                  |
    | `atoa webhooks list`            | List registered webhooks                     |
    | `atoa webhooks delete <id>`     | Remove a webhook                             |
    | `atoa webhooks trigger <event>` | Fire a synthetic webhook at your sandbox URL |
  </Accordion>

  <Accordion title="Bank Feed (Open Banking)" icon="building-columns">
    | Command                            | Purpose                                |
    | ---------------------------------- | -------------------------------------- |
    | `atoa bank-feed initiate`          | Start a bank authorization flow        |
    | `atoa bank-feed accounts`          | List authorized accounts for a consent |
    | `atoa bank-feed account <id>`      | Account details                        |
    | `atoa bank-feed balance <id>`      | Account balance                        |
    | `atoa bank-feed transactions <id>` | Account transactions in a date range   |
    | `atoa bank-feed revoke`            | Revoke account access                  |
  </Accordion>

  <Accordion title="Payouts" icon="money-bill-transfer">
    | Command                          | Purpose                                       |
    | -------------------------------- | --------------------------------------------- |
    | `atoa payouts list`              | List payouts (date / status / search filters) |
    | `atoa payouts transactions <id>` | Transactions for a single payout              |
  </Accordion>

  <Accordion title="Utilities" icon="wrench">
    | Command                   | Purpose                                       |
    | ------------------------- | --------------------------------------------- |
    | `atoa institutions list`  | List supported banking institutions           |
    | `atoa get/post/delete`    | Raw HTTP requests against any API path        |
    | `atoa completion <shell>` | Print the shell-completion bootstrap          |
    | `atoa --help`             | Top-level help; works on every subcommand too |
  </Accordion>
</AccordionGroup>

Most commands accept these **common flags** (per-command `--help` is authoritative):

| Flag        | Use                                                              |
| ----------- | ---------------------------------------------------------------- |
| `--env`     | Override env (`sandbox` \| `production`) for this single command |
| `--output`  | `json` \| `table` \| `yaml`                                      |
| `--verbose` | Print a redacted request log for debugging                       |
| `--dryRun`  | Resolve the request but don't send it                            |
| `--yes`     | Skip confirmation prompts                                        |
| `--profile` | Operate against a specific profile                               |

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="401 or 'No profile is configured' on every command">
    First-run state, or your session expired / was revoked (e.g. a later login
    on the same device evicted it). Check who you are, then re-pair:

    ```bash theme={null}
    atoa whoami          # confirm the active profile
    atoa login           # log in again via your browser
    ```

    If an **account command** complains you only have an SDK key (or vice versa),
    obtain the missing credential — `atoa login` for a login session,
    `atoa keys create` for an SDK key. See [the dual-credential
    model](#the-dual-credential-model).
  </Accordion>

  <Accordion title="'Timed out acquiring lock on …/session.json.lock'">
    Another `atoa` process is mid-write. If none is running (e.g. one crashed),
    remove the stale lockfile:

    ```bash theme={null}
    rm ~/.atoa/auth/session.json.lock
    ```
  </Accordion>

  <Accordion title="'Refusing to read …/session.json: insecure permissions' (POSIX)">
    The session file gained group/other read bits — fix with `chmod 600 ~/.atoa/auth/session.json`.
  </Accordion>

  <Accordion title="`atoa webhooks trigger` says 'No webhook subscription configured'">
    You need to register a sandbox webhook URL first:

    ```bash theme={null}
    atoa webhooks create --url https://webhook.site/<your-uuid> --event PAYMENTS_STATUS
    atoa webhooks trigger PAYMENTS_STATUS
    ```

    The trigger uses the URL registered for the **same event type** under your active profile's sandbox.
  </Accordion>

  <Accordion title="TAB completion doesn't work">
    1. Re-source your shell config after install (e.g. `source ~/.bashrc`, or open a new PowerShell window).

    2. Confirm completion is registered:

       ```bash theme={null}
       # bash
       complete -p atoa
       # zsh
       echo $_comps[atoa]
       # PowerShell
       Get-PSReadLineKeyHandler -Bound | Where-Object Function -eq Complete
       ```

    3. For PowerShell specifically, make sure you ran `| Out-String | Invoke-Expression` after `atoa completion pwsh` — the script must be **evaluated**, not just printed.
  </Accordion>

  <Accordion title="Want to nuke everything and start fresh">
    ```bash theme={null}
    atoa reset --yes              # local only
    atoa reset --revoke --yes     # local + best-effort server-side revoke
    ```
  </Accordion>
</AccordionGroup>

***

## Need Help?

Contact our team at `hello@paywithatoa.co.uk` or use chat support on the [Dashboard](https://dashboard.paywithatoa.co.uk/).
