| 1 | --- |
| 2 | title: "Poll USDC Payment Confirmation — Watch Endpoint" |
| 3 | sidebarTitle: "GET /usdc/watch" |
| 4 | description: "Poll an active USDC payment request for on-chain confirmation. Returns confirmed status and a transaction hash once the transfer is detected." |
| 5 | --- |
| 6 | |
| 7 | After creating a USDC payment request, use the watch endpoint to check whether the corresponding on-chain transfer has been confirmed. Call this endpoint periodically with the `payment_id` returned by the create endpoint until `confirmed` is `true`. When you initialize `ProcureNetWalletClient` with `autoWatch: true`, the client handles this polling loop automatically — you only need to call this endpoint directly when you want to manage the polling lifecycle yourself. |
| 8 | |
| 9 | ## Endpoint |
| 10 | |
| 11 | ```http |
| 12 | GET /mods/procurement_wallet/usdc/watch/{paymentId} |
| 13 | ``` |
| 14 | |
| 15 | ## Authentication |
| 16 | |
| 17 | Requests require your mod API key in the `x-perplexity-mod-key` header. |
| 18 | |
| 19 | ```text |
| 20 | x-perplexity-mod-key: <your-api-key> |
| 21 | ``` |
| 22 | |
| 23 | ## Path Parameters |
| 24 | |
| 25 | <ParamField path="paymentId" type="string" required> |
| 26 | The `payment_id` returned when you created the payment request via [`POST /usdc/request`](/api/usdc-request). |
| 27 | </ParamField> |
| 28 | |
| 29 | ## Polling Behavior |
| 30 | |
| 31 | Call this endpoint on a fixed interval until the response includes `"confirmed": true`. A reasonable starting interval is every 10–15 seconds for `base` and `arbitrum` networks, and every 30–60 seconds for `ethereum` mainnet. |
| 32 | |
| 33 | <Note> |
| 34 | If `autoWatch: true` is set on your `ProcureNetWalletClient` instance, the client automatically calls `watchPayment()` after initiating a payment request. You do not need to poll manually in that case. |
| 35 | </Note> |
| 36 | |
| 37 | ## Code Example |
| 38 | |
| 39 | <CodeGroup> |
| 40 | ```typescript TypeScript |
| 41 | import { ProcureNetWalletClient } from '@procurenet/wallet-sdk'; |
| 42 | |
| 43 | const wallet = new ProcureNetWalletClient( |
| 44 | 'https://api.procurenet.io', |
| 45 | process.env.PROCURENET_MOD_KEY! |
| 46 | ); |
| 47 | |
| 48 | // Manual polling loop |
| 49 | async function pollUntilConfirmed(paymentId: string): Promise<string | undefined> { |
| 50 | const INTERVAL_MS = 15_000; |
| 51 | const MAX_ATTEMPTS = 40; // ~10 minutes |
| 52 | |
| 53 | for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) { |
| 54 | const result = await wallet.watchPayment(paymentId); |
| 55 | |
| 56 | if (result.confirmed) { |
| 57 | console.log('Transaction confirmed:', result.transaction_hash); |
| 58 | return result.transaction_hash; |
| 59 | } |
| 60 | |
| 61 | console.log(`Attempt ${attempt + 1}: not yet confirmed. Retrying...`); |
| 62 | await new Promise(resolve => setTimeout(resolve, INTERVAL_MS)); |
| 63 | } |
| 64 | |
| 65 | console.warn('Payment not confirmed within polling window.'); |
| 66 | return undefined; |
| 67 | } |
| 68 | |
| 69 | const txHash = await pollUntilConfirmed('pay_xyz789'); |
| 70 | ``` |
| 71 | |
| 72 | ```bash cURL |
| 73 | curl -X GET \ |
| 74 | https://api.procurenet.io/mods/procurement_wallet/usdc/watch/pay_xyz789 \ |
| 75 | -H "x-perplexity-mod-key: $PROCURENET_MOD_KEY" |
| 76 | ``` |
| 77 | </CodeGroup> |
| 78 | |
| 79 | ## Response |
| 80 | |
| 81 | <ResponseField name="confirmed" type="boolean" required> |
| 82 | `true` when the on-chain transaction has been detected and confirmed. `false` if the payment is still pending or has expired without a matching transfer. |
| 83 | </ResponseField> |
| 84 | |
| 85 | <ResponseField name="transaction_hash" type="string"> |
| 86 | The blockchain transaction hash for the confirmed transfer. This field is only present when `confirmed` is `true`. Store this value for auditing and reconciliation. |
| 87 | </ResponseField> |
| 88 | |
| 89 | ### Example Response — Pending |
| 90 | |
| 91 | ```json |
| 92 | { |
| 93 | "confirmed": false |
| 94 | } |
| 95 | ``` |
| 96 | |
| 97 | ### Example Response — Confirmed |
| 98 | |
| 99 | ```json |
| 100 | { |
| 101 | "confirmed": true, |
| 102 | "transaction_hash": "0x4f3c8b2a1e9d7f6c0b5a4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b" |
| 103 | } |
| 104 | ``` |
| 105 | |
| 106 | ## Expired Payments |
| 107 | |
| 108 | <Warning> |
| 109 | If a payment request expires before the transfer is confirmed, this endpoint returns `"confirmed": false` with no `transaction_hash`. The payment request cannot be reactivated. Create a new payment request via [`POST /usdc/request`](/api/usdc-request) and restart the confirmation flow. |
| 110 | </Warning> |
| 111 | |
| 112 | ## Error Codes |
| 113 | |
| 114 | | Status | Meaning | |
| 115 | |---|---| |
| 116 | | `401` | Missing or invalid `x-perplexity-mod-key` header | |
| 117 | | `404` | No payment found for the given `paymentId` | |
| 118 | |
| 119 | <Tip> |
| 120 | Once you receive `"confirmed": true`, record the `transaction_hash` in your own systems immediately for auditing and reconciliation purposes. |
| 121 | </Tip> |