Poll payment intent status
GET /payment_intents/:id while waiting for processing or a provider callback.
If your integration cannot host a webhook endpoint, poll for status. Webhooks are still preferable in production.
Endpoint
GET /payment_intents/:idScoped to the authenticating merchant and the key's stamped environment. An intent from another merchant or environment returns 404 not_found.
Request
curl -s "$API_BASE/payment_intents/dord_01HZX..." \
-u "$API_PUBLIC_KEY:$API_SECRET_KEY"const auth =
"Basic " +
Buffer.from(`${process.env.API_PUBLIC_KEY}:${process.env.API_SECRET_KEY}`).toString("base64");
async function getPaymentIntent(id: string) {
const res = await fetch(`${process.env.API_BASE}/payment_intents/${id}`, {
headers: { Authorization: auth },
});
if (res.status === 404) return null;
if (!res.ok) {
const { error } = (await res.json()) as { error: { code: string; message: string } };
throw new Error(`${error.code}: ${error.message}`);
}
return res.json();
}Success
{
"object": "payment_intent",
"id": "dord_01HZX...",
"amount": "10000",
"currency": "MYR",
"country": "MY",
"environment": "test",
"status": "succeeded",
"merchant_reference": "order_12345",
"payment_method": "MY_FPX",
"next_action": null,
"failure_code": null,
"failure_message": null,
"created_at": "2026-05-09T12:00:00.000Z",
"updated_at": "2026-05-09T12:01:00.000Z"
}Statuses are processing, requires_action, succeeded, failed, and expired.
Polling pattern
async function waitForTerminal(id: string, deadlineMs = 60_000) {
const start = Date.now();
let delayMs = 500;
while (Date.now() - start < deadlineMs) {
const intent = await getPaymentIntent(id);
if (!intent) return null;
if (!["processing", "requires_action"].includes(intent.status)) return intent;
await new Promise((resolve) => setTimeout(resolve, delayMs));
delayMs = Math.min(delayMs * 2, 5_000);
}
return null;
}Cap polling. Past 60 seconds the right answer is usually to wait for a webhook or inspect the intent in the dashboard.