check_payment_status

Check the status of an in-flight UPI payment for a Food 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 a Food 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.

See check_payment_status in actionComing soon

Example

const result = await client.callTool({
  name: "check_payment_status",
  arguments: {
    paasId: "PAAS_TXN_12345",
    orderId: "241234567890",
    addressId: "addr_01HXYZ",
    lat: 12.9352,
    lng: 77.6245,
  },
});

Parameters

ParameterTypeRequiredDescription
paasIdstringyesThe payment TRANSACTION id from the place_food_order 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 place_food_order response instead of calling this tool.
orderIdstringcond.orderId echoed from place_food_order. Passed through for auto-confirm.
addressIdstringcond.Echo from place_food_order — passed through for auto-confirm.
cartIdstringnoEcho from place_food_order — passed through for auto-confirm.
latnumbercond.Echo from place_food_order — passed through for auto-confirm.
lngnumbercond.Echo from place_food_order — passed through for auto-confirm.
finalizebooleannoINTERNAL — set by the widget at the polling cap to finalize the order. Do NOT set this yourself.

Food — always echo orderId + addressId + lat + lng (and cartId if you have it). The server uses these fields to run confirm_order under the hood: without them, the server cannot reconcile the order and it stays pending even for a paid, placed order. On a widget host the picker echoes them for you; a self-driven poll must pass them itself.

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; see below.)

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.statusTerminalMeaningWhat your client does
success / paidsuccessPayment done; order auto-confirmed (data.confirmed: true, data.orderStatus).Show "order placed". Do NOT call confirm_order.
failedfailurePayment didn't go through; any debit is refunded (~4 days).Offer retry → get_payment_options.
refund-initiatedfailureMoney was debited but a refund has already been started; order not placed.Tell the user a refund is on its way. Do NOT retry.
cancelledfailureOrder cancelled; refund if already paid.Tell the user it was cancelled.
cart_changedfailureCart price/stock changed → order NOT placed (not a payment failure).Ask the user to review the cart, then place again.
pending / ""not terminalStill 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 place_food_order 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

FieldValue
Namecheck_payment_status
MCP ServerFood
EndpointPOST mcp.swiggy.com/food
StagePayment
Behaviourread-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 place_food_order, 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 call confirm_order yourself 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_options to 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_food_order. (Only call confirm_order in the rare case where you hit your own polling cap while still pending.)