UAH Mono Payouts
Use this skill when a user asks you to convert USDT BEP20 into a UAH payout through a verified exchange route.
MVP scope is narrow:
- Source asset: USDT.
- Source network: BEP20 / BNB Smart Chain.
- Destination: UAH.
- Contact source: agent-owned context, usually
./mandate-wallet/contacts.csv
.
- Human approval: the user's explicit "create order" confirmation in chat. A separate web approval link is optional and not part of the default flow.
- Status monitoring: poll the MCP order status every 60 seconds.
Do not use this skill for other tokens, other chains, portfolio management, generic wallet operations, or unrelated exchange flows.
Prerequisites
Installing this skill only installs these instructions. It does not guarantee that the payout MCP server is connected in the agent client.
MCP endpoint:
text
POST https://mcp-wallet.mandate.md/mcp
Use it as either:
- a native remote HTTP MCP server when the agent client supports MCP servers, or
- a direct HTTPS JSON-RPC endpoint when the agent has bash, curl, HTTP, browser automation, or another HTTP-capable tool.
Claude Code setup:
bash
claude mcp add --transport http uah-mono-payouts https://mcp-wallet.mandate.md/mcp
claude mcp list
Then restart Claude Code or run
and confirm the server is connected.
If a client says
Native hook relay unavailable
, treat it as a client MCP bridge problem, not as proof that the hosted payout service is down. If you have any HTTP-capable tool, continue by calling the endpoint directly with JSON-RPC.
If native MCP tools are unavailable and you have no HTTP-capable tool, fail closed and tell the user:
text
The payout skill is installed, but this agent client cannot reach the payout MCP server: native MCP is unavailable and I do not have an HTTP-capable tool for direct JSON-RPC. I cannot quote, create an order, or provide deposit instructions safely.
Mandatory Rules
You must fail closed.
- Never invent exchange rates, deposit addresses, comments, order IDs, expiry times, or support contacts.
- Never scrape exchange or provider pages yourself.
- Never tell the user to send funds before MCP returns
payment_instructions.can_send: true
.
- Never tell the user to send funds when the order expires in under 60 seconds.
- Never continue if MCP is unavailable or order status is unclear.
- Always show the network as , not just .
- Always tell the user that each payment is subject to AML screening by the exchange/payment providers.
- Always warn that wrong network, wrong amount, missing memo/comment, or reused expired orders can lose funds.
- Always preserve the route label, order ID, order link, created time, expiry time, and support path returned by MCP. Do not replace them with guessed provider details.
- Always show when MCP returns it, including when it appears inside
payment_instructions.fields.exchanger_order_url
.
- For user-facing payment instructions, show the exchanger link as a Markdown link label, not as a raw technical URL.
- Do not expose provider JSON/API status URLs as the user-facing order verification link. Use the human order page URL returned by MCP.
- Always tell the user to verify the deposit address, exact amount, network, expiry, and provider status on the exchanger order page before sending.
- If MCP returns deposit instructions without an exchanger verification link, say that the exchanger verification link was not returned. Do not invent one.
Mandatory Flow
- Parse the user's payout intent.
- Resolve the recipient from your own context, usually
./mandate-wallet/contacts.csv
.
- If no contact matches, ask the user to add the recipient locally or provide exact Monobank UAH details.
- If multiple UAH accounts match, ask which account to use.
- If the UAH amount is ambiguous, ask whether the user means:
- receive that UAH amount, or
- send USDT worth that UAH amount.
- Build a object with exact bank details.
- Call .
- Present the best route briefly.
- Ask the user once whether to create the order if their prior message did not already say to proceed.
- Call .
- If the response includes
payment_instructions.can_send: true
, show those exact payment instructions immediately, including the exchanger verification link if returned.
- If
payment_instructions.can_send
is false or missing, do not send funds; show the returned blocker.
- Call every 60 seconds until the order reaches a final state. After payment is detected, MCP will notify the payout route when supported and then report the route-side provider status.
Final states:
If status becomes
, show the support details returned by MCP and stop automatic retries.
After the user sends funds, keep polling. Report these non-final states plainly:
- : payment was detected on-chain; waiting for route-side confirmation.
- : payment was acknowledged; UAH payout is being processed.
- : stop automatic retries and show MCP support details.
Contact Resolution
Contacts usually live in the agent's local project:
text
./mandate-wallet/contacts.csv
Supported UAH rails:
For
, the contact must include a card number.
For
, the contact must include IBAN, EDRPOU, and recipient full name.
Pass selected details to MCP as
. For a Monobank card:
json
{
"rail": "monobank_uah_card",
"currency": "UAH",
"card_number": "4441111122223333",
"recipient_full_name": "Swift Adviser",
"telegram": "@SwiftAdviser",
"email": "swiftadviser@gmail.com"
}
For a Monobank IBAN:
json
{
"rail": "monobank_uah_iban",
"currency": "UAH",
"iban": "UA...",
"edrpou": "12345678",
"recipient_full_name": "Swift Adviser LLC",
"telegram": "@SwiftAdviser",
"email": "swiftadviser@gmail.com"
}
If the exchange flow asks for Telegram, use the contact Telegram if present. If missing and the flow requires a fallback, use:
If the exchange flow requires email and the contact has none, use:
Do not invent bank details.
Amount Semantics
Plain language like this is ambiguous:
text
переведи мне USDT в 5000 гривен
Ask one clarifying question:
text
5000 UAH should be the amount received, or should I send USDT worth about 5000 UAH?
If the user says they want to receive 5000 UAH, use:
json
{ "amount_semantics": "receive_fixed_uah" }
If the user says they want to send a 5000 UAH equivalent, use:
json
{ "amount_semantics": "send_uah_equivalent" }
For a request like:
text
выведи 500 usdt bep20 на mono uah
Treat
as fixed USDT to send:
json
{
"amount": "500",
"amount_currency": "USDT",
"amount_semantics": "send_uah_equivalent",
"source_token": "USDT",
"source_network": "BEP20"
}
MCP Tools
Use the MCP server:
text
https://mcp-wallet.mandate.md/mcp
Expected tools:
render_payment_instructions
Direct HTTPS JSON-RPC
Use direct JSON-RPC when native MCP tools are unavailable but HTTP calls are possible. Every direct call is a
to:
text
https://mcp-wallet.mandate.md/mcp
List tools:
json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}
json
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "quote_uah_payout",
"arguments": {
"payout": {
"rail": "monobank_uah_card",
"currency": "UAH",
"card_number": "4441111122223333",
"recipient_full_name": "Swift Adviser",
"telegram": "@SwiftAdviser",
"email": "swiftadviser@gmail.com"
},
"amount": "300",
"amount_currency": "USDT",
"amount_semantics": "send_uah_equivalent",
"source_token": "USDT",
"source_network": "BEP20"
}
}
}
json
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "create_uah_payout_order",
"arguments": {
"quote_id": "quote-id-returned-by-quote_uah_payout",
"payout": {
"rail": "monobank_uah_card",
"currency": "UAH",
"card_number": "4441111122223333",
"recipient_full_name": "Swift Adviser",
"telegram": "@SwiftAdviser",
"email": "swiftadviser@gmail.com"
},
"user_timezone": "UTC"
}
}
}
json
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "get_order_status",
"arguments": {
"order_id": "order-id-returned-by-create_uah_payout_order"
}
}
}
Read the tool result from the JSON-RPC
field. If the response has
, stop and report the structured blocker. Do not guess.
Optional compatibility helper for deployments that expose server-side demo contacts. Do not depend on it for the normal personal-agent flow.
If
is true, ask the user which account to use before quoting.
Use after payout details and amount semantics are known. Include the selected
object when available.
The backend ranks verified exchange options. You do not manually choose rates from websites.
Show one best route unless the user asks for alternatives.
If the response is:
json
{ "error": "quote_unavailable", "quotes": [] }
do not create an order and do not tell the user to send funds.
If
diagnostics.supported_ranges
is present, use it to explain the supported send ranges for enabled routes. For
reason: "amount_below_min"
, suggest the smallest
returned by MCP. For
reason: "amount_above_max"
, suggest splitting the payout or using an amount at or below
. If
is empty, say that MCP currently has no enabled route for this method and amount.
Example:
text
No safe quote for 30 USDT.
This route is currently available from 230.41 to 10000 USDT. I did not create an order, and you should not send funds.
Use after the user has implicitly confirmed the route or asks to proceed. Include the same
object used for quoting.
This creates an exchange order and normally returns
immediately. Treat the user's "create the order" message as the approval step. Do not ask the user to open a separate approval link unless the user or deployment explicitly requires web approval.
Only pass
require_web_approval: true
for a legacy/manual review flow. In that mode, wait for approval and then call
render_payment_instructions
.
In the default flow, show deposit instructions only from
and only when
payment_instructions.can_send
is true.
When present, preserve and show:
- top-level
- top-level
payment_instructions.fields.exchanger_order_url
The exchanger order URL is the user's independent verification page for deposit details. Do not hide it, but render it as a Markdown link label instead of printing a raw URL when writing to the user.
If
payment_instructions.message
is present, show it exactly. Do not rebuild it from
, because the message already has the intended emoji, title, bold labels, hidden order link, and expiry wording.
render_payment_instructions
Use only for an order that MCP has already approved or moved to
.
Usually you do not need this tool because
returns
. Use it only to re-render instructions for an already-created order.
If the tool returns
, show its message and do not add payment instructions.
Poll every 60 seconds after payment instructions are shown.
If
or
provider_status_checked_at
is present, include it in status updates. Do not infer route-side progress from a balance probe.
If
is present, include it in status updates when the user is checking whether the order is still safe to fund.
Stop polling when the order is final.
Use only as a diagnostic probe for a known deposit address. It does not replace order status and must not be used to infer approval.
User-Facing Messages
Reply in the user's conversation language. Keep messages compact and decision-oriented:
- Start with the outcome.
- Show the exact route as a bold line.
- Put amount, network, order ID, and verification link in scan-friendly lines.
- Before deposit, end with a short checklist.
- Never bury safety details in a long paragraph.
Route Found
text
Route found.
**119.84 USDT BEP20 -> Monobank UAH**
You receive: **5000 UAH**
You send: **119.84 USDT**
Network: **BEP20 / BNB Smart Chain**
Route: **route label returned by MCP**
Rate: **41.72 UAH**
Quote expires: **18:42 WITA**
AML: payment is subject to provider AML screening.
Say "create order" and I will create the exchange order and return exact deposit instructions.
Payment Instructions
text
✅ **Order ready**
**119.84 USDT BEP20 -> Monobank UAH**
🧾 **Order:** SL-483920
🔗 **Check order:** [open order page](https://exchanger.example/order/SL-483920/)
💵 **Amount:** **119.84 USDT**
⛓️ **Network:** **BEP20 / BNB Smart Chain**
🏦 **Address:** `0x...`
📝 **Comment/memo:** `SL-483920`
⏳ **Expires:** today at 18:42 WITA (10:42 UTC)
Check the order page once before sending.
Send the exact amount. Do not use another network. Do not reuse this order after expiry.
Payment is subject to provider AML screening.
If
payment_instructions.fields.comment
is null or empty, omit the
line entirely. Do not write
.
If MCP does not return
, keep the same structure but replace the verification line with:
text
Verification link: MCP did not receive an exchanger order link for this order.
Under 60 Seconds
text
Do not send.
This order expires in under 60 seconds. I will create a fresh one if you want to continue.
Completed
text
Payment received.
Order: SL-483920
Tx: 0x...
Status: completed
Support Needed
text
Support needed.
Order: SL-483920
Route: route label returned by MCP
Reason: partial payment detected
Support: https://...
What Not To Do
Do not say:
text
Send any amount around 120 USDT.
Say:
text
Send exactly 119.84 USDT.
Do not say:
Say:
text
Send USDT on BEP20 / BNB Smart Chain.
Do not say:
text
The order is probably still valid.
Say:
text
I need to check the order status before you send.
Fail-Closed Behavior
If any required tool fails, stop the payment flow and explain the precise blocker:
- contacts unavailable
- contact details incomplete
- quotes unavailable; if MCP returns supported ranges, show those ranges and ask whether the user wants to retry with a supported amount
- order creation failed
- approval expired
- order expires in under 60 seconds
- payment status unclear
- provider status unclear
Never continue by guessing.