A create request timed out: how to retry without creating duplicates
When you create a record (an invoice, a bill, a journal entry) and the request times out, you can't tell from the error alone whether the provider created it. If you just send it again, you may end up with two records in your customer's accounting system. This article explains how to retry safely.
How to recognise an uncertain create
Apideck waits up to 28 seconds for the provider to respond. If the provider doesn't answer in time, or the connection drops, Apideck returns a 504 (timeout) or 502 (connection dropped).
For create requests, the error tells you that the result is unknown:
{
"status_code": 504,
"type_name": "ConnectorExecutionError",
"detail": {
"retriable": false,
"outcome": "uncertain"
}
}
(The example is abbreviated. The full error includes more fields.)
outcome: "uncertain"means the provider may or may not have created the record.retriable: falsemeans don't retry blindly. Retry with an idempotency key, or check first.
Apideck never retries a create on its own. Each create is sent to the provider exactly once, so a duplicate can only come from a retry on your side.
The easiest way: send an Idempotency-Key
For the operations below, you can add an Idempotency-Key header to the create. If it times out or fails with an uncertain outcome, resend the same request with the same key, and the record is created at most once.
POST /accounting/invoices
Idempotency-Key: 8f14e45f-ceea-467a-9a7c-3c8e5d2b1a90
Connector | Operations | Key remembered for |
|---|---|---|
QuickBooks Online | Journal entry and invoice creates, invoice updates | 24 hours |
Intuit Enterprise Suite | Journal entry and invoice creates, invoice updates | 24 hours |
NetSuite | Journal entry creates | 24 hours |
Sage Intacct | Journal entry, invoice and credit note creates | 24 hours |
Sage Intacct REST | Journal entry, invoice and credit note creates | 1 hour |
Xero | Bill creates | 6 minutes |
How it works:
Generate a new key for each record you create, for example a UUID. A key is 1 to 255 printable characters.
Retry within the window. After the time in the table, the same key counts as a new request, so retry promptly. This matters most for Xero, where the window is only 6 minutes.
With a key, a timed-out create is safe to retry. The error then shows
retriable: true.
What the responses mean:
Response | Meaning |
|---|---|
409 | The first attempt with this key is still running. Wait a moment and retry. |
422 | You reused the key with a different body. Each key belongs to one request. |
400 | This connector or operation doesn't support the key. Nothing was created. Retry without the header and use the steps below. |
Two more things to know:
A key stays tied to its first request, even if that request was rejected. If you fix a rejected request, send it with a new key.
Some connectors repeat the original error to retries with the same key. If retries keep returning the same error, check whether the record exists before sending it again with a new key.
Without an idempotency key
For other connectors and record types, use your own identifier to check before retrying:
Give every record your own unique identifier before you send it. Usually that's the document
numberorreference(see below).Send the create.
On a timeout, a dropped connection or any 5xx, look the record up by that identifier.
If it's there, the create succeeded. Take the
idfrom the lookup and carry on.If it's not there, it's safe to retry the create.
Treat a 500, 502 or 503 on a create the same way, even when the response has no outcome field. The provider may still have saved the record.
How to look a record up, by provider
QuickBooks Online
Invoices:
GET /accounting/invoices?filter[number]=INV-1001Journal entries:
GET /accounting/journal-entries?filter[number]=JE-1001QuickBooks limits
numberto 21 characters. Longer values are rejected, not shortened.QuickBooks doesn't reject a duplicate number on journal entries (with default company settings), so making each number unique is up to you.
Xero
Invoices:
GET /accounting/invoices?filter[number]=INV-1001Bills:
GET /accounting/bills?filter[bill_number]=BILL-1001, orfilter[reference]=…Xero allows duplicate bill numbers, so a lookup can return more than one bill. Pick the one that matches your other details.
NetSuite
Set
referenceon the record. NetSuite stores it as its External ID and requires it to be unique.If you retry with the same
referenceand the first attempt did succeed, NetSuite rejects the retry with "This record already exists" instead of creating a duplicate. That error tells you the original create went through.This applies to invoices, bills, payments, bill payments, credit notes, refunds and expenses.
Invoices can also be looked up with
GET /accounting/invoices?filter[number]=….
Sage Intacct REST
Invoices and credit notes: look up by
filter[number]=….Journal entries:
numbercan't be set when you create one, because Sage assigns it when the entry is posted. Usesource_idas your own identifier instead. To check whether a create landed, list recent journal entries withfilter[updated_since]and match onsource_id.
Other providers
If the resource supports a number or reference filter, use it. You can see which filters each connector supports in the connector coverage. Otherwise, list recently created records with filter[updated_since] set to just before your create, and match on your own identifier.
Keep your own timeout above 30 seconds
Set your HTTP client's timeout above 30 seconds. Then you receive Apideck's answer, including the outcome information, instead of timing out first.
