Route trace: every hop of a sale
The Routegate team5 min read
A sale that failed over is three conversations with three channels. The route trace shows each one, in order, with what it said and how long it took.
Every support conversation in this business starts the same way. A customer says their data did not arrive, and someone has to work out what actually happened to the sale. Was it sent? Which line sent it? Did the network refuse it, or did it simply never answer? Did anything try again?
On most platforms that question is answered by a person reading logs, if the logs exist. On Routegate it is answered by the sale itself. Every transaction carries its route trace: each hop it took, in order, with the outcome at that hop and how long it took to get one.
A sale is a series of attempts
When you send a vend, Routegate does not make one call and hope. It ranks the channels that can serve the sale, by your routing rules and your supply order, and works down that list. Each call to a channel is an attempt, and every attempt is written down.
The detail that matters most is when it is written down. An attempt is recorded as started before the call goes out, not after the answer comes back. If anything stops halfway, a worker restarting or a connection dropping, the record already says a call was made to that channel. A retried job reads that record before doing anything, so a sale is never quietly sent twice because a process lost its place.
Each attempt then ends with one of four outcomes:
success: the channel delivered.temporary: the channel could not serve it this time, a busy line or a vendor that could not be reached. The sale moves on to the next channel in the same call.permanent: the request itself was refused, a number that is not on the network for example. Trying elsewhere would not change the answer, so it stops.unknown: the channel took the request and then gave no clear answer. This is the dangerous one, and it is never moved on. Sending the same sale to a second channel while the first one might still deliver it is exactly how a customer gets credited twice and a business pays twice.
An unknown outcome leaves the sale pending while Routegate asks that same channel for a final answer, and on a line, reads the line's own balances to settle it. The wallet hold stays where it is until there is an answer.
What the trace shows
In the dashboard, open any transaction and the route trace is drawn as a line of hops. Each hop is labelled by the kind of route that served it:
- Line, your own line, and Backup line for a second or later line that took over.
- Vendor, one of your vendor links.
- Reserve, a line from Routegate Reserve, if you turned it on for that network.
- Test, the mock that serves test mode.
Under each hop is its outcome, the message the channel gave if it refused, and its duration: milliseconds under a second, seconds with two decimals above it. A sale that went line, busy, then vendor, delivered, reads exactly like that, left to right.
The dashboard reads the trace from the transaction's timeline endpoint, which answers with the status events and the attempts together:
curl https://api.useroutegate.com/v1/transactions/01a0d8df-0000-7000-8000-000000000001/timeline \
-H "Authorization: Bearer $DASHBOARD_SESSION" {
"success": true,
"code": "OK",
"message": "OK",
"data": {
"attempts": [
{
"attempt_no": 1,
"candidate_kind": "sim",
"provider": "simgateway",
"outcome": "temporary",
"error_message": "line busy",
"duration_ms": 1130
},
{
"attempt_no": 2,
"candidate_kind": "provider",
"provider": "vendor:01a0d8e1-0000-7000-8000-0000000000aa",
"provider_reference": "VEND-REF-123",
"outcome": "success",
"duration_ms": 1920
}
]
}
}That endpoint belongs to the dashboard and takes a signed-in session, not an API key. The hop-by-hop view is for the people operating the business, the ones answering the customer.
What your integration sees
Your code does not need every hop to do its job, but it does need to know which route served the sale. Every transaction, and every transaction webhook, carries two fields for that:
channel: what served the latest attempt.simfor your own line,stockfor Routegate Reserve,vendorfor a vendor link,testfor test mode. It is null only before the first attempt.sold_as:planwhen the sale was one of Routegate's plans,vendor_onlywhen it was one of your own vendor-only plans.
When channel is vendor, vendor_account_id names which of your vendor accounts it was. When it is stock, platform_fee is the Reserve's price for that plan, and it is zero on every other route.
curl https://api.useroutegate.com/api/v1/transactions/RG-01JB6Z \
-H "Authorization: Bearer $ROUTEGATE_API_KEY" {
"reference": "RG-01JB6Z",
"status": "successful",
"network": "mtn",
"product_code": "MTN-SME-1GB",
"price": 265.00,
"sold_as": "plan",
"channel": "vendor",
"vendor_account_id": "01a0d8e1-0000-7000-8000-0000000000aa",
"platform_fee": 0.00
}The transaction also carries its timeline: every status change, who or what made it and why. Both fields filter the transaction list too, so ?channel=vendor is every sale your vendors served, which is the first number to look at when you are deciding whether your lines need topping up.
Why it is built this way
There were two easier designs. One was to record only the final outcome. It is smaller and simpler, and it makes every support question a guess. The other was to record attempts after they finish, which looks the same until the day a process stops between the call and the record, and then the sale goes out a second time with nothing to say the first one happened.
Recording before the call costs one write per attempt. In exchange, a failed-over sale explains itself, a retry can never double send, and the answer to "what happened to this sale" is on the screen before anyone asks.
What it does not show
A composite share, one Data Share split across several of your lines at once, is a single attempt from its lead line, so the trace shows one hop for it rather than one per line. Each line's part of the delivery is still recorded, and it is what the delivered data report counts per line.
And the trace never invents an answer. A hop that is still unknown says so, and the sale beside it says pending, with the next status check already scheduled.