check_payment_status
Check the status of an in-flight UPI payment for an Instamart order (one status read). The confirmation widget polls automatically and auto-confirms on success, so you rarely need to call this yourself.
Check the status of an in-flight UPI payment for an Instamart order — a single read of the live payment status. The payment confirmation widget polls this automatically on a fixed cadence and auto-finalizes it on any terminal outcome — confirms on success, and finalizes-to-failed on failed/timeout, so in normal flows you do NOT need to call it at all. It's a long-poll (the server holds ~19s, returning early on a status change; fast only on a transient cache miss), so never tight-loop it — hammering it stresses the payment cache. For an ad-hoc user question (“did my payment go through?”), call it at most once and report the result. A self-driven headless client that owns the flow instead follows the bounded polling procedure below — poll until data.terminal, re-poll gently on pending, capped — never a tight loop.
Example
const result = await client.callTool({
name: "check_payment_status",
arguments: {
paasId: "PAAS_TXN_12345",
orderId: "241234567890",
},
});Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
paasId | string | yes | The payment TRANSACTION id from the checkout response (field paasId). NOT a payment-method name — never pass "PayWithQR", "UPI", "UPIIntent", or an app package id here. If you don't have a real paasId, re-read the checkout response instead of calling this tool. |
orderId | string | cond. | orderId from the checkout response — optional for a bare status read, but pass it so the widget can auto-confirm on SUCCESS (else a paid order can stay pending). |
finalize | boolean | no | INTERNAL — set by the widget at the polling cap to finalize the order. Do NOT set this yourself. |
Session credentials (user identity, access token) are supplied automatically by the authenticated MCP session - you do not pass them in the tool call. See Authenticate.
Response
For any resolved status read, check_payment_status returns success: true — a terminal payment failure is still a successful status read. Read the outcome off data.status, not the top-level success. (A transport/tool error — e.g. a missing/bad paasId — returns the standard success: false failure envelope instead.)
A terminal read (here, a refund):
{
"success": true,
"data": {
"paasId": "PAAS_TXN_12345",
"status": "refund-initiated",
"terminal": true,
"isTerminalSuccess": false,
"isTerminalFailure": true
},
"message": "↩️ REFUND-INITIATED — a refund has been initiated for this payment."
}
A confirmed success also carries confirmed: true + orderStatus (the order was finalized server-side, so you do NOT call confirm_order):
{
"success": true,
"data": {
"paasId": "PAAS_TXN_12345",
"status": "success",
"terminal": true,
"isTerminalSuccess": true,
"isTerminalFailure": false,
"confirmed": true,
"orderStatus": "CONFIRMED"
},
"message": "✅ Payment SUCCESS — order CONFIRMED automatically."
}
How your client integrates
On a widget host you don't call this at all — the picker polls and auto-confirms for you. Only if you self-drive (headless): poll until data.terminal === true, then branch on data.status:
data.status | Terminal | Meaning | What your client does |
|---|---|---|---|
success / paid | success | Payment done; order auto-confirmed (data.confirmed: true, data.orderStatus). | Show "order placed". Do NOT call confirm_order. |
failed | failure | Payment didn't go through; any debit is refunded (~4 days). | Offer retry → get_payment_options. |
refund-initiated | failure | Money was debited but a refund has already been started; order not placed. | Tell the user a refund is on its way. Do NOT retry. |
cancelled | failure | Order cancelled; refund if already paid. | Tell the user it was cancelled. |
cart_changed | failure | Cart price/stock changed → order NOT placed (not a payment failure). | Ask the user to review the cart, then place again. |
pending / "" | not terminal | Still processing. | Keep polling; don't act yet. |
So a client knows a refund was initiated when data.status === "refund-initiated" (with data.isTerminalFailure === true). The human-readable message mirrors the same outcome for chat surfaces.
Cap your polling — don't loop forever. A pending result (including a transient cache miss) means "not resolved yet," not "poll harder." Honor the window the checkout response gives you (maxTimeToPollForInMs; roughly ~1 min for a UPI-app intent, ~5 min for scan-QR) and re-poll gently on pending — the status read is a long-poll, so space your calls rather than tight-looping. If you reach your cap while still pending, call confirm_order once to finalize — the backend marks the order failed if the payment is still non-terminal, and a late success is reconciled server-side. Then tell the user it timed out and offer a retry. (On a widget host the picker does all of this for you.)
On a transport/tool error (missing or bad paasId, network failure) you get the standard failure envelope instead:
{
"success": false,
"error": { "message": "description of what went wrong" }
}
See Error codes for the full catalogue.
Details
| Field | Value |
|---|---|
| Name | check_payment_status |
| MCP Server | Instamart |
| Endpoint | POST mcp.swiggy.com/im |
| Stage | Payment |
| Behaviour | read-only |
Agent guidance
How Swiggy agents and orchestration logic use this tool. Surface these expectations in your prompts or tool-selection policies.
⚠️ THE PAYMENT WIDGET POLLS AUTOMATICALLY. After checkout, the confirmation widget polls this and auto-finalizes on any terminal outcome (confirms on success; finalizes-to-failed on failed/timeout), server-side. You normally do NOT need to call this at all. Do NOT tight-loop it — it's a long-poll, and hammering it stresses the payment cache.
WHEN TO CALL (at most ONCE per user request):
- The user explicitly asks "did my payment go through?" / "check status" → call ONCE and report the result. Do NOT immediately re-call on PENDING.
- Terminal SUCCESS / PAID → normally already confirmed for you server-side (
data.confirmed); only callconfirm_orderyourself if the result says auto-confirm couldn't run (missing args). - Terminal FAILED → do NOT confirm; tell the user it failed and offer
get_payment_optionsto retry. - Terminal REFUND-INITIATED → do NOT confirm; tell the user a refund was initiated.
- PENDING → tell the user it's still processing and will update automatically. Do NOT loop.
Next in this journey →
On terminal SUCCESS the order is already confirmed for you server-side (data.confirmed: true) — do not call confirm_order yourself. Continue to tracking: track_order. (Only call confirm_order in the rare case where you hit your own polling cap while still pending.)