Seven Days Is How Long the Phone Waits
The offline queue on an AWRA phone has four ceilings, six automatic retries and one rule about the eighth day. All of them are numbers you can plan a shift around.
- Written by
- AWRA OpsHub TeamOperations Insights
- Issued
- Build
- Initial
A van goes out of coverage at nine and comes back at four. Seven hours of check-ins, transfers and counts sat on a handset with nowhere to go. What happens next is not "it syncs" — it is a specific sequence with specific limits, and the limit worth knowing before you plan around it is that the phone stops replaying an entry once it is seven days old.
Offline capture is the easiest thing in the world to demonstrate and the hardest thing to be honest about. The demonstration is thirty seconds long: turn off the network, record a movement, turn the network back on, watch it land. The honest version is a set of ceilings — how old, how many, how large, how many attempts — because every one of them is a decision somebody made, and a system that has not made them has simply not met the case where they matter.
This post is the whole set, with the figures as they are in the app today.
What the phone is allowed to hold
Two of those look arbitrary and are not. Three hundred entries is well above a real shift, which queues tens — a thousand is not heavy use, it is a bug, and a cap is how the app finds out. Six megabytes is a long way under what Android allows the app to store, and deliberately so: the queue is one record rewritten whole every time anything is added, retried or reclassified, so a queue large enough to be interesting is a queue that makes every one of those writes slow enough to feel in your hand.
| Ceiling | Where it is | What happens when it is reached |
|---|---|---|
| Age of an unsent entry | Seven days from capture | The entry is held rather than sent. It is intact, and a person decides. |
| Number of queued entries | 300 | New capture is refused with a message naming the limit, so nothing is silently dropped. |
| Size of the queue | 6 MB serialised | Same refusal. Whichever of the two is closer is the one you are told about. |
| Warning threshold | 80% of either | The readiness screen starts saying so while there is still room to act. |
| Automatic retries | 6 answered failures | The entry stops retrying itself and waits for a person. Nothing is discarded. |
| Cached reads for offline use | 40 per view, 60 per record type, 21 days | The oldest are dropped. These are copies of things the server already has. |
The last row is a different kind of limit
The first five are about work you captured — writes that exist nowhere else until they land, which is why none of them is ever thrown away. The last is about copies of things the server already knows: the report you opened yesterday, the asset you looked at last week. Dropping one of those costs a reload. Confusing the two is how an app ends up either forgetting a shift of work or carrying every record it has ever displayed.
A queued entry is not a sent entry
This is the single most important sentence in offline operation, and most of the design follows from taking it seriously. Between capture and landing there is a window in which the phone believes something and the server has not agreed to it yet.
-
Capture
The entry is written to the device with a per-device key of its own, so the same entry arriving twice is recognised as one thing rather than posted twice.
-
Drain
When there is a connection, entries are sent in order. The order matters: a transfer approved before it is dispatched is not the same as the reverse.
-
Classify
Whatever comes back is sorted into one of four outcomes — landed, refused, try again later, or the connection never got an answer. What the phone does next depends entirely on which.
-
Reconcile
A refusal is surfaced with the server's own sentence in front of it, on a screen where a person can read the values that were captured and decide.
A decision recorded offline is recorded, not applied. If a colleague decides the same record first, the queued copy is refused — and the confirmation on the phone says so at the moment it is captured, rather than a screen later.
That wording is shared across every screen that can queue a verdict, for one reason: it is the part that must not drift. A screen that said "Approved" would be telling a reviewer something the server never agreed to, and the correction would arrive much later, out of context, in a queue screen.
It is also why creating a record and deciding about one are treated differently. A queued decision always names something the server already has — a transfer, an adjustment, a count line, a leave request — so the worst case is a refusal. That is a much smaller promise than inventing a new record on a device and hoping the identifier is still free when it arrives.
A lost connection and a refusal are not the same failure
Every offline system has to answer one question well: did the request reach the server at all? The answer changes what should happen to everything else in the queue, and getting it wrong is how a shift of good work ends up in a list for somebody to rescue by hand.
It never reached the server
- No answer came back at all — the connection is gone.
- The drain stops. Nothing behind this entry would land either, so trying is wasted battery.
- The whole drain backs off, not the individual entry: fifteen seconds, doubling to a five-minute ceiling. A connection flapping in a warehouse produces an event every few seconds, and the app must not chase each one.
- Nothing is marked failed. A van out of coverage for a shift has captured work with nothing wrong with it.
The server answered
- A refusal in the 400s is terminal — the values are wrong and no amount of waiting fixes them. It goes to the failed list with the server's own message.
- A 500 stays pending: the captured values are not wrong, so a later attempt may well work. The drain steps over it rather than stopping, so one broken endpoint cannot block everything behind it.
- An expired sign-in is a 400-series answer and is deliberately excluded from the terminal rule — otherwise a token that lapsed overnight would fail every entry on the device by morning.
- A payload this device refuses to send at all is terminal too. With no status it looks exactly like a lost connection, and treating it as one used to hold up the entire queue behind an entry that could never clear.
That fourth point on the right is the subtle one, and it is worth a moment because it is the shape of most offline bugs: two completely different situations that are indistinguishable at the point where you have to decide what to do. One is a van in a valley. The other is a malformed entry that will be malformed forever. Both arrive with no status code.
The retry schedule, in full
When the server answers and the answer is a 500, the entry waits before its next attempt, and the wait doubles. Six answered failures is the ceiling on unattended trying.
Waits between attempts, after n answered failures
Long enough to ride out a backend restart without anybody noticing; short enough that a genuinely poisoned entry is in front of a human within the same shift. The count is kept only against attempts the server answered — an attempt that never left the device says nothing about whether the entry can ever succeed, so a morning out of coverage does not burn the allowance.
The spread is the detail that gives away whether an offline queue has met a real warehouse. It is derived from the entry's own key rather than drawn at random, because the thing that needs decorrelating is not the attempts — it is the devices. When an access point drops, every handset in the building is retrying on exactly the same schedule and would arrive back together, which is how a recovering server gets knocked over by the fleet that was waiting for it.
Seven days, and what happens on the eighth
Seven days is a working week: a shift, a weekend, and a day of slack for somebody who came in from the field on Friday and did not open the app until Monday. Past that, the shelf the entry was captured against has almost certainly moved.
So on the eighth day the entry is held. It is not sent, and it is not deleted. The word "expired" is deliberately absent from the message the operator reads, because nothing was thrown away: the entry is intact, the values are intact, and the only thing that changed is that a person now has to agree to send it. The message says what happened to the stock, not to the request.
The values still match
Release it and send
Once you have released a held entry, the age rule gets out of the way permanently for that entry — otherwise you would be looking at a Retry button that visibly does nothing.
The shelf has moved
Discard and recapture
The right answer for most week-old movements. You are not losing information — you are declining to apply a photograph of last Tuesday to today's count.
You cannot tell
Read it against the record
A held entry keeps its full payload and its capture time, so it can be compared with what the register says now before anybody commits to either.
This is the rule that used to be missing, and its absence was invisible in exactly the way that matters: the readiness screen displayed how old each entry was, and no code anywhere read that number. A check-in captured three weeks earlier would replay straight into today's stock. Every screen was correct, the age was on display, and nothing consumed it.
Questions worth asking any vendor about offline
Four questions, and how to read the answers
How old can an unsent entry get before it stops replaying by itself?
What you will hear
"It syncs whenever it reconnects." Sometimes with pride.
How to read it
There is no age rule, which means a fortnight-old count can land on today's stock. Ask what the number is. If there is a number, the case has been thought about; if there is not, it has not come up yet.
What happens when the server answers with an error rather than not answering at all?
What you will hear
A single word: "it retries".
How to read it
The useful answer distinguishes the two. A refusal and a lost connection need opposite responses, and a system that treats them alike either gives up on good work or retries bad work forever.
How many entries can the device hold, and what happens at the limit?
What you will hear
"As many as you like."
How to read it
Then the limit is the device's storage and you will find it on the worst possible day. A stated cap with a refusal message is a better answer than no cap.
When I approve something offline, what does the screen say?
What you will hear
"Approved."
How to read it
This is the one to press hardest on. Nothing is approved until the server agrees, and a screen that says otherwise is describing a state that may never exist. The honest wording is that the decision is recorded and not yet applied.
Our take
Offline capability is not a feature you can evaluate by watching it work, because the demonstration only ever covers the case where everything is fine. What separates a system that survives a warehouse from one that demos well is a set of numbers somebody was willing to commit to: how old, how many, how large, how many attempts, and what happens at each edge. Ours are seven days, three hundred entries, six megabytes and six answered failures, with a held state rather than a discard at the boundary. You may reasonably want different ones for your operation. What you should not accept from anybody is that there are none.
Offline behaviour is the most commissionable part of this product
Every ceiling on this page is a constant with a reason beside it, which makes each of them a candidate for being yours instead of ours. These are the requests that come up most, and none is speculative.
Retry windows you set
Your own attempt allowance and doubling schedule, per workspace or per operation type, so a fleet on a private network with a known maintenance window is not tuned for a public one.
The record as it was at capture
A server-side snapshot taken when a device fetches a record, so the conflict screen can show what changed since you looked rather than only what the two sides hold now.
Master records created offline
Customers, items and suppliers raised on a disconnected device, with identifiers reconciled on arrival and duplicates surfaced rather than merged silently.
Queue telemetry you can see
Queue depth, held entries and refusal reasons across your whole fleet on one screen, so a device that has been quietly failing for three days is visible before somebody reports it.
Any of these is a defined piece of build rather than a research project — the constants, the classification and the held state are already in one place, which is what makes changing who owns them a small job.
Tell us what your shift actually looks likeTry it with the network switched off
It is a thirty-second test and it tells you more than a demonstration will. Capture a movement in flight mode, reconnect, and watch what the app says at each step — particularly what it claims about a decision it has not sent yet.
Talk to us about field operationsFrequently asked questions
What happens to my work if I am offline for longer than a week?
Nothing is deleted. On the eighth day an unsent entry stops replaying on its own and is held instead, with a message saying how old it is and why it is waiting. You open it, compare the captured values with what the register says now, and either release it to send or discard it and recapture. Once you have released one, the age rule does not hold it again.
Why is there a limit on how many entries the phone will hold?
Because the alternative limit is the device storage, and you meet that on the worst possible day with no warning. Three hundred entries is far above a real shift, which queues tens, so reaching it is a signal that something is wrong rather than that you have been busy. The app warns at eighty per cent of whichever ceiling is closer and refuses new capture with a message naming the limit rather than dropping anything.
If I approve a request while offline, is it approved?
No, and the app says so at the moment you do it. The decision is recorded on the device and sent when there is a connection. Until the server accepts it, nothing has been applied — and if a colleague decides the same record first, the queued copy is refused and you can discard it. That wording is shared by every screen that can queue a verdict, precisely so it cannot drift into something more comfortable.
How does the app know a lost connection from a rejection?
By whether an answer came back at all. No answer means the connection is gone, so nothing behind that entry would land either and the whole drain stops and backs off. An answer is a statement about that one entry and says nothing about the next, so the drain carries on: a rejection in the 400s is terminal, and a server error in the 500s stays pending and is stepped over rather than blocking the queue behind it.
Does an expired login fail everything I captured overnight?
No. An expired sign-in arrives as a 400-series answer, which is the family that would otherwise be treated as final, so it is deliberately excluded. Left in, a token that lapsed while the phone was on a charger would mark a whole shift of captured work as failed and hand somebody a list to rescue by hand in the morning.
What is cached for offline reading, and how long does it last?
Read-only copies of things the server already has: the last forty of each kind of view you opened, the last sixty records of each type, and nothing older than twenty-one days. These are separate from captured work and are dropped freely, because losing one costs a reload rather than a movement. Captured work is never dropped on age — it is held.
Can I change any of these limits for my organisation?
Not as a setting today, and it is one of the more frequently asked pieces of commissionable work. The constants and the classification live in one place with their reasons written beside them, which is what makes moving ownership of them a small job rather than a rewrite. A fleet on a private network with a known maintenance window has a good case for its own numbers.