# Build the submission queue before adding a sync spinner

A reliable offline implementation records intent before sending, keeps attachments recoverable and clears pending work only after saving a server receipt.

By Cobnex editorial. Published 2026-09-10. Updated 2026-09-11.

## Define a command that survives a restart

A queue entry needs more than a URL and a request body. Include a stable command ID, account scope, source record version, schema version and attachment references. Keep retry attempts as operational metadata rather than generating a new business identity for each attempt.

The following example describes application data, not a browser API. The report revision identifies the office record used when the technician began their work.

```json
{
  "commandId": "inspection-example-41",
  "organisationId": "demo-maintenance",
  "reportId": "report-example-9",
  "baseRevision": 7,
  "schemaVersion": 2,
  "state": "pending",
  "attachmentIds": ["photo-example-1"],
  "attempts": 0
}
```

Commit this entry alongside the local report changes. Show the saved state after the local transaction completes. A failed transaction should leave the user on an editable screen with an honest error, not a success toast.

## Transfer files without losing the command

Upload attachments under identifiable records and record their confirmed remote references. If the app closes after a photograph uploads, the next attempt should discover that result or safely repeat the upload against the same identity.

The report submission should refer only to attachments the server can verify. An upload request finishing does not establish that a later report commit succeeded. Keep the two outcomes separate in storage and diagnostics.

Allow a user to remove a photograph from an unsubmitted draft. For a submission already in flight, use an explicit amendment or cancellation policy rather than mutating the meaning of an existing command ID.

## Save the receipt before cleaning up

On acceptance, persist the server result and update the queue atomically where the local schema permits. A restart between receipt handling and cleanup must not turn a completed command into an unrelated new submission.

For a timeout, query the command outcome or retry idempotently. For a version conflict, retain the draft and enter a review state. For expired authentication, pause until the correct account returns. These are different recovery paths and should have different labels.

## Make foreground recovery sufficient

Wire synchronisation to an explicit action and to suitable foreground lifecycle events. Background execution can improve convenience where supported, but the user must be able to finish the workflow without it.

Run the same queue processor through every entry point. Separate processors with different state rules tend to race over the same entry, especially when a tab and service worker are both active.

## Sources

- [MDN: IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)
- [MDN: Background Synchronization](https://developer.mozilla.org/en-US/docs/Web/API/Background_Synchronization_API)
