| 1 | --- |
| 2 | title: "Manually Confirm a USDC Payment — Admin Override" |
| 3 | sidebarTitle: "POST /manual-confirm" |
| 4 | description: "Force-confirm a pending USDC payment request using elevated admin credentials. Use when autoWatch is disabled or automated confirmation has stalled." |
| 5 | --- |
| 6 | |
| 7 | The manual confirmation endpoint lets an administrator force a USDC payment into confirmed status without waiting for the automated on-chain watch cycle. This is an elevated operation intended for operational recovery — use it when `autoWatch` is disabled on your `ProcureNetWalletClient`, when a payment has stalled due to network delays, or when you need to confirm a payment outside the normal polling window. Both `x-perplexity-mod-key` and `x-admin-key` headers are required; this endpoint is not accessible with a mod key alone. |
| 8 | |
| 9 | ## Endpoint |
| 10 | |
| 11 | ```http |
| 12 | POST /mods/procurement_wallet/usdc/manual-confirm |
| 13 | ``` |
| 14 | |
| 15 | ## Authentication |
| 16 | |
| 17 | This endpoint requires **both** your mod API key and an admin key. Both headers must be present and valid; a missing or invalid header on either returns `401`. |
| 18 | |
| 19 | ```text |
| 20 | x-perplexity-mod-key: <your-api-key> |
| 21 | x-admin-key: <your-admin-key> |
| 22 | ``` |
| 23 | |
| 24 | <Warning> |
| 25 | Your admin key is an elevated credential that bypasses normal on-chain confirmation requirements. Never expose `x-admin-key` in client-side code, browser environments, or mobile apps. Restrict all calls to this endpoint to server-side admin workflows with access controls and audit logging in place. |
| 26 | </Warning> |
| 27 | |
| 28 | ## Request Body |
| 29 | |
| 30 | <ParamField body="payment_id" type="string" required> |
| 31 | The `payment_id` of the payment you want to manually confirm. You can retrieve this from the original create-request response. |
| 32 | </ParamField> |
| 33 | |
| 34 | ## Code Example |
| 35 | |
| 36 | <CodeGroup> |
| 37 | ```typescript TypeScript (Bridge) |
| 38 | import { VCIProcureNetBridge, ProcureNetWalletClient } from '@procurenet/wallet-sdk'; |
| 39 | |
| 40 | // Instantiate the wallet client and bridge |
| 41 | const wallet = new ProcureNetWalletClient( |
| 42 | 'https://api.procurenet.io', |
| 43 | process.env.PROCURENET_MOD_KEY! |
| 44 | ); |
| 45 | |
| 46 | const bridge = new VCIProcureNetBridge(queue, wallet); |
| 47 | |
| 48 | // manualConfirmPayment accepts a local record ID and admin key |
| 49 | const result = await bridge.manualConfirmPayment( |
| 50 | localId, // local record ID (number) |
| 51 | process.env.ADMIN_KEY! |
| 52 | ); |
| 53 | |
| 54 | if (result.confirmed) { |
| 55 | console.log('Payment manually confirmed. TX:', result.transaction_hash); |
| 56 | } else { |
| 57 | console.warn('Manual confirmation did not result in a confirmed status.'); |
| 58 | } |
| 59 | ``` |
| 60 | |
| 61 | ```typescript TypeScript (Direct Client) |
| 62 | import { ProcureNetWalletClient } from '@procurenet/wallet-sdk'; |
| 63 | |
| 64 | const wallet = new ProcureNetWalletClient( |
| 65 | 'https://api.procurenet.io', |
| 66 | process.env.PROCURENET_MOD_KEY! |
| 67 | ); |
| 68 | |
| 69 | const result = await wallet.manualConfirm( |
| 70 | 'pay_xyz789', |
| 71 | process.env.ADMIN_KEY! |
| 72 | ); |
| 73 | |
| 74 | if (result.confirmed) { |
| 75 | console.log('Transaction hash:', result.transaction_hash); |
| 76 | } |
| 77 | ``` |
| 78 | |
| 79 | ```bash cURL |
| 80 | curl -X POST https://api.procurenet.io/mods/procurement_wallet/usdc/manual-confirm \ |
| 81 | -H "Content-Type: application/json" \ |
| 82 | -H "x-perplexity-mod-key: $PROCURENET_MOD_KEY" \ |
| 83 | -H "x-admin-key: $ADMIN_KEY" \ |
| 84 | -d '{ "payment_id": "pay_xyz789" }' |
| 85 | ``` |
| 86 | </CodeGroup> |
| 87 | |
| 88 | ## When to Use Manual Confirmation |
| 89 | |
| 90 | <CardGroup cols={2}> |
| 91 | <Card title="autoWatch Disabled" icon="eye-slash"> |
| 92 | If you initialized `ProcureNetWalletClient` with `autoWatch: false`, the client will not poll for on-chain confirmations. Use this endpoint to confirm payments after you have verified the transfer through an external source. |
| 93 | </Card> |
| 94 | <Card title="Stalled Confirmation" icon="clock"> |
| 95 | If a payment remains unconfirmed longer than expected — for example, due to network congestion on Ethereum mainnet — you can use this endpoint to unblock the settlement flow. |
| 96 | </Card> |
| 97 | <Card title="Operational Recovery" icon="wrench"> |
| 98 | During incidents where the watch service is temporarily unavailable, you can replay confirmations for affected payments using this endpoint from a secure admin context. |
| 99 | </Card> |
| 100 | <Card title="Testing & Staging" icon="flask"> |
| 101 | In non-production environments where you are not routing real on-chain transfers, use manual confirmation to exercise the rest of your payment settlement workflow without waiting for blockchain activity. |
| 102 | </Card> |
| 103 | </CardGroup> |
| 104 | |
| 105 | ## Response |
| 106 | |
| 107 | <ResponseField name="confirmed" type="boolean" required> |
| 108 | `true` when the payment has been successfully force-confirmed. In most cases this will be `true` on a valid request, but may return `false` if the payment is already in a terminal failure state. |
| 109 | </ResponseField> |
| 110 | |
| 111 | <ResponseField name="transaction_hash" type="string"> |
| 112 | The blockchain transaction hash if one is available. For manually confirmed payments, this may be absent if the payment was confirmed without a corresponding on-chain transfer. |
| 113 | </ResponseField> |
| 114 | |
| 115 | ### Example Response |
| 116 | |
| 117 | ```json |
| 118 | { |
| 119 | "confirmed": true, |
| 120 | "transaction_hash": "0x4f3c8b2a1e9d7f6c0b5a4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b" |
| 121 | } |
| 122 | ``` |
| 123 | |
| 124 | ## Error Codes |
| 125 | |
| 126 | | Status | Meaning | |
| 127 | |---|---| |
| 128 | | `401` | Missing or invalid `x-perplexity-mod-key` or `x-admin-key` header | |
| 129 | | `404` | No payment found for the given `payment_id` | |
| 130 | |
| 131 | <Info> |
| 132 | If you need to confirm multiple stalled payments in bulk, iterate over the affected payment IDs and call `wallet.manualConfirm(paymentId, adminKey)` for each one. Ensure your admin workflow logs each confirmation with a timestamp for your audit trail. |
| 133 | </Info> |