Verify Payments
Verification is the step that makes your integration safe. The callback is a forgeable browser redirect and the webhook is a thin trigger — neither carries amounts, and neither should be trusted for fulfilment. The verify endpoint is the single source of truth.
The call
curl -X POST https://api-staging.finomesh.com/api/v1/payments/{paymentId}/verify \
-H "X-Api-Key: $FINOMESH_API_KEY"
No request body. The payment must belong to your gateway — a foreign or unknown ID returns 404 not_found.
The response
{
"success": true,
"data": {
"paymentId": "0d9f3a64-4f0c-4b6e-9f6e-2f4f4c1b8a21",
"status": "ACCEPTABLE",
"checkoutMode": "HOSTED",
"isVerified": true,
"verifiedAt": "2026-06-12T10:21:43Z",
"amount": "49.99",
"currencyCode": "USD",
"amountUsd": "49.99",
"txHash": "0xabc…",
"expectedAmount": "51.69",
"receivedAmount": "51.42",
"assetSymbol": "USDT",
"deviationUsd": "-0.27"
}
}
| Field | Meaning |
|---|---|
status | The gateway's verdict. Fulfil on SUCCESS / ACCEPTABLE; withhold on MISMATCH / EXPIRED; wait on PENDING. |
isVerified, verifiedAt | Whether (and when) this payment has been verified. Flips on the first verify of a determined payment. |
amount, currencyCode, amountUsd | Your own invoice figures — use them to sanity-check the order you're about to fulfil. |
expectedAmount, receivedAmount, assetSymbol | The comparable crypto pair: what the customer was quoted vs. what landed on-chain, both in assetSymbol units. Present only once a resolving transaction exists (SUCCESS / ACCEPTABLE / MISMATCH); absent for EXPIRED and PENDING. |
deviationUsd | Signed USD value of received − expected — negative for underpayment, positive for overpayment, "0" for an exact match. The single number to branch on for MISMATCH review. |
txHash | The resolving on-chain transaction. |
All monetary values are strings — parse them with a decimal type, never a float.
Semantics worth knowing
- Idempotent — call it as many times as you like; repeat calls return the current authoritative state.
verifiedAtkeeps the timestamp of the first verification. - Verifying a
PENDINGpayment is a no-op — you getstatus: "PENDING"withisVerified: false. Poll-verify is therefore a valid (if blunt) integration style; the webhook is the push alternative. - Verification quiets the webhook — once a payment is verified, any still-undelivered webhook for it is skipped. If your callback handler verifies promptly, you will often never receive the (now redundant) webhook.
- Ownership-scoped — the payment must belong to the API key's gateway, otherwise
404. Cross-tenant payment IDs cannot be probed.
Recommended flow
on callback OR webhook (both give you paymentId):
resp = POST /payments/{paymentId}/verify
switch resp.data.status:
SUCCESS, ACCEPTABLE → mark order paid, fulfil
MISMATCH → hold order, alert staff (deviationUsd tells the story)
EXPIRED → cancel order
PENDING → do nothing yet (trigger was early or forged)
Related
- Reconciliation — catching payments a webhook never landed for.
- Verify payment reference — full field and error tables.