From sanabot-skills
Patterns for answering questions about the user's Sana card — metadata (status, last4, expiry), spending power (limits, charges, balance due), card transactions, full card number + CVV on explicit request, and guiding a user through end-to-end card registration and usage. Load this when the user asks about their Sana card, what they've spent, how much they can still spend, their full card details, or how to get/use a card. Requires the using-sanabot skill for connection/error handling context.
How this skill is triggered — by the user, by Claude, or both
Slash command
/sanabot-skills:sanafi-cardThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
How to answer Sanafi card questions safely using the Sanabot MCP tools. For the full tool catalogue, scopes, and error handling, see the `using-sanabot` skill — load it alongside this one.
How to answer Sanafi card questions safely using the Sanabot MCP tools. For the full tool catalogue, scopes, and error handling, see the using-sanabot skill — load it alongside this one.
Privacy rules before anything else:
get_card_sensitive tool, and only when the user explicitly asks ("what's my card number?", "I need my CVV to pay"). They are NOT included in get_card or get_card_balance.get_card_sensitive proactively or "just in case", never echo the PAN/CVV into summaries, titles, or anything that persists, and don't repeat them back more than needed to answer. Treat them like a password.| User says | Tool |
|---|---|
| "Show me my card" / "What card do I have?" / "Is my card active?" | get_card |
| "What's my full card number?" / "I need my CVV to pay" | get_card_sensitive (explicit request only) |
| "How much can I spend on my card?" / "What's my available credit?" | get_card_balance |
| "What did I spend this month?" / "Show my card transactions" | get_transaction_history with context: 'card' |
| "How much do I owe?" / "What's my balance due?" | get_card_balance (the balanceDueUsd field) |
| "How do I get a card?" / "How do I register?" / "How do I use my card?" | No tool — guide them (see "Guiding card registration & usage") |
| "Top up my card" / "Fund my card" / "Deposit USDC to my card" | card_deposit (see prerequisites below) |
| "Withdraw from my card" / "Move money off my card" | Not available through Sanabot — Sana app only |
get_cardReturns one or more cards' agent-safe metadata:
[
{
"type": "virtual",
"status": "active",
"last4": "1234",
"expirationMonth": 12,
"expirationYear": 2027
}
]
That's the metadata projection: get_card itself never includes cardNumber or cvc. If the user explicitly wants those, use get_card_sensitive (below) — don't try to read them from get_card. Cardholder name/billing address are never exposed.
If get_card returns an empty array, the user doesn't have a Sana card yet — guide them through registration (see "Guiding card registration & usage").
get_card_sensitiveReturns the full PAN and CVV of the user's active card. Call this only on an explicit user request for their full card details:
{
"type": "virtual",
"status": "active",
"last4": "1234",
"expirationMonth": 12,
"expirationYear": 2027,
"cardNumber": "4111111111111111",
"cvc": "123"
}
Rules:
get_card (metadata); only "show my full card number / CVV" → get_card_sensitive.read:card_sensitive scope (NOT covered by read:card, read, or read:all) and an active card. Returns null when there's no active card.403 means the key lacks read:card_sensitive — tell the user to regenerate the key with that scope ticked. Note get_card / get_card_balance may still work (they only need read:card); only the full-credential reveal needs the extra scope.You cannot register a card for the user — KYC and the activation payment require the user themselves in the dashboard. But you can walk them through it. Registration is fully self-serve on the web at https://sana.bot/gateway/app/card (no mobile app needed).
When get_card returns [] or get_card_balance returns null, the user has no card yet — offer to guide them. The end-to-end flow, all on that one page:
Using the card once issued:
card_deposit ("top up my card with 25 USDC"), or the user can send to cardContract.depositAddress from get_card_balance.get_card_sensitive on request).get_card_balance for spending power, get_transaction_history with context: 'card' for purchases.Keep guidance concise and link the page; don't try to collect KYC details or the payment through chat.
get_card_balanceReturns the balance breakdown plus contract addresses (useful if the user wants to fund the card):
{
"creditLimitUsd": 500,
"pendingChargesUsd": 12.34,
"postedChargesUsd": 56.78,
"balanceDueUsd": 69.12,
"spendingPowerUsd": 430.88,
"cardContract": {
"proxyAddress": "...",
"programAddress": "...",
"depositAddress": "..."
},
"tokens": ["USDC"]
}
Field cheat sheet for answering common questions:
spendingPowerUsd (credit limit minus pending + posted)balanceDueUsdcreditLimitUsdpendingChargesUsdcardContract.depositAddress, paired with the tokens whitelist (only fund with tokens in that list — e.g. USDC)If get_card_balance returns null, the user doesn't have an active card yet.
get_transaction_history with context: 'card'The transaction-history tool covers both crypto and card activity. To restrict to card spending:
// arguments
{ "context": "card", "page": 1, "limit": 20 }
limit maxes at 100. Default page size is 20 — usually enough for "what did I spend this week?"
For mixed contexts ("what's happened in my account?"), omit context and you'll get both crypto and card events interleaved.
"How am I doing on my card?"
get_card to confirm a card exists + status.get_card_balance for spending power.spendingPowerUsd + balanceDueUsd. Mention pending charges if non-zero."Where can I send USDC to top up?"
get_card_balance → read cardContract.depositAddress and the tokens list.tokens whitelist."Show me what I spent on coffee last week."
get_transaction_history with context: 'card'.card_depositTriggers: user wants to top up, fund, or deposit to their card balance.
Prerequisites before calling this tool:
https://sana.bot/gateway/app/api-keys). Without it, the call returns Agent signing not enabled for this user (status 403).write:card_deposit scope (not included in read:all). A key without it is rejected with a 403 — scope enforcement lives in the Sanafi API, not the gateway.Transaction exceeds per-transaction cap (status 403) or Transaction exceeds daily cap (status 403).Phase 1 restriction: USDC is the only supported deposit token. Attempts with other tokens will fail.
Input: { amount, idempotency_key? } — amount is in USDC. Pass an idempotency_key (UUID) if retrying after a network error to avoid double-deposits.
Flow:
card_deposit with the amount (and idempotency key if retrying).Error handling. The agent receives the upstream message with the HTTP status appended, i.e. <message> (status N):
Agent signing not enabled for this user (status 403) → "Enable agent signing in the Sanafi dashboard first."403 with no agent-signing or cap message → the key is missing the write:card_deposit scope: "Add write:card_deposit to your API key."Transaction exceeds per-transaction cap (status 403) / Transaction exceeds daily cap (status 403) → "This deposit exceeds your agent's spending cap — raise the cap in the dashboard or wait for the 24h window."Signing service unavailable (status 502) → "Sanafi's signing service hit an error. Retry with the same idempotency key."Card withdrawal (card balance → crypto wallet) requires the user's wallet to sign a structured message. Privy's delegated signing keys cannot fulfill this — it's outside what agent signing covers.
If the user asks you to withdraw from their card:
"Card withdrawal happens in the Sanafi mobile app — I can't do it for you from here."
Don't attempt to construct a workaround or suggest an alternative tool. Just redirect clearly.
get_card_sensitive only on a direct request, hand the values over once, and never persist or repeat them. Cardholder PII (name/phone/address) is still never available — don't promise it.tokens whitelist from get_card_balance. Deposits in non-whitelisted tokens may be lost.read:card scope — recommend re-generating the key with the right scope. A 403 on card_deposit may mean a missing write scope or delegation not set up — check the specific error code.npx claudepluginhub sanafi-onchain/sanabot-skills --plugin sanabot-skillsCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.