A timeout does not tell you whether a record was created
Imagine FileMaker sends a request to create a remote job. The service creates JOB-701, but the connection ends before FileMaker receives the reply. The local screen says Timeout. A second create request with a new identity might produce JOB-702.
Treat this as an uncertain operation. Save enough local state before the first request to identify the same intended action later, then use the service’s documented idempotency or lookup contract to recover.
This design reduces duplicate risk under explicit assumptions. It does not promise exactly-once delivery, and a FileMaker transaction cannot roll back a record already created by a remote service.
Create a durable operation record first
| Field | Purpose |
|---|---|
| OperationUUID | Identity generated once for this intended business action |
| BusinessRecordUUID | The local record the action belongs to |
| EndpointVersion / Method | The operation contract used |
| Request snapshot or secure reference | The exact approved request, access-controlled |
| RequestHash | Detect an accidental change; not a replacement for the request |
| State | Pending, Sending, Confirmed, Rejected or Uncertain |
| RemoteID / LastAttemptAt / SafeErrorCode | Recovery evidence without tokens |
Commit this record before sending anything. If that commit fails, stop. Keep secrets outside the ledger, and store only the sensitive request data genuinely required for recovery with appropriate access and retention. A global variable does not survive a restart.
Prevent a double-click from creating two operation records for the same action. Claim the business action under a lock, enforce its unique operation association, and reuse it on repeated clicks. Only one worker may send that operation at a time; a stale Sending state after a crash requires reconciliation.
Write down the provider’s recovery promise
For the fictional example, assume POST /jobs accepts an Idempotency-Key, remembers each key for 24 hours, rejects a changed body under the same key, and returns the original JOB-701 for an identical retry. Also assume an authenticated lookup by the client operation reference is available. These are teaching assumptions, not features every API has.
Real providers differ in retention, endpoint coverage, response replay and error handling. For example, Stripe documents its own idempotency behavior. That is evidence that the contract is provider-specific, not a specification for our fictional jobs endpoint.
Before implementing recovery, answer: how long is the key valid, must the body remain byte-identical, can a status lookup lag behind creation, and what happens if the same key is reused after expiry?
Use the saved identity on every authorized retry
FileMaker script fragment · use the persisted operation
# Read an already-committed operation record.
Set Variable [ $operationID ; Value: Operations::OperationUUID ]
Set Variable [ $requestBody ; Value: Operations::RequestJSON ]
If [ IsEmpty ( $operationID ) or IsEmpty ( $requestBody ) ]
Exit Script [ Text Result: "operation_not_prepared" ]
End If
Set Variable [ $options ; Value:
"--request POST --header " & Quote ( "Content-Type: application/json" ) &
" --header " & Quote ( "Idempotency-Key: " & $operationID ) &
" --data-binary @$requestBody --connect-timeout 5 --max-time 15" ]
# Do not send unless this provider supports the stated key contract.This fragment deliberately does not generate a UUID. Generating one on every retry would defeat the purpose. Validate that OperationUUID is an internal UUID and that the stored request still matches the approved operation. Configure the actual authenticated endpoint separately.
Choose the next action from the outcome
| Evidence | Next state and action |
|---|---|
| Documented success with verified remote ID | Confirmed; save RemoteID and the local business result together |
| Documented rejection that guarantees no creation | Rejected; show the reason and require a deliberate correction |
| Timeout, connection loss or ambiguous server error | Uncertain; look up or retry only under the provider contract |
| Remote success but local confirmation save fails | Uncertain locally; recover using the same operation reference |
| Key expired and no reliable lookup | Uncertain; manual reconciliation, no automatic new create |
An HTTP status alone may not prove rejection. Use the provider’s semantics. A 202 response may mean work is queued; keep it pending until the documented status mechanism confirms the result.
Do not hold a database transaction open throughout a slow network call. Use short local transactions to claim and persist state, perform the request, then use another short transaction to save the confirmed result. Recovery must cover the gaps between those steps.
Reconcile the lost reply
Workflow pseudocode · recovery without a new identity
Load the saved operation and claim its recovery work.
If Confirmed: show its saved remote ID; do not resend.
If Uncertain or stale Sending:
Query the provider using the saved operation reference, if supported.
If a matching completed operation exists: save that same remote ID.
If still pending: leave pending and check later under provider rules.
If lookup is inconclusive:
Retry the unchanged request with the same key only while
the documented idempotency guarantee remains valid.
Otherwise: keep Uncertain and ask for manual reconciliation.
If local confirmation cannot commit: preserve the operation identity;
do not label the business record as safely sent.A zero-result lookup is not always proof that creation failed; some services are eventually consistent. Do not turn it into permission for a fresh key unless the service explicitly guarantees that interpretation.
If the user changes the business request, treat that as a new deliberate operation only after resolving or canceling the earlier one through supported procedures. Keep the relationship between the two operations visible.
Test the gaps, not just the success response
Use a controlled mock service that can create JOB-701 and drop the reply. Verify that recovery returns JOB-701 again, with one remote object. Then force a local validation failure after remote success and repeat recovery.
Also test a changed body under the same key, an expired key, a delayed lookup, two Send clicks and an app restart in Sending state. These are acceptance cases for implementation, not tests already performed by this article.
The useful result is not merely “retry succeeded.” It is evidence that the local record and remote operation agree, or a clear unresolved state that prevents a second unintended create.
References
Primary references checked during preparation. Provider-specific contracts and native behavior still require review for your implementation.