@clink-ai/clink-cli
v0.2.32
Published
TypeScript CLI for Clink customer wallet APIs
Downloads
375
Readme
clink-cli
clink-cli is a command-line client for Clink customer wallet APIs.
Current source package version: 0.2.32.
Install
Recommended setup installs the latest npm CLI globally and synchronizes the official
agentic-payment-skills package:
npx @clink-ai/clink-cli@latest installTo install only the CLI without synchronizing Skills:
npm install -g @clink-ai/clink-cliRun without installing globally:
npx @clink-ai/clink-cli --helpPublished package name:
@clink-ai/clink-cliInstalled commands:
clink-cli
clinkclink is a shorter alias that invokes the same CLI as clink-cli.
Update both the npm CLI and the official Skill later with:
clink updateUse clink update --check to check versions without modifying installed CLI or Skill state, or
clink skills sync to repair only the official Skill. These are explicit operations: npm
postinstall never downloads Skills, and ordinary wallet/payment commands do not perform Skill
synchronization.
Requirements:
- Node.js
>=20 - Access to the target Clink API environment
Build Editions
The repository keeps one source tree on main and produces two vendored bundles from the same
commit:
npm run bundle:main
npm run bundle:visaThe outputs are:
dist-bundle/main/clink-cli.bundle.mjs
dist-bundle/visa/clink-cli.bundle.mjsThe main Edition exposes the standard wallet and payment commands, including wallet init, and
rejects the visa command. The Visa Edition exposes the same standard commands plus visa init,
visa recommend, visa commerce-run, and the other Visa subcommands. src/index.ts is the main Edition entry point;
src/visa-index.ts is the Visa Edition entry point. The two entry points create separate esbuild
dependency graphs, so the main bundle does not include src/visa/*.
The published @clink-ai/clink-cli command uses the main Edition. The vendoring workflow builds
both Editions from main: the main Edition is synchronized to agentic-payment-skills, and the
Visa Edition is synchronized to visa-skill.
Default base URL:
https://api.clinkbill.comQuick Start
Every command starts with:
clink-cli <command> [subcommand] [options]1. Initialize your wallet
Run this once to authorize the CLI and save OAuth credentials locally:
clink-cli wallet init --email [email protected]For sandbox (UAT), use --sandbox; for test (*.clinkbill.dev), use --test. The flags are
mutually exclusive. They select the wallet environment on wallet init and independently select
the public environment on Catalog discovery commands and tool internal-ucp get-merchant-list;
other commands reject them. Their URLs come from src/domains.ts.
The main distribution selects production without either flag or a custom base URL override; a
packaged distribution may supply a fixed wallet-init environment and reject conflicting flags. A
successful initialization saves the selected environment, so later authenticated commands do not need an environment flag.
The CLI always prints the browser URL. It opens the
URL when --open is passed or default-open-links is enabled (the saved default is false);
--no-open overrides both and is suitable for agents that should hand the URL to the user. If the
browser cannot be opened, the CLI prints a warning and continues polling; open the displayed URL
manually on any device. Complete email verification and confirmation in the browser while the CLI
polls for tokens. Existing customers keep their server-side name. For a new customer, the CLI uses
the email text before @ as the initial name; --name is no longer accepted. Use
clink-cli config set name <name> to change the local name afterwards. The resolved name
is always saved to local config.
The browser URL keeps only user_code in the query string. The supplied email and resolved name are
carried in the URL fragment, which the Portal reads and immediately removes from the address bar.
clink-cli wallet init --sandbox --email [email protected]
clink-cli wallet init --test --email [email protected]After authorization succeeds, wallet init also refreshes local cached payment methods from the card
setup-link endpoint. If that refresh fails, initialization still succeeds and the output marks
paymentMethodsCached: false. Its returned bindingUrl keeps only the Portal origin and appends the
locally stored email, so a signed-out browser can prefill login without exposing other backend URL
parameters. Card binding/setup/modify commands apply the same rule.
Access and Refresh Tokens are stored in the root authorization object in
~/.clink-cli/config.json. The CLI refreshes shortly before Access Token expiry, and atomically
replaces both tokens because the server rotates the Refresh Token on every refresh. The saved
authorization also records its issuer origin; the CLI never sends either token to another origin.
If an OAuth-authenticated Agent API request returns 401, the CLI refreshes and retries that
request once. A second 401 and all 403 responses are returned without another refresh.
wallet init also registers an Agent Client using a stable SHA-256 device identity. The CLI reads
IOPlatformUUID with /usr/sbin/ioreg on macOS, MachineGuid from the 64-bit registry view on
Windows, and /etc/machine-id with /var/lib/dbus/machine-id as the Linux fallback. The same
platform-specific path is used in containers; the CLI does not detect or special-case container
environments. There is no CLINK_DEVICE_ID override. Hostname and OS release are sent only as
optional troubleshooting metadata, and dry-run output redacts stable device identifiers.
Check the saved local config:
clink-cli wallet status --format prettyLog out and remove local credentials:
clink-cli wallet logoutLogout also removes the saved customerId, payment-method cache, and risk-rule
cache so the next login can bind a different customer safely. Email and display
name preferences are retained.
The local config file is stored at:
~/.clink-cli/config.jsonVisa benefits (Visa Edition)
Sign in through the direct VSRP OAuth page and create or reuse the Clink customer identified by Visa's trusted profile email:
clink-cli visa init --sandbox --openvisa init calls CWallet's Benefit authorization endpoint with clientId=clink-cli, a stable UUID
v4 installation identity, and the same Agent Client device metadata used by wallet init. The
Agent Client device ID is SHA-256 derived from macOS IOPlatformUUID, Windows MachineGuid, or
Linux machine-id without container-specific handling. The CLI then polls until the server-provided
expiresIn elapses; a missing or invalid value falls back to ten minutes. It does not open a Clink
landing page. Every explicit visa init starts a fresh Benefit OAuth transaction and replaces any
locally pending transaction instead of reopening an earlier authorization URL.
Agent hosts should use the recoverable form:
clink-cli visa init --sandbox --start --open --format json
clink-cli visa init --sandbox --resume <resume-id> --format json--start creates or reuses one pending transaction, attempts the system browser, and returns
manualOpenUrl, browserLaunch, expiresAt, and resumeCommand immediately. --resume probes
that exact transaction once; a pending response returns the same command, while success atomically
persists the authorization. These modes never expose deviceCode or OAuth state, never create a
background poller, and cannot overwrite a newer login or authorization.
The resulting Clink Access and Refresh Tokens replace the same root authorization used by
wallet init; the trusted email returned beside the Benefit Token response is saved as the root
email used for buyer.email during checkout. The returned agent_client_id and
visa_registration_status are saved as authorization.agentClientId and
authorization.visaRegistrationStatus; only SUCCEEDED means Visa registration completed. The
email is not embedded in the Access Token.
The CLI never creates a second local Clink Token set. A same-customer login replaces the previous
authorization and best-effort revokes its old Refresh Token family. A different customer is
rejected instead of silently switching accounts.
Discover offers:
clink-cli visa recommend "Visa权益有哪些" --anonymous --format pretty
clink-cli visa recommend "日本餐厅优惠" --region jp --category dining_restaurant
clink-cli visa recommend --personalized "我的卡能用什么"
clink-cli visa detail P2025110009
clink-cli visa taxonomyBroad all-offer requests and --all fetch the VSRA API in 50-item pages and aggregate every
Program. Explicit --limit or --page keeps ordinary paging. --personalized preserves the
separate VSRA Device Flow; its market Token and recoverable FSM state live under visa in the same
config file, while Clink OAuth remains only at root authorization. visa status reports both
login states without printing Tokens, OAuth state, device codes, or profile data.
VSRA defaults to the production market hosts. Development overrides remain available through
VSRA_ENV=test|staging|local, VSRA_BASE_URL, or VSRA_<MARKET>_BASE_URL.
Use the same foreground command for bounded preparation or a frozen purchase. Login-only preparation needs no purchase or Instruction context:
clink-cli visa commerce-run \
--context '{"mode":"prepare","target":"login","environment":"uat"}' \
--open \
--format jsonUse target: "visa_card_ready" only to verify an already VIC-ready Visa. Without a frozen
purchase Instruction context, preparation does not open or prompt card/VIC setup. Preparation
forbids --confirm-purchase and cannot create an Instruction, Checkout, or payment.
Immediately after visa recommend, resolve and freeze one product before Benefit login:
clink-cli visa product-search \
--merchant-url https://merchant.example/store \
--query "meal voucher" \
--format jsonFor an internal merchant, this performs one token-free merchant-scoped Catalog search and resolves
the selected exact product/variant, price, currency, availability, merchant, and localized title
directly from the rich search result. It does not call the customer-authenticated product-detail
endpoint. A unique exact-title match, unique title-containing match, or sole result returns
PRODUCT_VERIFIED; incomplete facts fail closed. Ambiguity returns PRODUCT_SELECTION_REQUIRED
with candidates and no login or payment side effects; rerun with --selected-product-id <id> (or
compatibility alias --product-id) after selection. Pass the query in the user's language and
preserve the Catalog title instead of translating it. External parsing is allowed only after
NOT_IN_INTERNAL_UCP_LIST. Use --sandbox or --test to select an environment for this token-free
discovery without changing the saved wallet environment.
After the product is verified, check and initialize Benefit login:
clink-cli visa commerce-login \
--context-file login.json \
--confirm-purchase \
--open \
--format jsonlogin.json contains only environment and the complete frozen
instructionContext. The command validates that context and the live purchase
confirmation before reading CLI config or touching Tokens or the network. It
returns immediately with CONTINUE_REGULAR_INSTRUCTION_FLOW when Benefit login
is already ready and reports instructionId: null; it never guesses a historical
Quick Instruction. Otherwise it starts one foreground Benefit login with the
exact context. When that login returns pendingInstructionId, the command waits
up to 300 seconds, uses the exact activation event only as a wake-up, and
exact-GETs the same ID. Only status=ACTIVE returns ready=true; a remaining
PENDING Instruction returns ready=false without opening another page. With
--open, the caller should tell the user to finish VSRP login and, when needed,
card binding, VIC, and Passkey in the one browser flow while the CLI keeps
waiting. The result preserves instructionId, instructionStatus,
instructionReady, pendingInstructionId, and browserLaunch for diagnostics.
manualOpenUrl is returned only when automatic launch failed or was not
requested; a successful browser launch does not duplicate the sensitive URL in
the final JSON.
This command never calls the standalone Instruction create API, routes a
merchant, searches Catalog, prepares a card, creates a Checkout, or pays.
--dry-run prints a zero-side-effect plan without inspecting login state.
The intended order is visa recommend -> visa product-search -> visa commerce-login ->
visa commerce-run.
A purchase uses the Visa Skill frozen schema:
clink-cli visa commerce-run \
--context-file purchase.json \
--confirm-purchase \
--open \
--format jsonThe JSON contains mode: "purchase", immutable environment, requestText, program.code,
selection (merchantUrl, product ID/query, quantity, and optional routing facts), expected
(merchant, item, actual major-unit amount, and currency), instructionContext, and
digitalDeliveryExpected. Top-level instructionId is rejected. Every mandate used to create a
new Instruction must match the exact expected amount and currency and use one MCC, which becomes
the Checkout MCC. The CLI adds no amount buffer.
Purchase mode requires the separate commerce-login stage to be ready and exact-revalidates the
product selected by product-search. It never starts login or performs product discovery. When a
VIC-ready Visa already exists, it lists that payment instrument's
Instructions and considers only ACTIVE candidates with matching currency, MCC, and merchant scope,
unexpired Instruction and Mandate timestamps, and an available one-time Mandate. The candidate
must have amountLimit >= the actual total. The CLI selects the smallest sufficient amount
(equality first), then the newest trusted createdAt/createTime. Missing or tied creation time
fails closed. It exact-GETs the selected ID and revalidates every condition before Checkout.
When no VIC-ready Visa exists, commerce-run first creates or reuses one no-card PENDING
Instruction through /agent/cwallet/instructions/pending. Only after CWallet returns the exact ID
does the CLI resolve the trusted Agent Portal binding URL; it withholds the URL until the first
successful exact-ID Event Hub poll and identity validation. It never opens that URL or a standalone
VIC page and keeps waiting for activation of that exact ID. Portal owns card binding,
3DS, and VIC. After activation, the CLI exact-GETs the same Instruction, refreshes cards, and
requires its attached paymentInstrumentId to be a same-card Visa with
visaRegistrationSucceeded=true. Amount, currency, MCC, and merchant scope are revalidated before
Checkout. Timeout returns only an exact instruction get continuation and never creates another
Instruction or starts Checkout/payment. If the pending endpoint returns CARD_READY or VIC_READY
without an ID because VIC completed during the request race, the CLI refreshes cards once and enters
the regular Instruction flow without retrying the pending POST.
The exact-ID wait also wakes on customer-wide payment_method.update and
vic_device.binding_succeeded events. A Portal card binding ceremony that started before the PENDING
existed is frozen by CWallet as NO_PENDING and never activates a later Instruction, so after such
a card event the CLI gives the activation event a bounded grace period, exact-GETs the same ID, and
if it is still PENDING re-posts the identical context once: CWallet then cancels the no-card PENDING
for the now VIC-ready customer and answers VIC_READY, and the CLI continues with the regular
Instruction flow (fallbackReason=card_vic_ready_without_pending_activation, one more Passkey). The
pending response may also carry ceremonyInProgress/activationExpected; the CLI surfaces them and
still waits.
For an already VIC-ready card, historical PENDING or draft Instructions are not authorized. If no usable ACTIVE candidate exists, the CLI creates and authorizes one regular Instruction for the exact actual price.
Before card and Instruction work, the CLI resolves the real merchant internally first and verifies the current Catalog product ID, title, price, currency, availability, merchant route, and endpoint. Only an explicit internal-list miss may use external product parsing.
Only live purchase mode requires --confirm-purchase, validated before Token refresh or business
network access. Checkout delegates to the same exactly-once ucp-checkout run implementation and
uses bounded GET-only recovery for the same Checkout before optionally waiting for voucher
delivery. --dry-run prints a zero-network, zero-payment-side-effect plan for either mode. When the
shared budget expires, output contains resumeReadOnly=true and one bound ucp-checkout get
continuation that can wait for both Checkout and delivery. Never append ucp-order commands or
rerun commerce-run after Checkout creation.
Prepare a no-card Instruction (Main Edition)
Main Edition exposes the same shared capability as one stable command:
clink-cli instruction prepare \
--title "Business trip" \
--mandates '[{"title":"Hotel","description":"Hotel payment","amountLimit":"1000.00","currencyCode":"USD","merchantCategoryCode":"7011"}]' \
--max-wait 900 \
--format jsonThe command sends the complete restricted instructionContext without paymentInstrumentId or
extra. Once CWallet returns an exact PENDING ID, the CLI resolves the trusted card binding URL and
starts an exact-ID purchase_instruction.activated watch. After the first successful Event Hub
poll and identity validation, stdout emits a structured PENDING handoff envelope containing
instructionId, bindingUrl,
watchReady=true, watchEventType="purchase_instruction.activated", terminal=false, and
processRunning=true. The process remains in the foreground; after the watch it exact-GETs the same
ID and emits the final envelope. Agents should parse these JSON envelopes, not stderr prose.
The command never opens the binding URL or a standalone VIC page. --open and --no-watch are
rejected. Timeout preserves the original ID in a read-only instruction get continuation and never
creates another Instruction or starts Checkout/payment. A concurrent CARD_READY/VIC_READY
response returns card_ready without an ID, another pending POST, or a binding handoff. The watch
also wakes on payment_method.update/vic_device.binding_succeeded; when the card is VIC-ready but
the exact ID stays PENDING after a grace period, one reconciliation POST of the same context returns
VIC_READY and the command reports card_ready with the original instructionId and
fallbackReason=card_vic_ready_without_pending_activation.
Pending expiry is server-owned: exact verification requires one valid future Instruction expiry
shared by every Mandate, while caller-supplied Instruction/Mandate expiry values are not compared.
2. Open card pages
Get the card-binding handoff with its built-in event watcher:
clink-cli card binding-linkWith the default watch enabled, card binding-link first refreshes
paymentMethodsVoList, starts a watcher scoped to payment_method.added whose Event Hub polls send
eventTypes: ["payment_method.added"], and waits for the first
successful, well-formed Event Hub poll (data.records must be an array). Only then does it print the
first stdout envelope:
{
"ok": true,
"data": {
"bindingUrl": "https://agent.clinkbill.com/payment-method-setup?email=alice%40example.com",
"paymentMethodsVoList": [],
"watchReady": true,
"watchEventType": "payment_method.added"
}
}bindingUrl is rebuilt from the trusted Portal origin, the local add-card path, and the locally
stored email. Backend path, query, fragment, and token data are not exposed. Without a stored email,
the URL still targets the add-card page. watchReady: true means the event listener is ready before
the browser handoff; it does not mean a card has already been added. The process remains alive until
the matching event or the 15-minute timeout, then prints the watch result as a second stdout
envelope. This scoped watcher ACKs only matching payment_method.added events. Unrelated current or
stale events are left queued for their owning workflows. A malformed successful poll response fails
before any binding handoff is printed.
To refresh the payment-method list without starting a listener:
clink-cli card binding-link --no-watch --format jsonThis form returns immediately with the trusted add-card bindingUrl, a normalized
paymentMethodsVoList, watchReady: false, and watchEventType: null. It makes no Event Hub poll
and prints a warning plus a replacement events poll command to stderr; start a listener before
giving the URL to a user who will act on it.
Open the add-card page:
clink-cli card setup-link --openOpen the manage-card page:
clink-cli card modify-link --openOpen Visa Passkey registration for an existing card:
clink-cli card passkey-link --payment-instrument-id pi_xxx --openFor setup-link, modify-link, passkey-link, instruction create, and instruction sign-url,
an OAuth-authenticated CLI first attempts a one-time browser handoff through a random 127.0.0.1
callback. A successful handoff reuses the CLI identity to establish the existing 30-minute Portal
session, then redirects to the validated Portal completion URL returned by the handoff service.
Listener or create failures open the trusted target directly; callback or approval failures redirect
to the existing email-code login while preserving the target page.
3. Check saved payment methods
List cached payment methods:
clink-cli card list --format prettyGet one cached payment method:
clink-cli card get --payment-instrument-id pi_xxxNotes:
- card add/update/delete happens on the web page, not in the CLI
card listandcard getread local cached datacard binding-link,card setup-link, andcard modify-linkrefresh the local payment method cache
4. Make a payment
Pay with merchant mode:
clink-cli pay \
--merchant-id merchant_xxx \
--amount 10.00 \
--currency USD \
--payment-instrument-id pi_xxxPay with session mode:
clink-cli pay --session-id sess_xxx --payment-instrument-id pi_xxxVIC-routed charge with instruction/mandate context:
clink-cli pay \
--session-id sess_xxx \
--payment-instrument-id pi_xxx \
--instruction-id ins_xxx \
--mandate-id mndt_xxx \
--shipping-address '{"street_address":"1 Market St","address_locality":"San Francisco","address_region":"CA","address_country":"US","postal_code":"94105","first_name":"Ada","last_name":"Lovelace","phone_number":"+14155550100"}' \
--products '[{"productId":"sku_1","productName":"Demo","quantity":1,"unitPrice":12.99,"currencyCode":"USD"}]'If --payment-instrument-id is omitted, CARD and BALANCE keep using the default cached payment
method. Other payment types first refresh the payment-method cache and select only a matching
paymentMethodType. If none match, bind one and refresh payment methods. One matching method is
selected directly; when several match, exactly one must be marked default, otherwise the command
requires an explicit --payment-instrument-id.
An explicit instrument for those other payment types is also checked against the refreshed list
and must have the requested type. Explicit CARD and BALANCE behavior remains unchanged. The
command never falls back to a card of another type. --purchase-instruction-id remains as a
backward-compatible alias for --instruction-id. --shipping-address maps to
aiAgentInstructionBo.shippingAddressJson and should use the UCP Postal Address shape
(street_address, address_locality, address_region, address_country, postal_code,
first_name, last_name, phone_number). --products maps to
aiAgentInstructionBo.products in the Clink order charge request; unitPrice is a major-unit
decimal.
When a payment returns channelPaymentResponse.status=5 with a PNG QR Data URL, pay writes the
validated PNG to a private temporary file and returns
customerAction.type=QR_CODE_REQUIRED, mediaType=image/png, imagePath, temporary=true,
cleanupRequired=true, directory-level cleanupPath, orderId, paymentExecutionDetailId,
expiresAt, and expiresSecond. expiresAt is Unix epoch seconds. Event consumers use
expiresSecond for their maximum wait and cap it at 900 seconds. The complete Data URL is
redacted from stdout. The caller owns the file lifetime and must remove cleanupPath recursively
after the payment reaches a terminal state or expires; the CLI intentionally keeps it after exit
so an Agent can display it.
List all public skills
clink-cli skills list --all --format pretty
clink-cli skills list --all --tippable --format prettyThe command requests the dashboard marketplace with pageSize=999&sort=NEW. Every list first
removes rows without nonempty publisher, name, or versionNo. --tippable additionally requires
nonempty skillId and merchantId, parses the tipsConfigJson string, and keeps only records whose
parsed configuration has the boolean value enabled: true. Missing, malformed, disabled, or
string-typed enabled configurations are filtered without failing the command. The CLI then
reverses the selected rows, injects contiguous one-based Number values, and returns the JSON array
in the normal { "ok": true, "data": [...] } success envelope.
Tip a skill publisher
clink-cli skills tip --publisher clinkpay --name PollyReach --amount 2
clink-cli skills tip --publisher jinsx商户四 --name PollyReach --amount 2
clink-cli skills tip --publisher '草诀歌AI Labs' --name 'SEO Deep Audit' --amount 2Skill tips always use USD and accept any positive finite amount. The CLI resolves a
case-insensitive exact --publisher/--name match. Publisher and skill names accept Unicode
letters, numbers, including Chinese, and internal ASCII spaces. Quote values containing spaces.
CLI tips always use sort=NEW to select the latest Marketplace version. Successful output includes
the resolved versionNo when the Marketplace supplies it.
The CLI uses only the selected marketplace record's merchantId and ignores tipsConfigJson. It
refreshes the payment-method list and selects only a record with explicit isDefault=true. If no
explicit default exists, the command returns HTTP-style code 422 with
No default payment method before creating an order. A default CARD is sent to the charge API
without checking Credit balance. A default BALANCE is sent only when its finite numeric
availableBalance covers the full tip amount; a missing, invalid, non-finite, or insufficient
balance returns 402 with Credit 余额不足,请先绑定银行卡. An unsupported explicit default type
returns 422 with Unsupported default payment method. The backend calculates any Credit
allocation.
After the synchronous payment response confirms success (channelPaymentResponse.status=1), and when the marketplace
record includes skillId, the CLI best-effort reports the tip metric to the resolved dashboard
base: POST /prod-api/skill-marketplace/internal/skills/{skillId}/metrics/tip. The metric uses only
the response-verified latest Marketplace versionNo selected by the pre-payment sort=NEW lookup.
The request body contains the payment
orderId, locked versionNo, major-unit amount, currency, and the fixed
source: "CLINK_PAYMENT". A metrics failure does not change the successful payment result. Failed
and 3DS-pending payments are not reported.
Explicit terminal agent-pay failures return payment_failed with exit code 5. Missing or unknown
agent-pay statuses return payment_unknown with exit code 6 and must be verified before retry.
Tip results also expose the channel's original rawPaymentStatus, rawPaymentMessage, and complete
payment payload so callers can diagnose the response without losing the compatibility status.
5. Search a merchant UCP Catalog
Search a registered merchant's public Catalog. The command defaults to production; add --sandbox
for sandbox/UAT or --test for test:
clink-cli ucp-catalog search \
--merchant-id merchant_xxx \
--query "wireless keyboard" \
--language en-US \
--context '{"currency":"USD"}' \
--limit 10 \
--test \
--format jsonThe command sends POST /agent/ucp/{merchantId}/catalog/search, automatically generates the
required Request-Id, and uses UCP-Agent: clink-cli unless --ucp-agent overrides it. Optional
--filters, --signals, and --attribution values must be JSON objects; --cursor continues a
previous page. Catalog price filters use minor currency units. The request is anonymous: the CLI
does not read ~/.clink-cli/config.json, inherit the wallet environment or CLINK_BASE_URL, send
OAuth/CSK/customer headers, refresh a token, or retry a 401. --customer-api-key and
--customer-id are rejected. A merchant without an enabled Catalog returns the backend's original
catalog_not_supported message. A 401/403 is surfaced as an API failure rather than a local wallet
authentication failure, so callers must not trigger wallet re-login for it.
Translation is opt-in and driven only by the UCP context.language field: pass --language <tag>
with an IETF BCP 47 tag such as en, zh-Hans, or fr-CA, or set language inside --context.
The two cannot be combined. Omit both and the merchant's original titles and descriptions come
back unchanged — the query text is never used to guess a target language. Pass the same language
to Search and Product, or the list and detail views disagree.
clink-cli ucp-catalog search \
--merchant-id merchant_xxx \
--query 熊猫外卖 \
--language en \
--format jsonFetch one product by an ID returned from search:
clink-cli ucp-catalog product \
--merchant-id merchant_xxx \
--product-id product_xxx \
--language en-US \
--context '{"currency":"USD"}' \
--format jsonThe Product request supports the same optional --language, --context, --filters, --signals,
and --attribution fields as Search. The current provider does not accept selected options or
preferences; use the product and variant data returned by the service. Product is anonymous and
uses the same production/--sandbox/--test environment rules as Search.
Search across merchants when you do not know which one carries the item:
clink-cli catalog search \
--query "iced latte" \
--channel-type shopify \
--ext '{"trace":"demo-1"}' \
--format jsonThe command sends POST /agent/ucp/extra/catalog/search and takes no --merchant-id: this endpoint
exists to find which merchants carry the item. Use ucp-catalog search when the merchant is already
known. --channel-type narrows to one channel, and omitting it discovers across every channel.
--ext is a free-form JSON object that is passed through and logged only — it never affects search
conditions or the response shape. All other options match ucp-catalog search. Results come back
grouped by target, each group carrying channel_type plus either merchant_id (internal merchant)
or store_id (external platform store); the shape does not change with --channel-type, only the
number of groups does. This command follows the same anonymous, config-independent environment
rules as merchant-scoped Search and accepts the same --language/context.language field. Broad
discovery forwards the language to each provider but does not run UCP's LLM translation pass, so
provider localization may vary; that translation pass is implemented for merchant-scoped Search
and Product only.
Query one order's current status by order ID:
clink-cli ucp-order get --order-id order_xxx --format jsonThe command sends GET /agent/ucp/orders/{orderId}. The endpoint is not merchant-scoped: ownership
is checked against the caller's wallet identity, so another buyer's order returns not_found.
Wait for an expected voucher or other digital delivery:
clink-cli ucp-order wait-delivery \
--order-id order_xxx \
--max-wait 900 \
--format jsonThis command repeatedly reads the same order and never retries payment, Checkout completion, or
order creation. A missing digital_delivery field is pending because the asynchronous payment
projection may not have initialized it yet. Pending responses honor digital_delivery.next_retry_at
with a local 3-to-30-second clamp. The command stops at ready or failed; ready requires at
least one artifact. A bounded timeout returns the last order plus resumeCommand. Use this command
only when the frozen product context expects digital delivery; ordinary UCP orders should keep using
the single-read ucp-order get.
List the calling wallet's orders, newest first, optionally filtered by status and creation time:
clink-cli ucp-order list \
--status paid,partially_refunded,refunded \
--start-time 2026-07-01T00:00:00Z \
--end-time 2026-07-31T23:59:59Z \
--size 20 \
--format jsonThe command sends GET /agent/ucp/orders with created_from, created_to, status, page, and
size query parameters. All filters are optional. --status accepts a comma-separated list matching
any of draft, pending, paid, cancelled, partially_refunded, refunded, and unknown values
fail locally; multiple statuses go out as repeated status params. --start-time / --end-time are
UTC: a timestamp with no zone offset is read as UTC and sent as RFC 3339. Rows carry id,
checkout_id, status, payment_status, amount, currency, and created_at; use
ucp-order get for the full order.
6. Manage UCP checkout for shadow merchants
Create and complete a checkout in one safety-gated command:
clink-cli ucp-checkout run \
--merchant-url https://shop.example/checkout/abc \
--merchant-category-code 5311 \
--currency USD \
--line-items '[{"id":"li_1","item":{"id":"sku_1","title":"Demo","price":"10.00"},"quantity":1}]' \
--payment-instrument-id pi_xxx \
--confirm-purchase \
--wait-delivery \
--max-wait 900 \
--format jsonLive run requires the explicit --confirm-purchase flag and rejects its absence before any
network request. It sends create once, then sends complete exactly once; neither payment/create
request is automatically retried. If complete returns complete_in_progress or any other status
besides completed, the command returns stage=complete with the authoritative response and a
read-only ucp-checkout get resumeCommand bound to the original endpoint and checkout ID.
--wait-delivery starts only when complete authoritatively returns status=completed and
data.order.id. Delivery ready, failed, and timeout outputs contain the fetched order and its
digital_delivery snapshot. Timeout reuses the read-only ucp-order wait-delivery resume command;
it never retries Checkout or payment. run --dry-run needs no confirmation and makes no network
requests or payment side effects; it prints an auditable create, exactly-once complete, and optional
delivery plan.
Create a UCP checkout session for a CLI-discovered merchant:
clink-cli ucp-checkout create \
--merchant-url https://shop.example/checkout/abc \
--merchant-category-code 5311 \
--currency USD \
--line-items '[{"id":"li_1","item":{"id":"sku_1","title":"Demo","price":"10.00"},"quantity":1}]'For ucp-checkout create, line_items monetary price / amount fields are decimal major-unit
values converted by --currency. Live update first reads the existing checkout currency and
converts decimal strings such as "12.00" to minor units; existing integer JSON numbers remain
minor-unit values for backward compatibility. The create API body sends customer_id and
buyer.email from the local clink-cli config get --format json data, and sends --currency as
context.currency. Update --dry-run performs no read request, so it requires --currency.
Then query, update, complete, or cancel it:
clink-cli ucp-checkout get --checkout-id chk_xxx
clink-cli ucp-checkout update \
--checkout-id chk_xxx \
--line-items '[{"id":"li_1","item":{"id":"sku_1","title":"Demo","price":"12.00"},"quantity":1}]'
clink-cli ucp-checkout complete --checkout-id chk_xxx
clink-cli ucp-checkout cancel --checkout-id chk_xxxBy default these commands call {baseUrl}/agent/ucp/external/checkout-sessions. To point at a
custom UCP endpoint prefix, pass --endpoint https://host/path; the CLI appends
/checkout-sessions, /checkout-sessions/{checkoutId}, or the corresponding action path.
Notes:
- the CLI command is
ucp-checkout; there is noexternalsubcommand - CLI-discovered merchants use the shadow-merchant external checkout path internally
- run reuses all create fields and adds
--payment-instrument-id,--confirm-purchase,--wait-delivery, and--max-wait - create sends
merchant_url,customer_id,buyer.email, andcontext.currency; the backend resolves authorization andorder_channel_id - complete sends a standard UCP
payment.instrumentsobject; the selected instrumentidis local configcustomerId#paymentInstrumentId,credential.tokenis the payment instrument ID, and when--payment-instrument-idis omitted or empty it uses the cached default card Idempotency-Keyis generated by the CLI for create/update/complete; callers do not pass--idempotency-key- OAuth Bearer authorization is required after a wallet first uses OAuth; legacy CSK remains only for never-OAuth wallets
7. Refund an order
Create a full refund:
clink-cli refund create --order-id order_xxxCheck refund status:
clink-cli refund get --refund-id rfd_xxx8. Check risk rules
Get current risk rule settings:
clink-cli risk get --format prettyOpen the risk rule page:
clink-cli risk link --openInstall agent skills
Synchronize the official payment Skill
The setup and update commands synchronize agentic-payment-skills after the npm CLI step:
npx @clink-ai/clink-cli@latest install
clink update
clink update --check --format pretty
clink skills sync --forceThe primary source is https://github.com/clinkbillcom/agentic-payment-skills. When that source is
unavailable, the synchronizer uses
https://www.clinkbill.com/public/skills/agentic-payment-skill.zip. The fallback is accepted only
when its schema-v1 manifest is available at
https://www.clinkbill.com/public/skills/agentic-payment-skill.manifest.json and its declared size,
archive SHA-256, content SHA-256, and Skill version all match. A missing, unreachable, malformed, or
mismatched manifest fails closed without installing the ZIP. The validated runtime package is
installed at ~/.agents/skills/agentic-payment-skills; source-root tests/ and docs/ directories
are not published. Updates use staged validation and the managed release transaction, so a failed
sync keeps the previously active Skill. clink update --check and clink skills sync --check do
not modify installed CLI or Skill state; their Skill result uses action: "checked" and reports
the would-be action in plannedAction.
CLI self-install/update is intentionally limited to the npm distribution. Vendored copies return a
clear error and can still use clink skills sync as the Skill-only recovery entry point. npm child
processes are invoked with argument arrays and no command shell. Their stdout is captured so an
explicit --format json command emits exactly one standard JSON envelope.
Install a Marketplace Skill
Install a specific release, let the server select the latest release, replace an unmanaged installation, or preview the operation:
clink-cli skills install clinkpay/[email protected]
clink-cli skills install clinkpay/PollyReach
clink-cli skills install clinkpay/PollyReach --force
clink-cli skills install clinkpay/PollyReach --dry-run --format prettyThe package format is <publisher>/<skillName>[@<version>]. When the version is omitted, the CLI
omits versionNo from the request and lets the server select its latest release. It does not infer
or report a resolved semantic version for that archive. Publisher and skill names may contain
Unicode and internal ASCII spaces; quote the complete package identity when it contains spaces.
Download and install behavior
The CLI uses the existing base URL resolution and precedence described under Configuration, derives the matching dashboard base, then requests a short-lived download URL:
GET /prod-api/skill-marketplace/public/skills/download-url?publisher=...&skillName=...[&versionNo=...]Clientid is sent only to this Clink dashboard endpoint, never to the signed download URL. The signed
URL is treated as a secret and is never printed. --timeout applies to network operations; signed
package downloads use at least 300000 milliseconds so slower transfers can finish.
--dry-run performs no network requests and makes no filesystem changes; it only prints the
planned result. Results support JSON and pretty output. Per-agent statuses are linked, shared,
copied, unchanged, or unsupported.
The installer accepts raw single-Skill files and ZIP packages:
- A single-Skill download may be a raw UTF-8
SKILL.mdfile or a ZIP withSKILL.mdat the archive root. Otherwise, archive-root directories that directly containSKILL.mdare selected. One selected directory is installed as the requested single Skill; two or more selected directories form a multi-Skill package using their directory names. Ordinary files and other directories are ignored. - When no archive-root Skill directory exists and there is exactly one top-level directory, the same selection applies inside that common wrapper. All multi-Skill child releases and Agent updates are committed atomically and rolled back together on failure.
Multi-skill results include multiSkill: true and a skills array containing each child skill's
action, install path, backup path, and Agent results. Single-skill output remains unchanged.
After a successful committed install, when the download-ticket response includes skillId, the CLI
best-effort reports the public download metric to the resolved dashboard base:
POST /prod-api/skill-marketplace/internal/skills/{skillId}/metrics/public-download with
{"source":"AGENT_CLI"}. Metrics failures do not roll back or fail the completed installation.
Managed skills live under ~/.agents/skills; each skill uses immutable entries in releases/ and a
current pointer. Reinstalling a managed skill updates that pointer automatically; an identical skill
is left unchanged. If a destination is not managed by clink-cli, installation stops unless
--force is supplied. A forced replacement retains the previous content as a backup. A failed
single-skill update rolls back to the previously active release; a failed multi-skill update rolls
back every child changed by that command.
Only agent roots that already exist are detected; installing a skill does not create new agent homes. Integration modes are:
| Agent | Mode | Notes |
| --- | --- | --- |
| Cursor, Claude Code, Codex, CodeBuddy | link | Links the agent's skill entry to the central installation. |
| Trae | link | Creates the symlink only after its real target is verified as an immutable release. |
| OpenClaw, OpenCode, GitHub Copilot | shared | Uses the shared central skill root. |
| Gemini CLI (GEMINI_CLI_HOME unset or HOME-equivalent) | shared | Uses the shared central skill root. |
| Gemini CLI (GEMINI_CLI_HOME resolves outside HOME) | link | Links the custom home to the central installation. |
| Hermes | copy | Copies the installed skill into its existing root. |
| CodeWork, ChatGPT | unsupported | Reports a warning and continues. |
~/.trae/skills is Trae's documented global skill directory, but Trae has not confirmed that it
discovers per-skill symlinks there. Before release, validate discovery with a real Trae installation;
if that check fails, use a copy adapter for Trae instead.
Verification and safety
After installation, verify discovery in the target agent:
- OpenCode: start a fresh process and check its native skill inventory.
- GitHub Copilot: run
/skills reload, then/skills info <skillName>. - Gemini CLI: run
/skills reload, then/skills list.
Downloads must use HTTPS. The installer enforces download and extracted-size limits, computes a
local SHA-256 package identity, and rejects unsafe ZIP entries and archive layouts. The SHA-256 is
used to identify local content; it is not a claim of publisher authenticity. Validation,
authentication, API, and network failures keep exit codes 2, 4, 5, and 6. Archive validation
and local filesystem, lock, link, copy, or rollback failures use error type install_error and exit
code 8.
Common Usage
Select sandbox (UAT) or test:
clink-cli wallet init --sandbox --email [email protected]
clink-cli card list --format pretty
clink-cli wallet init --test --email [email protected]
clink-cli card list --format pretty--sandbox selects the UAT URLs and --test selects the .dev URLs defined in src/domains.ts.
For authenticated commands, these flags are accepted by wallet init; OAuth credentials and the
environment are saved together, and switching environments requires another initialization.
Public ucp-catalog search/product, catalog search, and
tool internal-ucp get-merchant-list also accept the flags per invocation and default to
production. Those public actions never read the saved wallet configuration.
Visa Edition visa product-search is also token-free, but follows the edition's fixed or saved
environment unless --sandbox or --test explicitly selects a compatible environment.
Advanced custom endpoints remain supported through CLINK_BASE_URL. For wallet init, an explicit
--sandbox / --test or a distribution-fixed environment takes precedence over CLINK_BASE_URL,
then falls back to production. Later commands use CLINK_BASE_URL when present and otherwise use
the saved URL. These public discovery actions intentionally ignore CLINK_BASE_URL and
CLINK_WALLET_INIT_ENVIRONMENT so an invocation without an environment flag is always production.
Print requests without executing:
clink-cli pay \
--merchant-id merchant_xxx \
--amount 10.00 \
--currency USD \
--payment-instrument-id pi_xxx \
--dry-runCommands
Command groups:
npx @clink-ai/clink-cli@latest install [--force]clink update [--check] [--force]clink skills sync [--check] [--force]clink-cli wallet initclink-cli wallet logoutclink-cli wallet statusclink-cli card binding-linkclink-cli card setup-linkclink-cli card modify-linkclink-cli card passkey-link --payment-instrument-id <id>clink-cli card listclink-cli card get --payment-instrument-id <id>clink-cli risk getclink-cli risk linkclink-cli pay --merchant-id <id> --amount <amount> --currency <currency>clink-cli pay --session-id <id>clink-cli ucp-catalog search --merchant-id <id> --query <text> [--language <tag>] [--context <json>] [--filters <json>] [--cursor <cursor>] [--limit <n>] [--sandbox|--test]clink-cli ucp-catalog product --merchant-id <id> --product-id <id> [--language <tag>] [--context <json>] [--filters <json>] [--sandbox|--test]clink-cli catalog search --query <text> [--channel-type <type>] [--form-type <type>] [--ext <json>] [--language <tag>] [--context <json>] [--filters <json>] [--sandbox|--test]clink-cli ucp-order get --order-id <id>clink-cli ucp-order list [--status <statuses>] [--start-time <utc>] [--end-time <utc>] [--page <n>] [--size <n>]clink-cli ucp-checkout run --merchant-url <url> --merchant-category-code <code> --currency <currency> --line-items <json> --confirm-purchase [--payment-instrument-id <id>] [--wait-delivery] [--max-wait <seconds>] [--endpoint <url>]clink-cli ucp-checkout create --merchant-url <url> --merchant-category-code <code> --currency <currency> --line-items <json> [--endpoint <url>]clink-cli ucp-checkout get --checkout-id <id> [--endpoint <url>]clink-cli ucp-checkout update --checkout-id <id> --line-items <json> [--currency <currency>] [--endpoint <url>]clink-cli ucp-checkout complete --checkout-id <id> [--payment-instrument-id <id>] [--endpoint <url>]clink-cli ucp-checkout cancel --checkout-id <id> [--endpoint <url>]clink-cli refund create --order-id <id>clink-cli refund get --refund-id <id>clink-cli events poll [--type <type[,type...]>] [--checkout-id <id>] [--no-ack]clink-cli instruction list --valid-only --payment-instrument-id <id>(VIC; ACTIVE only, and one-time mandates are filtered toreserveStatus=0)clink-cli instruction prepare --title <t> --mandates <json> [--max-wait <seconds>](no-card PENDING; structured handoff then exact-ID final envelope)clink-cli instruction create --payment-instrument-id <id> --title <t> --mandates <json>(VIC; returns instructionId + passkeyUrl)clink-cli instruction get --purchase-instruction-id <id>(VIC)clink-cli instruction sign-url --payment-instrument-id <id> --purchase-instruction-id <id>(VIC)clink-cli instruction update|cancel(VIC page URL only)clink-cli pay --session-id <id> --instruction-id <id> --mandate-id <id> [--shipping-address <json>] [--products <json>](VIC charge context;--productsmaps toaiAgentInstructionBo.products)clink-cli skills install <publisher>/<skillName>[@<version>] [--force]clink-cli tool item-id --url <url>clink-cli tool parse-site --url <url>clink-cli tool parse-item --url <url>clink-cli tool checkout-total --url <url>clink-cli tool get-ucp-profile --url <url>clink-cli tool get-rest-endpoint --url <url>clink-cli tool internal-ucp get-endpoint --product-url <url>clink-cli tool internal-ucp get-merchant-list [--sandbox|--test]clink-cli config set <key> <value>clink-cli config getclink-cli config unset <key>
tool internal-ucp get-endpoint performs an exact product-hostname lookup against the merchant list
for the selected environment and generates the corresponding Clink UCP REST endpoint. It loads the
selected environment's anonymous GET /agent/ucp/merchants API; validated successes use a short
per-process TTL cache, and concurrent callers share one in-flight request. Errors are not cached,
and a cached hostname absence is refreshed before it can select external routing. The backend has
already filtered the result to enabled merchants. The command uses the environment saved by
wallet init; re-run initialization to switch environments. A domain outside that list returns
{"error_code":"NOT_IN_INTERNAL_UCP_LIST"} with a successful process exit so callers can continue
with standard UCP profile discovery. Conflicting merchant IDs for the target canonical hostname are
a terminal API configuration error and never enter profile fallback; unrelated hostnames remain
resolvable.
tool internal-ucp get-merchant-list is the public command for listing Clink UCP merchants. It
sends an anonymous GET /agent/ucp/merchants request with no query or body. No environment flag
means production, --sandbox means sandbox/UAT, and --test means test. The selected URL is
derived from API_BASE_URLS in src/domains.ts. The command ignores
~/.clink-cli/config.json, CLINK_BASE_URL, the wallet distribution environment, OAuth, CSK,
customer ID, and customer API key credentials.
The backend response is returned as {"merchants":[...]} after validation. It contains enabled
merchants with these public fields:
{
"merchants": [
{
"merchant_id": "mcht_xxxxxxxxxxxx",
"merchant_name": "Example Store",
"description": "What the storefront sells and which product categories it covers.",
"domain": "https://shop.example.com",
"ext": { "source": "merchant-config" }
}
]
}ext is opaque JSON copied from the merchant configuration and is never used for merchant identity
or endpoint routing. During a rolling deployment, an older backend may omit it. domain is a safe
absolute HTTP(S) merchant route URL, not a bare hostname. It may retain a route
path such as https://vtravel.link2shops.com/yiyuan/; userinfo, query, and fragment are forbidden,
while dot segments and default ports are normalized. Internal endpoint resolution uses only its
canonical hostname and requires an exact match with the product URL hostname. The returned Clink
endpoint is independently generated from the effective wallet API base and merchant_id; the
merchant route path is never appended to that endpoint.
Invalid rows are skipped independently, but a non-empty array with no valid merchant identity is
rejected. The anonymous read-only GET retries transport failures, 408, 429, and 5xx once with short
backoff inside one total timeout; other HTTP or response-contract failures are API errors (exit
code 5), and exhausted transport/timeouts are network errors (exit code 6). Neither is reported as
a missing merchant.
Show help:
clink-cli --help
clink-cli wallet --help
clink-cli card --help
clink-cli ucp-catalog --help
clink-cli catalog --help
clink-cli ucp-checkout --help
clink-cli ucp-order --help
clink-cli refund --help
clink-cli events --helpConfiguration
Useful config commands:
clink-cli config get
clink-cli config set base-url https://api.clinkbill.com
clink-cli config set customer-id cus_xxx
clink-cli config unset customer-api-keyconfig get and wallet status never print raw OAuth tokens or the raw customer API key. OAuth
credentials take precedence over legacy CSK, and a request never sends both.
wallet status makes no network request and reports the effective base URL and customer credentials
after applying command flags, environment variables, and saved config in precedence order.
It also reports authorizationEnvironmentMatches and the persistent oauthRequired state.
After a wallet first uses OAuth, logout or token expiry requires wallet init; the CLI never falls
back to stored, environment, or command-line CSK credentials.
Changing base-url to a different origin clears the saved OAuth and legacy CSK credentials before
the new environment can be used.
The first OAuth release stores tokens as plaintext JSON under the current user's home directory.
On Unix/macOS the CLI keeps ~/.clink-cli at mode 0700, config.json at mode 0600, and replaces
the file atomically under a lock. OS Keychain integration is deferred.
Settable config keys:
base-urlcustomer-iddefault-open-linksemailname
customer-api-key cannot be stored with config set. An existing saved legacy key can still be
removed with config unset customer-api-key; never-OAuth legacy users may continue supplying it
through CLINK_CUSTOMER_API_KEY or --customer-api-key.
customer-id is directly settable only for never-OAuth legacy wallets; OAuth owns the customer
identity and wallet init is the only way to switch it.
Resolution order:
- command flags
- environment variables
- saved local config
Environment variables:
CLINK_BASE_URLCLINK_CUSTOMER_IDCLINK_CUSTOMER_API_KEY
Global Options
--format <json|pretty>--dry-run--open--no-open--no-watch--customer-id <id>--customer-api-key <key>--timeout <ms>--help
--sandbox and --test are command-scoped options for wallet init, visa init,
ucp-catalog search, and Visa Edition visa product-search; they are not global options.
Event Watching
Browser-link commands can consume the webhook-event queue while waiting for the user action to
finish. Most commands print their first envelope and then start polling. card binding-link is the
readiness-gated exception: it withholds both the binding handoff and its handoff logs until the first
successful, well-formed Event Hub poll, then reports watchReady: true as described above.
instruction prepare is also readiness-gated, but its first stdout envelope is a structured
PENDING handoff emitted after the first successful exact-ID poll; the same process later prints the
final exact-ID result.
- The built-in watcher polls
POST /agent/event-hub/webhook-events/poll(pageSize: 20) every 5 seconds for up to 15 minutes. An unscoped watcher stops at the first non-empty batch; a scoped watcher continues until its target is present or it times out. - It logs event summaries to stderr and updates wallet-derived local state where applicable.
payment_method.*events refresh cached payment methods in~/.clink-cli/config.json; other event payloads are returned in the command output but are not persisted in the config file. - ACKs are sent through
POST /agent/event-hub/webhook-events/ackwith{ "eventIds": [...] }; acked events are removed server-side. Existing watchers retain their prior consumption behavior. The scopedcard binding-linkwatcher is stricter: Event Hub selectspayment_method.addedbefore pagination, the CLI ACKs only a matching event, and it preserves any nonmatching current or stale record returned defensively. - The terminal watch result is printed as a second JSON envelope to stdout; progress messages go to stderr.
Pass --no-watch to skip the built-in poll for scripted or non-interactive runs. For
card binding-link, the immediate envelope explicitly carries watchReady: false and
watchEventType: null, and stderr identifies the missing listener. Polling is also skipped under
--dry-run. instruction prepare intentionally rejects --no-watch because its binding handoff
must never outlive the foreground exact-ID listener.
Polling on demand: events poll
To consume state changes without printing a link, call clink-cli events poll.
It polls the webhook-event queue within a bounded window (default --max-wait 60
seconds, --limit 20 page size), processes each event, updates wallet-derived
state where applicable, and acknowledges consumed records by eventId.
clink-cli events poll --type payment_method.added --format json
clink-cli events poll \
--type payment_method.update,vic_device.binding_succeeded \
--payment-instrument-id pi_123 \
--format json
clink-cli events poll \
--type agent_order.succeeded \
--checkout-id checkout_123 \
--format jsonThe result is { ready, timedOut, events, ackedEventIds, nextToken? }. On timeout
it also returns resumeCommand. Ordinary polls need no cursor because ACKed events
are removed server-side. A Checkout-scoped timeout may additionally return the opaque
nextToken; the generated resumeCommand carries it automatically. payment_method.*
events refresh cached payment methods; other event payloads are not stored in
config.json.
With --type, events contains only the selected exact type. A comma-separated
list such as --type account-created,account-reloaded waits for any listed type.
Every record read is still processed; unselected records are acknowledged and
skipped so an older page cannot block the target. Selected records are acknowledged
by default. Typed --no-ack keeps selected records queued but still acknowledges
unselected records; only an untyped --no-ack poll peeks without acknowledging
anything. Consequently, ackedEventIds can include IDs absent from events. If the
original poll used CLINK_BASE_URL, resumeCommand preserves that override.
--payment-instrument-id requires --type and is mutually exclusive with
--checkout-id. It correlates card/VIC events through canonical payload aliases
or resourceId; same-type events for another card remain unacknowledged. Use it
for VIC readiness as an any-of wake-up signal, then refresh the exact card and
require visaRegistrationSucceeded=true.
--checkout-id is a strict selector for one UCP checkout. It requires a nonblank ID and exactly one
type: agent_order.succeeded or agent_order.failed. In this mode the poll request sends the
selector to Event Hub before pagination:
{
"pageSize": 20,
"eventTypes": ["agent_order.succeeded"],
"selectors": { "checkoutId": "checkout_123" }
}The CLI verifies the response locally using payload data.checkoutId / data.checkout_id or the
UCP path requestParams.extra.agentInstructionInfo.ucpCheckoutId (also accepted beneath data for
compatibility). Event Hub returns nextToken after each scanned page, and the CLI sends it on the
next request so unacknowledged events for another Checkout cannot permanently hide later records.
It never falls back to resourceId, data.orderId, or a payment order ID. A missing ID, a
different checkout, a malformed nested path, or conflicting aliases fails closed. A full page
without nextToken fails explicitly instead of polling the same page forever. Only the locally
verified exact match is returned and ACKed by default; every other Checkout event remains queued.
With --no-ack, the exact match also remains queued. A timeout resumeCommand preserves
--checkout-id, the opaque --next-token scan cursor, acknowledgement, and environment options.
For a completed UCP checkout, keep the identifiers separate: use the captured checkout ID for this
event selector, and use order.id from the checkout-complete response as the OMS/UCP order ID for
clink-cli ucp-order get --order-id <oms_order_id>. The order_* values in an
agent_order.* event's resourceId, data.orderId, or data.paymentOrderId are Clink Payment order
IDs and must not be passed to ucp-order get.
Local Development
Build from source:
npm install
npm run build
node dist/index.js --helpRun tests:
npm test
npm run test:e2e