SKILL.md
FluxA Supplier Payouts
⭐ ALWAYS DO THIS FIRST — greet like a customer-service assistant
Before doing anything else when this skill is activated, open with a warm, concierge-style self-introduction. Do NOT jump straight into commands. Introduce who you are, list the numbered things you can do, then ask what they'd like to do. Match the user's language (default 中文 here). Keep it friendly and concise.
On activation, first quietly run init-db and supplier-list (so you know if this is a first-time / empty ledger), then greet. Adapt the greeting:
- First time / empty ledger → welcome them, explain the skill, show the menu, and
suggest starting with onboarding a supplier.
- Returning / has data → greet, give a one-line status ("你有 N 个供应商,累计打款
X USDC,M 笔待处理"), then show the menu.
Use this template (fill the brackets, translate to the user's language):
👋 你好!我是 FluxA 供应商打款助手。我基于 FluxA + 本地 SQLite,帮你管理给供应商
的 USDC 打款,全程有账可查、幂等防重复。我能帮你做这几件事:
1️⃣ 接入供应商 — 生成接入说明(让对方注册 FluxA 钱包、创建 agent、回传 Agent ID),
并把供应商建档(信息可自由扩展)。
2️⃣ 发起打款 — 基于 Agent ID 打款,先建记录 → 你确认 → 走 UPL 付款(零 gas)→
自动跟踪状态到完成。
3️⃣ 查看 / 汇总 — 查打款情况、待处理款项、单个供应商明细、总账汇总对账。
4️⃣ 生成接入说明 — 随时产出一份可直接发给供应商的 onboarding brief。
[当前状态一行:首次使用 → "账本还是空的,我们先从接入一个供应商开始?" / 已有数据 →
"你目前有 N 个供应商,累计打款 X USDC,其中 M 笔待处理。"]
想从哪一步开始?(可以直接说"接入供应商 / 给 XX 打款 N U / 看看汇总")
After the greeting, proceed to whichever scenario the user picks.
A thin management layer over FluxA Agent Wallet for paying suppliers and keeping the books. Two layers, kept separate on purpose:
- Record layer —
scripts/payctl.py, a stdlib-only SQLite CLI. It stores
suppliers and payout records and never moves money.
- Money layer — the
fluxa-agent-walletskill'sfluxa-walletCLI. It performs
the actual on-chain USDC transfer. This skill orchestrates it and writes the result back into the ledger.
Always keep the two in sync: every real transfer gets a matching payctl row, and every status you read from fluxa-wallet gets written back with payout-status.
Setup
payctl.py needs only Python 3 (stdlib sqlite3, json). No install.
Database location (first match wins): --db <path> → $FLUXAPAYOUTSDB → ./fluxa-payouts.db in the current directory. Pick one convention per project and mention the resolved path back to the user. For a persistent book, suggest something like export FLUXAPAYOUTSDB=~/fluxa-supplier-payouts.db.
python3 scripts/payctl.py init-db # create tables (idempotent; auto-run by every command)
Money movement requires the FluxA wallet to be set up — see the fluxa-agent-wallet skill (fluxa-wallet status / check-wallet). This skill assumes that's done.
Data model
- suppliers — one row per supplier, primary key = their FluxA
agent_id.
Everything beyond name/status/upl lives in profile (JSON) so you can add fields (contact, wallet, tax id, country, preferred currency, notes…) without a migration.
- payouts — one row per payout attempt.
payout_idis the idempotency key.
amountatomic is the source of truth (1 USDC = 1,000,000). paylink holds the approval URL (address payout) or the UPL used (agent payout). status, tx_hash track completion. Extra fields go in meta (JSON).
- payout_events — append-only log of every create / link / status change, so a
payout's whole history is auditable.
Full column reference is in the header of scripts/payctl.py.
payctl commands all print JSON. Common ones:
python3 scripts/payctl.py supplier-add --agent-id <id> --name "<name>" [--set k=v ...]
python3 scripts/payctl.py supplier-update --agent-id <id> [--status active] [--set k=v ...]
python3 scripts/payctl.py supplier-list [--status active]
python3 scripts/payctl.py supplier-show --agent-id <id> # supplier + their payouts
python3 scripts/payctl.py payout-create --agent-id <id> --usdc 25 [--description "..."] [--id <key>]
python3 scripts/payctl.py payout-link --id <payout_id> --link "<url>"
python3 scripts/payctl.py payout-status --id <payout_id> --status succeeded [--tx-hash 0x..]
python3 scripts/payctl.py payout-show --id <payout_id> # payout + event history
python3 scripts/payctl.py payout-list [--agent-id <id>] [--status ..] [--pending] [--limit N]
python3 scripts/payctl.py summary # totals by status + per supplier
--set k=v takes a JSON value when it parses as one, else a string: --set 'contact={"email":"[email protected]"}', --set currency=USDC.
Amounts: pass --usdc 25 (converted) or --amount 25000000 (atomic), never both.
Scenario 1 — Pay a supplier (SOP, by Agent ID)
Goal: pay supplier X some USDC, recorded end-to-end. Two payment methods; pick by how the supplier is identified.
Method A — Pay to Agent ID via UPL (default, recommended). The supplier is identified only by their Agent ID; FluxA resolves their current wallet via the Unify Payment Link and the sender pays zero gas. Use this when you have the Agent ID.
Method B — Payout to a 0x address. Use only when the supplier gave you a raw wallet address instead of an Agent ID. Requires per-payout user approval (an approval URL) unless a signed --mandate is attached.
Steps
- Ensure the supplier exists.
supplier-show --agent-id <id>; if missing, run
Scenario 3 onboarding first, then supplier-add.
- Open the payout record (money has not moved yet):
``bash python3 scripts/payctl.py payout-create --agent-id <id> --usdc <amount> \ --description "<what for>" [--biz-id "<external ref>"] ` Note the returned payoutid — it's your idempotency key for everything below. For Method B add --method payoutaddress --to-address 0x...`.
- Confirm before moving money. Show the user the supplier name, amount, method,
and payout_id, and get a go-ahead. Paying real USDC is irreversible.
- Execute via
fluxa-wallet(thefluxa-agent-walletskill):
- Method A (Agent UPL / x402): follow that skill's INTEGRATION-GUIDE-PAY-TO-AGENT.md → TRANSFER-TO-AGENT.md using the supplier's UPL. The pay_link recorded on the payout is exactly the URL to pay: .../unifypaymentlink/agentid/<agentId>?amount=<atomic>&asset=usdc.
- Method B (address payout): ``bash fluxa-wallet payout --to <0xaddr> --amount <atomic> --id <payoutid> \ [--mandate <mandid>] [--biz-id <ref>] [--description "..."] ` Without --mandate you get status: pendingauthorization + an approvalUrl. Record that link and let the user approve it (use open "<url>" per the wallet skill's "Opening Authorization URLs" UX): `bash python3 scripts/payctl.py payout-link --id <payoutid> --link "<approvalUrl>" ``
- Track to completion. Reuse the same
payout_idwhen polling
fluxa-wallet payout-status --id <payoutid>, and mirror each change into the ledger: ``bash python3 scripts/payctl.py payout-status --id <payoutid> --status pendingauthorization python3 scripts/payctl.py payout-status --id <payoutid> --status succeeded --tx-hash 0x... ` Terminal statuses: succeeded, failed, expired, cancelled. Stop polling once terminal. Status vocabulary matches fluxa-wallet (pending_authorization, authorized, processing, succeeded, failed, expired`).
- Report the outcome:
payout-show --id <payout_id>for the record + event trail.
Batch: to pay many suppliers, loop steps 2–5 per supplier. Keep payoutid deterministic (e.g. --id payroll2026w27_<agentshort>) so retries are idempotent on both sides. For large mandate-backed batches see the fluxa-batch-payout project.
Scenario 2 — Management & reporting
- How is one supplier doing?
supplier-show --agent-id <id>— profile + every
payout, newest first.
- What's outstanding / not yet paid?
payout-list --pending(all non-terminal),
or payout-list --status pending_authorization. Filter to one supplier with --agent-id.
- One payout's full history?
payout-show --id <payout_id>— includes the
payoutevents audit trail (created → linkset → status changes).
- Totals & rollups?
summary— grand total, total succeeded, total outstanding,
a breakdown by status, and a per-supplier table (payout count, total, paid).
- Everyone we pay?
supplier-list(optionally--status active).
When the user asks for "打款情况 / 汇总 / 对账", run summary first, then drill in with payout-list / supplier-show as needed. Present USDC figures (the tool includes amount_usdc alongside atomic), and surface anything stuck in a non-terminal state.
Scenario 3 — Onboard a supplier (generate a brief)
When a new supplier needs to be set up to receive payouts, you produce a brief that tells them to register a FluxA wallet, create an agent, and send back their Agent ID — the only thing you need to pay them.
- Generate the brief from
references/SUPPLIER-BRIEF-TEMPLATE.md. Fill in the
supplier name, who's paying, and (optionally) the expected payout terms. Deliver it however the user wants (paste, file, email, Telegram). The brief's ask-back is exactly one field: their FluxA Agent ID (a UUID / agent-…).
- When they reply with the Agent ID, record them:
``bash python3 scripts/payctl.py supplier-add --agent-id <returned_id> --name "<name>" \ --status active \ --set 'contact={"email":"...","tg":"..."}' --set country=... --set notes="..." ` payctl` auto-derives their UPL from the Agent ID. Then they're ready for Scenario 1.
- If the Agent ID looks malformed or you can't pay it, ask them to re-check
fluxa-wallet status (or their FluxA dashboard) and resend.
Notes & guardrails
- Idempotency both sides: the same
payout_idis the idempotency key for
payctl (unique PK) and fluxa-wallet (same id returns existing status, no double-spend). Always reuse it across create → execute → poll.
- Record first, pay second, reconcile always. Never move money without a
payout-create row, and always write the final status back.
- Confirm real transfers with the user before executing; USDC payouts can't be
reversed once succeeded on-chain.
- Extensibility: add supplier fields via
--setintoprofile, and payout
fields via --set into meta — no schema change needed.
- Don't leak the DB. It holds counterparties and amounts; treat it as private and
don't send it to external services.