skills/airwallex-card-provisioning/SKILL.md
Use this skill when provisioning virtual or physical corporate cards in Airwallex Issuing — create cardholders, issue cards with spend limits, and manage card spending. Not for transfers or invoices.
npx skillsauth add chatandbuild/skills-repo Airwallex Card ProvisioningInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Creates virtual or physical corporate cards in Airwallex Issuing — one workflow to set up a cardholder, issue a card with spend limits, and optionally manage ongoing spend. Uses the Airwallex MCP tools (cardholder list/create/update, card list/retrieve/create/activate/update, issuing-transactions). A card's spend limits are not a separate lookup — they come back on the card retrieve as spend_limits. Requires Issuing enabled on the account, and operates on live production data — there is no sandbox.
Tone: The target user is a busy entrepreneur, not a finance analyst. Keep language conversational and action-oriented — say "Your Adobe card is set up with a $50/month limit" rather than "Card ID card_xxx created with authorization_controls.transaction_limits.limits[0].amount = 50.00." Show business labels (card nicknames, cardholder names) first; keep raw IDs and technical details in the background unless the user asks.
This skill only covers Issuing-domain operations (cards, cardholders, issuing-transactions). If the task requires anything outside that domain, stop — this is the wrong skill. Redirect the user:
AUTHORIZED ≠ money moved. It's a hold. CLEARED = money left. Be explicit when reporting.spend_limits come back with it) and list its recent transactions.card_abc.form_factor, currency, interval, or program purpose, ASK ONCE for shared defaults before creating. Process rows sequentially — never in parallel.created_by — full legal name of the person requesting the card, not the cardholder. Ask the user if unspecified.is_personalized — VIRTUAL → false, PHYSICAL → true. Ask the user if the form factor is unspecified; do not default silently.program is an object {"purpose": "COMMERCIAL"} (not a string); authorization_controls.transaction_limits is an object {"currency": "...", "limits": [...]} (not a bare array). Verify the exact field shape against the card-create tool's input schema before sending.allowed_categories. Only claim a restriction was applied if the API response explicitly shows the enforced control.individual.address uses country (not country_code); individual.express_consent_obtained is the string "yes" (not boolean true). Ask the user for DOB, address, and email — never fabricate.INACTIVE / ACTIVE / CLOSED only (CLOSED is permanent). BLOCKED, LOST, and STOLEN are NOT settable via update.postal_address or delivery_details — if either is wrong after creation, close and re-issue. Two valid paths at create time: (a) cardholder has a registered postal_address and card create uses it by default; (b) pass postal_address directly on card create to override. For EXPRESS shipment (or any China destination), delivery_details.mobile_number (E.164) is required. Always confirm the address with the user.INACTIVE — activate after delivery via the card-activate operation.status: AUTHORIZED to see pending holds.card_id) and sum in post-processing. Use cursor pagination.Copy-then-fill payloads. Replace every <...> with real values. The connector auto-generates request_id for create — you do not need to supply it. Verify the exact field shape against the tool's input schema before sending.
Cardholder — INDIVIDUAL (named person):
{
"email": "<cardholder_email>",
"cardholder_type": "INDIVIDUAL",
"individual": {
"name": {"first_name": "<first_name>", "last_name": "<last_name>"},
"date_of_birth": "<YYYY-MM-DD>",
"address": {"line1": "<street_address>", "city": "<city>", "postcode": "<postcode>", "country": "<2_letter_country>"},
"express_consent_obtained": "yes"
}
}
Cardholder — DELEGATE (purpose card, minimal fields):
{ "email": "<team_or_purpose_email>", "cardholder_type": "DELEGATE" }
Virtual card:
{
"cardholder_id": "<cdh_id>",
"form_factor": "VIRTUAL",
"created_by": "<requesting_persons_full_name>",
"is_personalized": false,
"nick_name": "<card_purpose>",
"authorization_controls": {
"allowed_transaction_count": "MULTIPLE",
"transaction_limits": {"currency": "<currency>", "limits": [{"amount": "<amount>", "interval": "MONTHLY"}]}
},
"program": {"purpose": "COMMERCIAL"}
}
Physical card: same as virtual, plus "form_factor": "PHYSICAL", "is_personalized": true, and a postal_address (line1, city, state, postcode, country). For EXPRESS shipment or any China destination, also pass delivery_details with preferred_delivery_mode: "EXPRESS" and an E.164 mobile_number.
Step 1 — Understand the card request. Collect: purpose/nickname, cardholder (name + email), card type (Virtual/Physical), currency, spend limit (amount + interval), and any requested merchant restriction (MCC support is unconfirmed — see constraints).
If the user gives a natural-language request ("Create a virtual card for Adobe, $50/month"), extract what you can and ask for gaps (e.g., "Who should this card be assigned to?"). If the user provides a document (spreadsheet, PDF, list) with card specs, extract each person's currency, limit, and form factor AS WRITTEN — do NOT normalize to a single default. Present the extracted table for confirmation before proceeding.
Step 2 — Build the card spec table. For a single card, present a table with: Purpose/Nickname, Cardholder, Form factor, Currency, Spend limit, MCC restriction (all merchants — unconfirmed), Personalized.
For a batch, list cardholders BEFORE building the table so you can show match results inline, then present a per-row table:
| # | Name | Email | Currency | Limit/mo | Cardholder | Status | Issue | | --- | --- | --- | --- | --- | --- | --- | --- |
Distinguish document issues (missing name/email/currency/limit/DOB, conflicts, duplicates — these set the Status column) from system defaults (nicknames, created_by, express_consent_obtained, delivery addresses — ask for these once, after the table; do NOT mark rows Blocked on them). A row is ✅ Ready when all document-extracted fields are complete and unambiguous.
After the table, summarize how many rows are ready, which are blocked (and why), which are duplicates (with a recommendation), and which shared defaults you still need. If the user gives a blanket override (e.g., "set all limits to $2,000 USD") that conflicts with document values, flag the conflict and ask before applying. Do NOT proceed until the user confirms.
Step 3 — Confirm live data and Issuing. Validate access via a low-cost read and verify Issuing is enabled on the account.
Step 4 — Match existing cardholder by email. Reuse only if status is READY; otherwise stop and explain the cardholder must reach READY before issuing. Paginate fully until there are no more results.
Step 5 — Create cardholder (if needed). Use the INDIVIDUAL or DELEGATE template, fill in values, show the full payload, get explicit confirmation, then execute. In a batch, confirm row-by-row.
Step 6 — Create card. Do NOT add extra JSON fields for MCC or merchant restrictions unless the exact field is documented and verified. Process card creates sequentially — a parallel failure cancels sibling calls. Copy the Virtual or Physical template, fill in values, show the full payload, get explicit per-card confirmation, then execute. For physical cards, show the cardholder's registered address alongside the intended delivery address and confirm before issuing; never fabricate an address. Physical cards are created INACTIVE — activate after delivery.
Step 7 — Verify and confirm. Re-fetch the created card (and limits if needed), then show: card ID, nickname, type, currency, limits, status. In the final confirmation, do NOT display any part of the card number — including masked/last-4 digits. Identify cards by nickname and card ID only. Direct the user to the Airwallex Dashboard for PAN, CVV, and expiry — do NOT construct Dashboard URLs. If the user mentioned a specific vendor (e.g., "card for Adobe"), remind them of the next step: copy the card details from the Airwallex Dashboard and enter them on the vendor's site.
Update limits: Always show current spend vs limit first, then update after the user confirms.
Review spend: List transactions per card (filter by card_id) and sum amounts. Show utilization for every card (spent / limit / %); flag cards at ≥ 80% and offer to increase. Map cryptic merchant descriptors ("STRIPE* NOTION", "AMZN MKTP US") to recognizable names; when uncertain, show both ("AMZN MKTP US (likely Amazon)").
Category aggregation (e.g., "what are we spending on software?"): there is no API-level category filter. Combine two signals — the card nickname ("Adobe Subscription", "AWS Dev") and per-transaction merchant descriptors — to classify cards into user-friendly categories (Software, Travel, Office, etc.). Present a grouped summary with a per-card breakdown and category total:
Software spend this month: $847
Figma: $30 / $50 (60%)
AWS: $412 / $500 (82%) ⚠️ approaching limit
Notion: $24 / $30 (80%) ⚠️ approaching limit
GitHub: $21 / $50 (42%)
Other (3 cards): $360
If a card's category is ambiguous, ask the user rather than guessing.
Activate physical card after delivery.
Batch provisioning: follow the batch table from Step 2. Process ✅ Ready rows first — create cardholders where needed, then create cards row by row, sequentially. After ready rows are done, report results and re-present blocked rows for the user to resolve. Report each card_id with its cardholder nickname.
| Situation | Action |
| --- | --- |
| Cardholder details incomplete | Ask for missing required fields (name, email, DOB for INDIVIDUAL) |
| All required fields present | Proceed — do NOT block on optional fields unless the card type requires them (e.g., physical cards need postal_address) |
| Card creation fails | Show the full error, re-check the template includes ALL required fields, retry once; for any other rejection, stop and show the error |
| Limit format unclear | Ask: amount + currency + interval (per transaction / daily / monthly) |
| Cardholder not READY | Stop — the cardholder must reach READY before issuance (may need KYC; check the Airwallex Dashboard) |
| Physical card missing postal address | Ask for the delivery address |
| MCC / merchant restriction requested but not documented | Say support is unconfirmed; create the card without guessed restriction fields or direct the user to the Airwallex Dashboard |
| PAN / CVV / expiry requested | Refuse as a platform security boundary; direct to the Airwallex Dashboard |
| Auth expired | The connector refreshes tokens automatically; if a tool keeps failing on auth, ask the user to re-authorize the Airwallex connection |
Phase 1: Gather Requirements
understand request → build card spec → user confirms
Phase 2: Create Card
confirm live data + Issuing → match cardholder → create cardholder if needed
→ create card (sequential, per-row confirmed) → verify & confirm (no card number)
Phase 3: Manage (ongoing)
show spend vs limit → update limits → aggregate by category → activate physical cards
Adapted from Airwallex's official AgentOS card-provisioning skill, licensed under the Apache License 2.0 (see LICENSE.txt). Modified for the ChatChat Airwallex connector: CLI-specific instructions and external URLs removed, supporting reference files inlined, workflow steps aligned to the operations this connector actually exposes, and unsupported operations redirected to the Airwallex Dashboard.
tools
Use only when the user explicitly asks to stage, commit, push, and open a GitHub pull request in one flow using the GitHub CLI (`gh`).
development
Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like "the xlsx in my downloads") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved.
development
Use this skill when turning messy workout information into clear logs, comparing user-provided sessions, surfacing trends or likely PRs, and suggesting realistic next-session steps.
tools
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.