> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paywithatoa.com/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)

> **[Log in with your browser](#first-time-setup)**
>
> `atoa login` opens the dashboard, you approve, done. No token to copy-paste.

> **[Test webhooks locally](#trigger-test-webhook-events)**
>
> `atoa webhooks trigger PAYMENTS_STATUS` fires a fake event at your sandbox URL — no real payment needed.

> **[Two credential types](#the-dual-credential-model)**
>
> Your browser login runs account commands; an SDK key runs payments/data commands.

> **[Shell completion](#shell-completion)**
>
> TAB-completes commands, flags, profile names, and key IDs across bash, zsh, and PowerShell.

***

## 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](https://docs.paywithatoa.com/introduction.md#step-1-sign-up-for-developer-access) guide.

***

## Installation

**npm**

```bash theme={null}
npm install -g @atoapayments/atoa-cli
```

**yarn**

```bash theme={null}
yarn global add @atoapayments/atoa-cli
```

**pnpm**

```bash theme={null}
pnpm add -g @atoapayments/atoa-cli
```

Verify the install:

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

> [!NOTE]
>
> The CLI stores credentials locally under `~/.atoa/auth/`, readable only by
> your user account.

***

## 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)).

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,
      │                                  addons, comms, custom-branding,
      │                                  custom-sms, direct-debit
      │
      └─► SDK API key (optional, via `atoa keys create`)
              ──► payments / data commands
                  payments, refunds, customers, payment-methods,
                  card-on-file, webhooks, bank-feed, payouts, institutions,
                  get, post, delete
```

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`, `addons`, `comms`, `custom-branding`, `custom-sms`, `direct-debit`, `google` |
| **SDK key** (`atoa keys create`) | `payments`, `refunds`, `customers`, `payment-methods`, `card-on-file`, `webhooks`, `bank-feed`, `payouts`, `institutions`, `get`, `post`, `delete` |

> [!NOTE]
>
> The generic verbs `atoa get/post/delete` authenticate with the **SDK key**, not
> the browser session — they're for calling the payments API directly, so
> `atoa keys create` must have been run first.

***

## 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>`.

***

## 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).

***

## 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.

### 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

`atoa staff add` and `atoa staff invite` are the same command under two names — both create
the staff member and notify them.

```bash theme={null}
atoa roles list                 # role IDs available on this business
atoa staff list                 # current staff members
atoa staff add \
  --firstName Alice --lastName Smith \
  --email alice@example.com \
  --role <roleId>               # role ID from `atoa roles list`
atoa staff add <same flags> --store <storeId>   # restrict to store(s); repeat --store for several
atoa staff update <userId>      # edit name, email, phone, role or permitted stores
atoa staff delete <userId> --yes
```

Run `atoa staff update` or `atoa roles update` with no arguments on a terminal and the CLI
prompts for each field with the **current value already filled in** — press Enter to keep it.

```bash theme={null}
atoa roles permissions                  # the permission catalogue: id, name, category, requires
atoa roles add --name "Shift lead"      # prompts for the permissions to grant
atoa roles add --name "Shift lead" --permission <id>   # repeat --permission for several
atoa roles update <roleId>              # edit name, description or permissions
atoa roles delete <roleId> --yes
```

`--permission` takes permission **ids**, and `atoa roles permissions` is where they come from —
every other view shows only names, so start there when scripting a role. Each row also carries a
`requires` list, which is why a role can end up with more permissions than you asked for.

> [!NOTE]
>
> Some permissions depend on others. When you tick one, the CLI automatically adds
> the permissions it requires and prints which ones it added.

### KYB (Know Your Business) verification

```bash theme={null}
atoa kyb status          # verification status for the active business
atoa kyb link            # open the KYB form in your browser
atoa kyb card status     # card-payment application status, plus what's blocking it
atoa kyb card link       # open the card-payment application form
```

Card payments need a separate application, which stays blocked until KYB passes. Both `link`
commands open the browser for you and print the URL as well, so there's something to fall back on
if no browser can be launched.

### Bank accounts

```bash theme={null}
atoa bank list                          # bank accounts on the active business
atoa bank add
atoa bank add --sortCode <sortCode> --accountNumber <accountNumber> \
  --accountHolderName "<account holder name>"
atoa bank get <bankAccountId>
atoa bank delete <bankAccountId> --yes
```

Run `atoa bank add` with no flags on a terminal and it walks you through it — pick your bank, enter
the details, confirm. Your first bank account is set as the primary automatically.

The primary account is where Atoa settles your payments. Pass `--setPrimary` on a later account to
move settlement to it — this also re-points any store currently linked to the previous account.

If a one-time code is required, `atoa bank add` sends it and exits with code `9`. Re-run the same command with
`--otp` to complete it:

```bash theme={null}
atoa bank add --bankName "<bank name>" --sortCode <sortCode> \
  --accountNumber <accountNumber> --accountHolderName "<account holder name>" --output json
#   → exit code 9: "An OTP was sent to your registered contact. Re-run with --otp <code>"

atoa bank add <same flags> --otp <code> --output json
```

### 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 add                                         # interactive: name, address, postcode
atoa stores add --locationName "<store name>" --addressLine1 "<address line 1>" \
  --cityOrTown "<city>" --addressPostalCode "<postcode>"
atoa stores update <storeId>                            # prompts with current values prefilled
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)
```

### Google Business listing

Pair a store with its Google Business listing so reviews flow back to Atoa:

```bash theme={null}
atoa google search "<business name>"                    # find listings, with addresses
atoa google link --store <storeId> \
  --search "<business name>" --placeId <placeId>
atoa google locations                                   # listings linked to this business
atoa google unlink --store <storeId> --yes              # remove a link
```

**Search first, then link with a `placeId` from those results.** The listing's name and address are
stored exactly as the CLI sends them and are never looked up from the id, so `--placeId` on its own
exits with code `3` — the search is what supplies them. Pass the same `--search` text to both commands.

Linking a store that already has a listing **replaces** it — there's no warning, so check with
`--dryRun` first. `atoa google unlink` reverses it.

> [!NOTE]
>
> `atoa google search` works on any account. `link`, `unlink` and `locations` need the **Google
> Review add-on** and exits with code `8` without it — `atoa addons list` shows your plan's limits. Once the
> plan allows it, `locations` still exits with code `4` until the merchant connects their Google Business
> account, which happens in the dashboard rather than the CLI.

### Direct Debit

Atoa collects your subscription fees by Direct Debit. A business can hold **one** mandate —
once it's active, setup refuses rather than creating a second one.

```bash theme={null}
atoa direct-debit status     # whether a mandate exists, and its status
atoa direct-debit setup      # interactive: bank details + billing address, then the mandate
```

Run interactively and the CLI prefills the account holder name, email and billing address
already on the business, and masks the account number it has on file — press Enter to keep it.

To run it without a terminal, pass every field as a flag plus `--acceptMandate`:

```bash theme={null}
atoa direct-debit setup --accountNumber <accountNumber> --sortCode <sortCode> \
  --name "<account holder name>" --email <email> \
  --addressLine1 "<address line 1>" --city "<city>" --postalCode "<postcode>" \
  --acceptMandate --output json
```

Only `--addressLine2` is optional. The name is the **account holder's own**, not the business's —
the mandate is signed by an individual — and it's capped at 20 characters here.

> [!WARNING]
>
> `--acceptMandate` is **required** and records the merchant's agreement to the Direct Debit
> mandate terms: the request stores an affirmative acceptance with a timestamp. Without it the
> command exits with code `3` and creates nothing. Only pass it once the merchant has explicitly agreed, and
> tell them what they're agreeing to.

### Notification preferences

```bash theme={null}
atoa comms list                              # every topic, with each channel's state
atoa comms set <topic> --email off           # <topic> takes the topic's ID or its display name
atoa comms set <topic> --sms on --push off
```

Each channel reads as `on`, `off`, or `unavailable` — Atoa doesn't send every channel for
every topic, and an unavailable channel can't be switched on.

### Add-on plans

```bash theme={null}
atoa addons list                    # current plan, feature usage, and what you can move to
atoa addons upgrade                 # pick a plan on a TTY, or pass a plan ID
atoa addons downgrade <planId>
atoa addons cancel-downgrade        # cancel a downgrade that hasn't taken effect yet
```

> [!NOTE]
>
> A downgrade is refused while your usage exceeds the target plan's limits. The CLI
> names which features are over the limit so you know what to reduce first.

### Checkout branding and SMS sender name

```bash theme={null}
atoa custom-branding get            # current checkout theme colour
atoa custom-branding set '#FF0000'  # 6-digit hex
atoa custom-branding reset          # back to the Atoa default

atoa custom-sms list                # current sender name and its review status
atoa custom-sms set AcmeLtd         # request a sender name (3-11 chars; letters, numbers, spaces)
atoa custom-sms delete --yes
```

### Onboard a brand-new account

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

Every prompted value also has a flag, so signup can run with no terminal — useful in CI or from a
script. The code goes to your inbox, so it takes two runs:

```bash theme={null}
atoa signup --email you@example.com --output json
#   → exit code 9: "An OTP has been sent to you@example.com."

atoa signup --email you@example.com --otp <code> --acceptTerms \
  --businessName "Acme Ltd" --industry "Retail - Other" \
  --monthlyTurnover "Up to £10,000" --businessStructure "Limited Company" \
  --vatNumber 123456789 --firstName Ada --lastName Lovelace \
  --postalCode "SW1A 2AA" --addressLine1 "10 Downing Street" --output json
```

`--acceptTerms` records acceptance of the [Privacy Policy](https://paywithatoa.co.uk/atoa-business-privacy-policy/)
and [Terms of Service](https://paywithatoa.co.uk/terms/); `--marketing` is a separate opt-in.
`--startNew` creates a second business rather than resuming an existing signup.

`--businessStructure` is `Limited Company` or `Charity`. `--industry` and `--monthlyTurnover` are
server-defined lists that vary by environment, so the values above are illustrative — pass the
option's name and, if it doesn't match, the CLI exits with code `3` listing every valid one to choose from.

> [!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.

***

## Payment links

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

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

> [!NOTE]
>
> `--amount` is in **GBP** (e.g. `10.50` for £10.50), not pence.

***

## 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.

***

## 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); --purgeKey also drops the SDK key
atoa logout --profile ci --purgeKey --yes
```

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

> [!NOTE]
>
> Passing `--output` does more than pick a format — it also puts the CLI into **non-interactive
> mode**, so no prompt is ever opened. A command that would have asked for a value reports the
> missing flag by name and exits with code `3` instead. This is what makes the CLI safe to drive
> from an AI agent: it never blocks on a prompt the agent cannot see, and a missing value comes back
> as a named flag the agent can supply on the next run.

### 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, or a one-time-code / sign-in throttle — **stop and wait**; retrying extends the block |
| `6` | Network / TLS / DNS | Couldn't reach the server |
| `7` | Business not selected | The account has several businesses and none is active — run `atoa business use <id>` |
| `8` | Plan limit | The add-on plan doesn't allow this — `atoa addons list` shows the limits |
| `9` | One-time code sent | Not a failure: a code was sent and nothing was written. Re-run with `--otp <code>` |

```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 "Throttled — wait it out; retrying extends the block" ;;
    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. They authenticate with the **SDK key**, so run `atoa keys create` first:

```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.

### 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`.

#### 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.

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`).

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:

**bash**

```bash theme={null}
atoa completion bash >> ~/.bashrc && source ~/.bashrc
```

**zsh**

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

**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'
```

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

> **Authentication & account setup**
>
> | Command | Purpose | Auth |
> | - | - | - |
> | `atoa login` | Pair the CLI with an account via the browser | — |
> | `atoa logout` | Clear a profile's login session (`--purgeKey` 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 |

> **Business, sessions & SDK keys**
>
> | 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 |

> **Team, KYB & bank accounts**
>
> | Command | Purpose | Auth |
> | - | - | - |
> | `atoa staff list` | List staff members | login |
> | `atoa staff add` | Add a staff member (`--role`, `--store`) | login |
> | `atoa staff invite` | Alias of `staff add` | login |
> | `atoa staff update [<userId>]` | Edit name, email, phone, role or permitted stores | login |
> | `atoa staff delete [<userId>]` | Remove a staff member from the business | login |
> | `atoa roles list` | List available roles | login |
> | `atoa roles permissions` | Permission catalogue — the ids `--permission` takes | login |
> | `atoa roles add` | Create a role (`--permission`, repeatable, takes ids) | login |
> | `atoa roles update [<roleId>]` | Edit a role's name, description or permissions | login |
> | `atoa roles delete [<roleId>]` | Delete a role | login |
> | `atoa kyb status` | KYB verification status | login |
> | `atoa kyb link` | Open the KYB form in the browser (prints the URL too) | login |
> | `atoa kyb card status` | Card-payment application status and what blocks it | login |
> | `atoa kyb card link` | Open the card-payment application form | login |
> | `atoa bank list/get/add/delete` | Manage business bank accounts | login |

> **Add-on plans**
>
> | Command | Purpose | Auth |
> | - | - | - |
> | `atoa addons list` | Current plan, feature usage, and available moves | login |
> | `atoa addons upgrade [<planId>]` | Move to a higher plan | login |
> | `atoa addons downgrade [<planId>]` | Move to a lower plan (refused if usage exceeds it) | login |
> | `atoa addons cancel-downgrade` | Cancel a scheduled downgrade | login |

> **Direct Debit**
>
> | Command | Purpose | Auth |
> | - | - | - |
> | `atoa direct-debit status` | Whether a mandate exists, and its status | login |
> | `atoa direct-debit setup` | Set up the subscription mandate (one per business) | login |

> **Google Business listing**
>
> | Command | Purpose | Auth |
> | - | - | - |
> | `atoa google search "<text>"` | Find Google listings by name and address | login |
> | `atoa google link --store <id>` | Attach a listing to a store (`--search`, `--placeId`) | login + add-on |
> | `atoa google locations` | Listings linked to this business | login + add-on |
> | `atoa google unlink --store <id>` | Remove a store's listing | login + add-on |

> **Notifications & branding**
>
> | Command | Purpose | Auth |
> | - | - | - |
> | `atoa comms list` | Notification topics and each channel's state | login |
> | `atoa comms set <topic>` | Toggle `--email`/`--sms`/`--push` for a topic | login |
> | `atoa custom-branding get` | Current checkout theme colour | login |
> | `atoa custom-branding set <hex>` | Set the checkout theme colour | login |
> | `atoa custom-branding reset` | Restore the Atoa default colour | login |
> | `atoa custom-sms list` | Custom SMS sender name and review status | login |
> | `atoa custom-sms set <name>` | Request a custom SMS sender name | login |
> | `atoa custom-sms delete` | Remove the custom sender name | login |

> **Payment links**
>
> | Command | Purpose | Auth |
> | - | - | - |
> | `atoa payment-links create` | Create a hosted checkout link (`--amount`, `--storeId`) | login |
> | `atoa payment-links get <id>` | Fetch a link and its status (`--storeId`) | login |
> | `atoa payment-links delete <id>` | Delete a link (`--storeId`) | login |

> **Profiles**
>
> | 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 |

> **Payments**
>
> | 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 add` | Create a store (login) |
> | `atoa stores update [<id>]` | Edit a store, prefilled with its current values (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) |

> **Customers, Cards & Card-on-File**
>
> | 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 |

> **Refunds**
>
> | 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 |

> **Webhooks**
>
> | 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 |

> **Bank Feed (Open Banking)**
>
> | 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 |

> **Payouts**
>
> | Command | Purpose |
> | - | - |
> | `atoa payouts list` | List payouts (date / status / search filters) |
> | `atoa payouts transactions <id>` | Transactions for a single payout |

> **Utilities**
>
> | Command | Purpose |
> | - | - |
> | `atoa institutions list` | List supported banking institutions |
> | `atoa get/post/delete` | Raw HTTP requests against any API path (SDK key) |
> | `atoa completion <shell>` | Print the shell-completion bootstrap |
> | `atoa --help` | Top-level help; works on every subcommand too |

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

> **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).

> **'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
> ```

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

> **`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.

> **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.

> **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
> ```

***

## Need Help?

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


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.