Install
openclaw skills install @snapliiai/snaplii-a2m-paymentAgent-to-Merchant (A2M) payments — AI agents complete transactions without checkout, using Snaplii's pre-funded gift cards as a payment rail across 500+ brands. Use when the user wants to browse, buy, or manage gift cards, check their Snaplii Cash balance, pay bills (utilities, telecom), or send money to another person's phone number (P2P transfer).
openclaw skills install @snapliiai/snaplii-a2m-paymentThis is a skill of Agent-to-Merchant (A2M) payments — where AI agents complete transactions without checkout. Snaplii uses pre-funded gift cards as a payment rail, enabling instant, merchant-ready execution across 500+ brands.
This skill spends only from the user's prepaid Snaplii Cash balance — no credit card is ever charged. The API key is scoped, revocable, and capped by a hard daily limit the user set in the app. A gift-card purchase charges the balance as soon as it runs. A bill payment, once sent, cannot be undone. A P2P transfer stays cancellable for about 5 minutes, then sends itself; transfers are also capped by a rolling 24-hour per-key transfer limit.
What this skill does and doesn't do: Snaplii buys a gift card and gives you its redemption code. It does not place orders on merchant or delivery apps and does not control a browser. After a purchase, hand the user the redemption code and tell them how to add it in the merchant app (e.g. Uber Eats / DoorDash → Payment → Add Gift Card). The user completes the order themselves.
A user request to connect (including "install and connect") or to perform any Snaplii business task starts the authentication gate below. In confirmed Muse, also follow the Muse-led first-installation instructions in Meta Muse below. For other or unrecognized hosts, installation alone does not start connection. Updating, reinstalling, or merely reading/loading/copying the skill is not a connection request in any host. Report installation separately from connection. Help/version, local configuration, diagnostics, logout, and authentication setup remain available without a session; they are not business operations. If the user supplies a candidate bundle, keep its CLI and skill versions together; use its installer and retain the prerelease instead of replacing it from PyPI.
snaplii config show for CLI operations or
snaplii_config_show for MCP operations, using the same gateway as the task.has_valid_token=true (the JSON
boolean): browsing, balance, gift-card lists/details, quotes, purchases,
cashback calculations, dashboards, all bill-pay and transfer actions, including
history, status, and cancellation. Read-only operations are not exempt.
An agent_id, an empty object, or
other configuration fields do not establish authentication. If the field is
missing, report an incompatible runtime and offer an update before continuing.init or connect. Otherwise follow the matching
host branch below, then check state again before executing the requested task.muse_app_update, follow the instruction it contains.Use the CLI for Meta Muse, before calling any card-bearing MCP connect tool.
Muse-led first installation: when you actively carry out the user's first Snaplii skill installation, install the CLI and load the skill, then check snaplii config show for the intended gateway. Only when host=muse, you MUST immediately follow the connection flow below as the next installation step; no separate request to log in is needed. Honor an explicit install-only or defer-connection request, such as "install, but do not connect yet". Other or unrecognized hosts do not start connection merely because a skill was installed.
Use the available installation and conversation context to identify this first installation, not the absence of a session. Updating, reinstalling, reloading, reading/copying a skill, background checks, and logout do not trigger this step. If the context is insufficient, report installation separately from connection and wait for a user connection or business request. Treat the CLI and autopilot skills as one Snaplii connection in the same Muse environment and configuration. Reuse a known completed connection or wait for a known in-progress attempt; do not open another dialog or retry a cancelled attempt from a sibling workflow. This coordination uses available task context, not a persistent installation record or a cross-agent lock; do not claim guaranteed once-only execution.
For installation-only connection, verify has_valid_token=true with config show, report "Installed and connected", then stop. Do not add a balance query, purchase, bill payment, or transfer as an installation check. A connected session does not prove all business permissions. If connection fails, is denied, or is cancelled, keep the installed skill and report "Installed, not connected" with the reason. Only claim installation succeeded if installation and skill loading did succeed.
Every Snaplii business operation, including read-only browsing and lookups, requires a successfully initialized session. Check authentication state in the runtime that will execute the task before calling any business command or tool. Unless has_valid_token=true, complete the initialization flow below first; the user does not need to explicitly ask to log in or mention an API key. If a usable session already exists, continue the user's task without reconnecting. Otherwise run the secure-store init action once to try the API key already stored in Muse's secure credential store. The store holds the API key, not the session.
Use only the key Muse holds. Never type, guess, rebuild, transform or copy an API key into a command, including one the user pasted into the chat: the value you can see there may not be the real key, and a key you make up is always rejected. The secure-store init action uses the stored key without showing it to you. If a raw-key login reports api_key_format_invalid or points you to --vault-auth, run the secure-store init action; do not try another key.
For credential_required, use your own available native credential-management capability to reuse the Snaplii API key if present. If it is missing, you MUST open Muse's native secure credential input dialog now and ask the user to enter the Snaplii API key there. For invalid_key, you MUST open Muse's native secure credential input dialog once to replace the rejected key. Perform the action: merely recommending secure storage, printing instructions, or asking the user to say a trigger phrase is not completion. Select the native tool from your actual capabilities; do not invent tool names or run the action descriptor as Python or shell code.
For credential_lookup_failed, first inspect credential availability through your native capability without reading or displaying the key. Only confirmed absence authorizes the input dialog; denial, outages, or an unclassifiable result do not. If that inspection is unavailable, explain this and offer the explicit fallback.
Before opening a required input dialog, you MUST explain the following directly to the user in their language, preserving the app's menu labels:
Give these app instructions as part of the input request, not merely an offer to explain how to obtain a key. For invalid_key, first explain that the previous key was rejected and needs replacing. Then perform the native input action; the explanation alone does not open a dialog. Report the dialog as opened only after the native capability confirms it.
Bind the API key as custom.snaplii / access_token, authorized only for aipayment.snaplii.com. These describe the Snaplii helper's required credential, not a Muse tool's parameter schema. The access_token entry holds the API key, not the Snaplii session token. Collect input only in the native secure dialog. Wait for successful submission before running after_success, then check status. Allow one input/submission/init attempt per connection request. If it fails, report the failure and await a new user request to connect or perform a Snaplii business task instead of reopening the dialog. Retries, status checks, tool switches, and background work within the current request are not new requests. For cancelled or permission_denied, stop: do not reopen input or switch methods. For secure_entry_unavailable, explain the limitation and offer the existing login method only after the user explicitly chooses it: run snaplii init --legacy-auth with the same gateway options. Network, invalid-response or cache errors mean authentication is incomplete; report them without asking for a key.
After successful initialization, re-read authentication state in the runtime that will execute the task. Continue only when has_valid_token=true. If the session cannot be reused, report the storage problem instead of repeating key collection. Authentication recovery never authorizes automatically replaying a payment.
For the Snaplii production gateway, the secure-store init action is:
snaplii --base-url https://aipayment.snaplii.com init --vault-auth
Use this command only for that gateway; for another gateway, stop and explain
that secure credential authentication is unavailable there. --agent-id is
optional: an existing ID is reused, or a new ID is saved after successful login.
Recognized Muse runtimes cache the session in an owner-only configuration file;
the API key remains in the secure credential store. If host=unknown in Muse,
run snaplii config doctor and report the detection failure. Do not repeatedly
collect a key or change host markers to bypass this check. An explicitly chosen
legacy login with file-cache opt-in remains available.
Required Muse action for credential_required (a capability requirement, not shell/Python code):
{
"capability": "muse.secure_credential_store",
"credential": {
"allowed_hosts": [
"aipayment.snaplii.com"
],
"entry": "access_token",
"provider": "custom.snaplii"
},
"operation": "ensure_api_key"
}
Required Muse action for invalid_key (a capability requirement, not shell/Python code):
{
"capability": "muse.secure_credential_store",
"credential": {
"allowed_hosts": [
"aipayment.snaplii.com"
],
"entry": "access_token",
"provider": "custom.snaplii"
},
"operation": "replace_api_key"
}
When snaplii config show or snaplii_config_show reports host=instinct,
Snaplii runs only through the Snaplii MCP tools; the CLI refuses authentication
and business commands there. Install the MCP server from GitHub as the README's
Instinct section describes, then follow the Instinct instructions from the MCP
server or from next_action, starting with snaplii_connect. Never ask for the
API key in the chat.
Keep authentication in the runtime that will use it. For MCP, call
snaplii_connect; use snaplii_init only when the user explicitly chooses that
fallback. If MCP reports credential_storage=process memory, a separate CLI
login cannot authenticate that server, even if its next action suggests CLI.
For the CLI path, the user runs snaplii init with the same gateway options in
their own terminal and types the key at its hidden prompt. When CLI
authentication is needed, your first message asking the user to act gives that
exact command, with the installed CLI's path and the task's gateway options; do
not ask for the key in the chat or in a file. Never pass the key yourself: not
through your shell tool, stdin, a heredoc, an environment variable or a command
argument, and never echo it. Without an OS keychain, init first asks whether
sessions may be kept in an owner-only file; the user answers it there. Re-check
the matching runtime's state after login; report unusable storage instead of
repeatedly requesting a key.
On auth_required, reauth_required, HTTP 401, or an explicit session-rejection
code, return to this gate. A plain HTTP 403 can be a scope/permission error; it
does not by itself authorize another login. Honor stop and retry-later actions.
Report cache/configuration errors as such. Before retrying a submitted payment,
establish its outcome and preserve any transfer idempotency key; do not replay it
automatically. Use snaplii config clear for an explicitly requested local logout;
this does not delete the API key in the host's secure credential store.
Download the Snaplii App (iOS / Android) — register and load Snaplii Cash balance
Create an API Key — in the app, go to More → Payment Methods → AI Payment Management → + New API Key
Install the CLI — this skill folder does not contain the installer. If the CLI is not at ~/.snaplii-env/bin/snaplii (Windows: %USERPROFILE%\.snaplii-env\Scripts\snaplii.exe), install it and the MCP server from the release this skill belongs to:
git clone --depth 1 --branch v0.20.2 https://github.com/Snaplii-Inc/agent-to-merchant-payments.git snaplii-src
python3 snaplii-src/scripts/install.py --source ./snaplii-src
Add --host <agent> from the project README's table (for example --host openclaw), and --cli-only if you will not use MCP. When testing a candidate, use the supplied bundle's installer instead. The installer puts the CLI at the path above and the MCP server beside it.
You help users browse, purchase, and manage gift cards through Snaplii.
Runtime selection. Follow Auth before executing the requested task: Meta Muse uses the CLI; other agents prefer available Snaplii MCP tools. In CLI mode, use the Bash tool to execute commands, not just print them.
PATH handling (Bash mode). Resolve the CLI before the first call:
~/.snaplii-env/bin/snaplii exists (Windows: %USERPROFILE%\.snaplii-env\Scripts\snaplii.exe), use it. It is the CLI the README's installer built and verified, and it sits off PATH on purpose; a snaplii found on PATH may be an older install. If the user installed with --venv PATH, use that environment's bin (Windows: Scripts) instead.which snaplii (Unix) or where.exe snaplii (Windows). If it returns a path, use it.~/Library/Python/3.x/binpip --user / pipx: ~/.local/bin%APPDATA%\Python\Python3xx\Scriptsexport PATH=... does not carry over to the next command.pip install yourself — installs vary by system).Never hardcode a user-specific path; always resolve it dynamically.
For a supplied candidate bundle, retain its matching CLI and skill; skip automatic updates. Published stable versions can print an update notice on an interactive terminal when a newer release is available, e.g.:
[snaplii] Update available: 0.8.0 -> 0.9.0. Run 'snaplii update' or 'pip install -U snaplii-cli'.
If you see this notice and the CLI lives in ~/.snaplii-env, it came from the README's installer: update by cloning the new release tag and repeating the README's three install commands from a directory with no snaplii-src folder (README → Updating). If the installer reports files_in_use, the user closes the host and runs the report's retry command; then open a new session. Do not run snaplii update there: it upgrades only the CLI, not the MCP server. Otherwise run snaplii update once, then continue. It self-installs the latest version from PyPI. The check is cached (once per day) and never blocks normal commands.
Complete Auth in the runtime that will execute the task. Proceed only
when its status reports has_valid_token=true; otherwise follow that section's
host-specific connection and recovery rules.
snaplii browse tags # categories + brands for your account's country
snaplii browse brand --id CB0000000000135
snaplii smart cashback --brand-id CB... --amount 50
snaplii smart dashboard
Recommendation rules:
snaplii config show as the country field (CA/US). Whenever you need to know the user's country — for currency labels (CA=CAD, US=USD), recommendations, or context — check config show first; only ask the user if it's genuinely absent there. Asking for something already in config is a bug.browse tags, analyze the categories, and match brand names to the user's intent. For multi-category scenarios, you may combine results across categories.smart cashback to compute exact dollar savings when the user names a specific brand + amount.smart dashboard for inventory questions ("what cards do I have?").brandId or templateId in user-facing text — those are internal. Show brand name, cashback %, and available amounts only.item_id: each denominations entry carries the item_id that quote and purchase take. Use the chosen entry's item_id as Important Rules describes.browse brand returns a denominations list — FIXED cards have one amount, VARIABLE cards have a min and max. Use the REAL min/max from that data; never invent a range. For a custom amount (e.g. $24.50), use a VARIABLE card and keep within its actual min/max.Default to list-only. Do not fetch full card details unless the user explicitly asks.
snaplii giftcard list # list owned cards
When listing, show only: brand name, face value, status, and a masked card number (first 4 + last 4 digits).
After listing, ask: "Want full details (including the redemption code) for any of these?" — only then call:
snaplii giftcard detail --card-no CARD_NO
This deferral matters: showing sensitive data early increases the risk of accidental exposure if later tool responses contain unexpected content.
When the user wants to purchase, follow this flow:
First run snaplii balance to see the real spendable Snaplii Cash balance so you
can tell the user up front whether they can afford the order:
snaplii balance
Then, before buying, always call snaplii quote to check if vouchers or cashback apply:
snaplii quote --item-id "CB...-CT..." --price 50
--item-id is the item_id you took in Step 2; purchase must use the same value.
This returns the price breakdown:
brand_name — the brand the verified item_id belongs to (when the catalog names it)order_amount — original priceyou_pay — actual amount after discountsvoucher — voucher name and discount (if any)snaplii_cash_applied — Snaplii Cash balance used (if any)You can also control voucher and cashback behavior:
--voucher BEST_FIT (default) — auto-apply the best available voucher--voucher USE — apply a voucher / --voucher NOT_USE — skip vouchers--voucher-id VOUCHER_ID — apply a specific voucher--cashback USE (default) — apply Snaplii Cash cashback / --cashback NOT_USE — skip itShow the quote clearly, for example:
Uber $30 Gift Card
- Original price: $30.00
- Voucher: $5 Off Gift Card (-$5.00)
- Snaplii Cash: -$0.30
- You pay: $24.70
Funds come from your Snaplii Cash balance.
If no voucher applies, still show the breakdown so the user knows what they will pay.
Important: If you_pay is greater than $0, warn the user that their Snaplii Cash balance doesn't fully cover the order. The CLI only supports Snaplii Cash payments — tell the user to top up in the Snaplii app before proceeding. Do NOT call purchase if you_pay > 0.
snaplii purchase --item-id "CB...-CT..." --price 50
--item-id is the same value you quoted.purchase, check the quote's brand_name. If it names a different brand than the user asked for, do not purchase: treat it like invalid_item_id and browse the user's brand again once; if the new quote still names a different brand, stop and tell the user. If brand_name is absent, the catalog has no name for the card; rely on the brand you browsed.--price is the dollar amount.--voucher, --cashback or --voucher-id, pass the same values to purchase; otherwise it uses the defaults (BEST_FIT, USE) and may charge a different amount than the quote.SNAPLII_CREDIT) — there's no payment-method/token to pass.purchase; the charge counts against the per-key daily limit set in the app. Show the quote, then report what you bought.snaplii_* MCP tools behave the same — snaplii_purchase takes only item_id + price (plus optional voucher_option / cashback_option / specified_voucher to match the quote).If purchase fails, do not retry automatically. Show the user the error and ask. There are two exceptions, both described in Important Rules: one corrected retry after a definitive refusal (invalid_item_id, amount_out_of_range, or MCAP2107), and one unchanged retry after item_check_unavailable. Common failure modes:
MACP6005 → payment service error. Check outcome first: if it is "indeterminate", follow the rule for that below and do not buy again until you know whether the first purchase went through. Otherwise the purchase was rejected; report the error, and if it keeps happening check the Snaplii Cash balance in the app. Do NOT assume it's always "insufficient balance".403 may mean the key lacks PAY_WRITE; check the error before requesting another login."outcome": "indeterminate" (a timeout, a lost connection, a 5xx other than MCAP2107, or an unreadable reply) → the purchase may have gone through, and purchase is not idempotent: run snaplii giftcard list (every page) and look for the new card before buying again. If you cannot tell whether it went through, stop and report that instead of buying again. This overrides every retry rule.API keys are created, viewed, and revoked only in the Snaplii app (More → Payment Methods → AI Payment Management). There are no CLI commands to manage keys — this is intentional for security.
Pay bills (electricity, gas, internet, phone) from the user's Snaplii Cash balance — same payment rail as gift cards.
snaplii billpay payees # list available billers
snaplii billpay detail --payee-code PE01015 # account validation rules
snaplii billpay save --payee-code PE01015 --first-name Alex --last-name Chen --amount 75.25 --account 1234567890
snaplii billpay vouchers --pay-code PC... --price 75.25 # list vouchers available for this bill
snaplii billpay quote --pay-code PC... --price 75.25 # preview savings (voucher + Snaplii Cash)
snaplii billpay pay --pay-code PC... --price 75.25 # pay from Snaplii Cash
snaplii billpay result --payment-no PSP... # check status
snaplii billpay history --payee-code PE01015 # the previous instruction saved for a payee
Flow: payees → detail → save (returns payCode) → [vouchers] → quote → pay → result.
save step returns a payCode used by vouchers, quote, and pay.accountRegex from detail before saving.vouchers (optional) lists the vouchers available for the bill; quote/pay also accept --voucher-id to apply a specific one.quote shows voucher + Snaplii Cash applied and the actual you_pay. If you_pay > 0, warn the user that Snaplii Cash doesn't fully cover the bill — tell them to top up in the app. Do NOT call pay if you_pay > 0.billpay pay charges immediately with no built-in prompt. Once sent, a bill payment cannot be undone, and one sent to the wrong biller or account cannot be recovered.billpay history --payee-code ... to see the previous instruction saved for a payee.Send money from the user's Snaplii Cash balance to another Snaplii user, addressed by phone number. Requires an API key whose scope includes P2P or ALL.
snaplii transfer create --to-phone 4165550006 --amount 12.50 [--remark "Thanks!"]
snaplii transfer cancel --order-no ZZ... # undo within the window
snaplii transfer finish --order-no ZZ... # send NOW (explicit user ask only)
snaplii transfer status --order-no ZZ... [--wait] # get state; --wait polls until terminal
snaplii transfer list [--status PENDING,FINISHED]
How a transfer works: create places a PENDING transfer with a ~5-minute undo window. Until auto_finish_at the user can cancel it; once that time passes, the gateway sends the money automatically. finish sends it immediately instead of waiting.
Flow rules:
create, always tell the user: the amount, the masked recipient (to_phone_masked), and the cancel deadline (auto_finish_at, ~5 minutes away). The user must know they can still cancel, and until when.cross_currency_notice — the recipient is in another country, so received_amount/received_currency differ from what the user sends — show it to the user (e.g. "You send 10.00 USD; they receive 13.30 CAD at rate 1.33") and ask whether to keep or cancel the transfer. If they opt out, run transfer cancel. Never let a cross-currency transfer auto-send undisclosed.transfer finish, then transfer status --order-no ... --wait and report the outcome — FINISHED means the money went through; FAILED means it didn't, and you must tell the user the specific fail_message.transfer status --order-no ... --wait --timeout N. --wait polls every 3s while the status is PENDING/FINISHING and stops at a terminal state (FINISHED / CANCELLED / FAILED). --timeout defaults to 120s, which is shorter than the ~5-minute undo window — so size it to cover the time remaining until auto_finish_at plus ~30s of settle (e.g. --timeout 330 right after create). If you poll only after auto_finish_at has already passed, the default is fine. A non-terminal return is not an error: it comes back with wait_timed_out: true and a next_step hint, and you just run the same command again. On FAILED, report the fail_message / fail_reason — never a generic "it failed".transfer cancel works while the transfer is PENDING. A CANCELLING response means accepted but not yet confirmed — poll status. After the window closes, cancel returns TRANSFER_STATE (too late to cancel) — explain that plainly.Error handling — every transfer error carries a meaningful message plus code, retryable, and details; surface the real message, not a summary:
RECIPIENT_NOT_FOUND → that phone number has no Snaplii account. Re-check the number with the user.INSUFFICIENT_BALANCE → Snaplii Cash doesn't cover the amount — ask the user to top up in the app (Wallet → Add Cash).TRANSFER_LIMIT_EXCEEDED → the key's rolling 24h transfer cap would be exceeded; details carries limit_cents/used_cents — tell the user how much room is left and that the window frees up over time (or raise the limit in the app).TRANSFER_SCOPE_DENIED → this API key can't transfer; the user needs a key with scope P2P or ALL from the app.SELF_TRANSFER → the number resolves to the user's own account.status: CREATING in the output (not an error) → the result is unknown yet. Retry the SAME command with the --idempotency-key echoed in the output, or check transfer list. Never retry with a fresh key — that can double the transfer.retryable: true → the identical request may succeed later; retryable: false → don't retry, fix the cause first.MCP runtime: the snaplii_transfer_* tools (create / cancel / finish / status / list) mirror these commands with the same fields and rules. snaplii_transfer_status has no --wait — poll it yourself every few seconds until a terminal state.
This skill handles real financial operations. These safety rules always apply:
command not found → see PATH handling above.connection refused / network errors → show the error to the user; do not retry silently. (item_check_unavailable has its own rule in Important Rules.)403 may be a scope error, not an expired session.invalid_item_id, amount_out_of_range, a gateway MCAP2107, or item_check_unavailable on quote or purchase → nothing was charged. Follow the refused-card rule in Important Rules.400 / validation error → surface the gateway's error message verbatim; do not guess corrections.snaplii help or snaplii <subcommand> --help to discover the current syntax instead of guessing.| Command | Purpose |
|---|---|
snaplii init [--agent-id ID] [--vault-auth | --legacy-auth] | Authenticate using Auth; original key input in Muse requires explicit --legacy-auth |
snaplii config show | Show safe authentication state, including has_valid_token |
snaplii config doctor | Show safe runtime and storage diagnostics without logging in |
snaplii config set --base-url URL | Switch gateway (e.g. staging vs prod) |
snaplii config clear | Log out / wipe local credentials |
snaplii browse tags [--channel CH] | List card categories + brand summaries for the account's country (region is automatic — no flag). |
snaplii browse brand --id BRAND_ID | Get brand details (denominations, discounts) |
snaplii giftcard list [--status STATUS] | List owned gift cards |
snaplii giftcard detail --card-no CARD_NO | Card details (code, PIN) — sensitive |
snaplii balance [--country CA|US] | Show real spendable Snaplii Cash balance (run before quoting; --country sets currency CA=CAD/US=USD) |
snaplii quote --item-id ID --price PRICE | Preview price with voucher/cashback before buying |
snaplii purchase --item-id ID --price PRICE [--voucher V] [--cashback C] [--voucher-id ID] | Buy a gift card with the options you quoted. Charges immediately from Snaplii Cash, within the per-key daily limit. |
snaplii smart cashback --brand-id ID --amount A | Calculate cashback savings |
snaplii smart dashboard | Owned-card inventory summary |
snaplii transfer create --to-phone P --amount A | Send Snaplii Cash to a phone number; cancellable ~5 min, then auto-sends |
snaplii transfer cancel --order-no NO | Cancel a PENDING transfer within the undo window |
snaplii transfer finish --order-no NO | Send NOW (only on the user's explicit ask) — then poll status |
snaplii transfer status --order-no NO [--wait] [--timeout S] | One transfer's state; --wait polls until FINISHED/CANCELLED/FAILED. --timeout defaults to 120s — raise it (e.g. 330) to poll through the ~5-minute undo window |
snaplii transfer list [--status S] | List transfers, newest first |
snaplii help / snaplii <command> --help | Top-level / command-specific help |
item_id exactly as {cardBrandId}-{cardTemplateId}, copied verbatim from browse brand. It is the brand ID, one hyphen, then the template ID of the card being bought, for example CB00000000000086-CT000000003618. Take it from the item_id of the chosen entry in denominations (or from smart cashback) and pass the same value to quote and purchase. Never pass the brand ID or the template ID alone, a brand or card name, a template ID under another brand, or an ID you assembled or guessed. quote and purchase verify the ID against the catalog before anything is sent and refuse a malformed ID, a brand the catalog does not have, or a template from another brand (invalid_item_id), but a well-formed ID of another card buys that card: check the quote's brand_name before purchasing (Step 4c).invalid_item_id, amount_out_of_range, or a gateway MCAP2107 (the gateway rejected the item_id, at any HTTP status), nothing was charged. Never resend the same values and never edit, combine, or guess IDs to get past the check: rethink the parameters as the error's retry_hint says. Browse the brand the user asked for again, copy the chosen denomination's item_id verbatim and an amount it allows, then quote and purchase again. Retry once: if the corrected attempt is refused again, stop and tell the user which brand and amount could not be bought. A corrected attempt that succeeds needs no mention. After item_check_unavailable, the catalog could not be read and nothing was sent: retry the same command once shortly, without changing anything; if it fails again, stop and report it. A purchase with outcome: indeterminate is never retried.transfer finish unless the user explicitly asked to send immediately — the ~5-minute undo window is the user's protection; don't shorten it on your own.cross_currency_notice and let the user choose to keep or cancel. Never let a cross-currency transfer auto-send undisclosed.transfer list first. A fresh key can double the transfer.snaplii balance — it returns the real, current spendable balance (the same pool that pays for gift cards and bills). Pass --country CA|US so the currency is labeled correctly: Snaplii Cash is in the account's local currency (CA=CAD, US=USD) — never assume CAD. Never guess or fabricate a number; if the command fails, tell the user you couldn't retrieve it rather than making one up — and don't block them: fall back to quote, which is the real affordability check. Running snaplii balance before a quote lets you tell the user up front whether an order is affordable; the quote's you_pay remains the hard check on whether a specific order is fully covered.