Accept crypto with CoinGate
Accept crypto with confidence using everything you need in one platform.
Debugging CoinGate Callbacks: When Nothing Arrives
The payment went through. The customer is happy. Your system never marked the order paid, and now you are reading logs trying to reconstruct what happened.
The basics of callback handling are covered elsewhere. Verify the token, acknowledge fast, whitelist the source IPs, keep your handler idempotent, all of that sits in our developer integration guide. This is the sequel, for when the basics are in place and something still went wrong. CoinGate has two API endpoints that turn callback debugging from guesswork into a lookup, and it helps to know they exist before you start bisecting your own code.
Reconstructing paid orders from your own logs? Get an API token and ask the API instead.
A quiet callback is usually not a lost callback
Before you go hunting, know that a callback which has not landed yet is very often still on its way. CoinGate retries, and it retries for a long time.
A delivery counts as delivered when your application returns an HTTP 200 or 204, and CoinGate waits 20 seconds for that response. Anything else and it backs off on a published ladder:
- Retries 1 to 5: every 1 minute
- 6 to 10: every 5 minutes
- 11 to 15: every 10 minutes
- 16 to 20: every 20 minutes
- 21 to 25: every 30 minutes
- 26 to 30: every 1 hour
- 31 to 35: every 5 hours
- 36 to 40: every 1 day
- 41 and above: the callback is terminated and no more will be sent
Read the bottom of that ladder again. The last five attempts are a day apart, so the tail of a failing callback stretches across days, not minutes. If your endpoint was down for an hour this morning, the callback is almost certainly still coming. Do not go fulfilling orders by hand from the dashboard until you have checked.
What stops delivery for good
A few responses do not get a ladder at all. The docs call these conditions for canceling and terminating a payment notification, and there are five of them:
- A 301 or 302 redirect. Typically an http payment gateway callback URL redirecting to https. Put the final protocol in the URL you register.
- A 401. Your server wants authentication, which usually means the whole site or environment is password protected.
- A 403. Something is explicitly blocking us. Check the firewall, the CDN and the IP allowlist.
- A destination in the TOR network. Not allowed.
- A destination on a private network. Localhost, 127.0.0.1 and internal IPs are not permitted.
If your callback URL sits behind anything that answers with a redirect or an auth challenge, that is your bug, and no amount of waiting will fix it. This is also the one class of failure that looks identical to a network problem from the outside, which is exactly why the next two endpoints are useful.
Did CoinGate even try? The callbacks list
Your first debugging question is not what your handler did. It is whether a callback was ever attempted. GET /v2/api_callbacks answers that, newest first:
curl "https://api.coingate.com/v2/api_callbacks?callbackable_type=Order&callbackable_id=74" -H "Authorization: Token YOUR_API_TOKEN"
Four query parameters, no more. callbackable_type and callbackable_id narrow the list to a single resource, an order in this case, and page and per_page handle paging at a maximum of 100 records per page. The type accepts eight values: Order, Billing::Payment, MerchantRefund, MassPayout::Payout, BillingRequest, SendRequest, BillingContact and CryptoAddress. Two of them carry a double colon, so watch your shell quoting.
Each entry looks like this:
{
"id": 5521,
"callbackable_type": "Order",
"callbackable_id": 74,
"status": "retrying",
"retries": 7,
"created_at": "2026-07-27T10:14:59Z"
}
Two fields do the work. retries is a count of attempts made so far, not a list of them. And status is your diagnosis. The reference lists eight values and leaves them to speak for themselves:
successandfailureare the two ends. Failure is what a callback looks like after 40 attempts without success.retryingandsendingmean it is in flight. Checkretriesagainst the ladder above to see how long it has been trying.readymeans it is queued.without_callbackpoints at a resource with no callback URL on it, so nothing was ever sent. It is a surprisingly common answer to “why did it not fire”.canceledandskippedpoint back at the five stop conditions above.
Most of the time the status on its own tells you which side of the wire the problem is on. Ours, in transit, or a configuration you never set.
What did my server actually return? The retries endpoint
If a callback was attempted and keeps failing, you need to see what your own endpoint said back on each attempt. Take the id from the list and ask for its retries:
curl "https://api.coingate.com/v2/api_callbacks/5521/retries" -H "Authorization: Token YOUR_API_TOKEN"
Every individual attempt comes back, newest first, with no paging on this one:
{
"id": "b1d2e3f4-...",
"api_callback_id": 5521,
"response_status": "500",
"callback_params": { "status": "paid", "order_id": "74", "token": "[FILTERED]" },
"created_at": "2026-07-27T10:20:59Z"
}
This is the good part. response_status is the HTTP code your merchant endpoint returned on that attempt, and it comes back as a string rather than a number, so compare it as one. A row of 500s means your handler is throwing. A 404 means the URL is wrong. A 403 means something in front of your app is blocking us, and remember that one also ends delivery rather than retrying it.
callback_params is the payload we sent, which is what you want for replaying the request locally. Note the token: sensitive values are masked with [FILTERED], so you can read the shape of the payload but you cannot lift a working token back out of the log. That is deliberate, and it is also why verification has to happen in your handler at the time of delivery rather than after the fact.
Between the two endpoints you can answer the whole question. Did we try, what state is it in, and what did your server say on each try. That is the entire debugging loop, and none of it requires opening a support ticket.
Replay a real payload instead of making a test payment
Reading the log tells you what happened. Fixing your handler needs something to fire at it, and making live payments to trigger callbacks gets old fast.
There is a re-send tool in the dashboard under Integrations, API Management, Callback Testing. It sends the latest real callback payload for a specific resource again, based on that resource’s current state, so if the order is paid you get the standard paid payload. The payload cannot be customized. If you need to fire something bespoke at your endpoint, that is a job for curl, HTTPie or Postman, not for the dashboard.
Turn the list endpoint into a monitor
The smarter use of these endpoints is not waiting for a problem. Poll GET /v2/api_callbacks on a schedule, filter for anything in failure or sitting a long way up the retry ladder, and alert yourself.
A callback on its fifteenth retry has been failing for well over an hour. That is a signal your endpoint has been unreachable for most of the morning, and you would rather hear it from your own monitor than from a customer asking where their order is. The same pattern works for every resource type the API sends callbacks about, so one monitor covers automated payouts, refunds and recurring billing as well as checkout orders.
A short pre-flight checklist
Most callback failures come down to a handful of causes. Rule these out before you dig:
- Your URL answers with a redirect, a 401 or a 403. Any of those cancels delivery outright.
- Your endpoint is not publicly reachable, because it is on localhost or inside a private network.
- A firewall or a CDN is blocking our servers. Callbacks come from the addresses listed at api.coingate.com/v2/ips-v4, one per line, no authentication needed to read it. Sandbox has its own list, and the lists can change, so read them rather than hardcoding what you saw once.
- No
callback_urlwas set on the resource in the first place, which shows up aswithout_callback. - Your handler does too much work inline and blows the 20 second window, so a successful fulfillment still reads as a timeout and gets retried.
That last one deserves a note. If you are running heavy work inside the callback, acknowledge first and process after. It is the same discipline that keeps payment channel callbacks from stacking up under load.
Frequently asked questions
My callback never arrived. Is it lost?
Probably not yet. Failed callbacks are retried up to 40 times on a backing-off ladder whose final attempts are a day apart, so the window runs for days. Check GET /v2/api_callbacks for that resource first. A status of retrying means it is still coming.
How do I see why my callback endpoint is failing?
Call GET /v2/api_callbacks/{id}/retries. Each attempt carries the response_status your server actually returned and the payload that was sent, which is usually enough to tell a 500 in your handler apart from a wrong URL or a block in front of it.
What makes CoinGate stop retrying?
Five conditions cancel and terminate delivery instead of retrying it: a 301 or 302 redirect, a 401, a 403, a destination in the TOR network, and a destination on a private network such as localhost. If your payment gateway callback URL sits behind a redirect or an auth challenge, that is the first thing to fix.
Why does the token show as [FILTERED] in the retries?
Sensitive values inside callback_params are masked in the stored retry records. You get the rest of the payload for debugging, and you cannot read the verification token back out of the log.
Can I get alerted about failing callbacks?
Nothing built in, but the list endpoint is enough to build it. Poll it on a schedule and alert on anything in failure or stuck deep in retrying. That turns a silent delivery problem into a signal you see before your customers do.
Wrapping up
Callback debugging stops being painful once you know where the record is kept. The list tells you whether we tried and where it stands. The retries tell you what your own server said back, attempt by attempt. Keep the ladder in your mental model so you do not over-react to a callback that is simply mid-backoff, watch for the redirect and the 401 and the 403 that end delivery early, and consider polling the list as a monitor rather than reaching for it as a debugger. Full field reference sits in the API callbacks documentation.
The information is there. You just have to ask for it.
Building a crypto payment integration you can actually operate? Start with us.
Accept crypto with CoinGate
Accept crypto with confidence using everything you need in one platform.