Export failures
An export that fails is annoying and it is also the safe outcome. The alternative, in a product that posts to a general ledger, is a bill that appears twice or against the wrong account, and that costs considerably more to unpick than a red row on a screen. A failed batch is not the same as an empty one, though: it means at least one invoice needs a person, and any bill in it that did post is already in your ledger.

This page covers what makes an export fail, how to tell which cause you have, and how to retry without posting anything twice. It assumes you can run an export, which is an admin or a controller. The fixes that touch a connection — reconnecting, re-syncing reference data, mapping a vendor — are admin-only, so a controller may have to hand those over.
The two ledgers fail differently
Most of the causes below are the same wherever the bill was going. A few are not, and knowing which ledger you are on saves reading the wrong section.
- QuickBooks Online. If a bill with the same document number already exists in the target company, QuickBooks refuses the create, and that check is the ledger's own. Not every QuickBooks Online edition includes Accounts Payable either, so a connected company that cannot hold bills at all is not something a retry can fix. Outside the US, an invoice that states tax with no mapped purchase tax code waits before it is sent.
- Xero. A supplier bill's invoice number is not unique in Xero, so Xero will create a second bill without complaint, and what prevents a double post there is ours rather than the ledger's. Stated tax with no matched tax rate waits too, and it waits in every country, because a Xero line expresses tax through its tax rate and nothing else.
Where to look first
The Exports screen lists runs and their batches with a status each and, on a failed batch, the reason recorded against it. Above the list, a "needs attention" summary gathers the individual invoices that are parked across recent failed batches, each with its own reason.
Read the item-level reasons before the batch-level one. A batch that says some of its bills need attention is a summary; the invoice rows underneath say what is actually wrong, and different invoices in one batch can fail for different reasons.
Cause 1: inletAP did not know which ledger to use
This one stops the invoice before anything is sent. No ledger was involved, nothing is wrong with the invoice, and there is nothing to retry until you change a setting. The row starts with one of three words.
| You see | What happened | Fix |
|---|---|---|
no_default_ledger | Two or more connections of the same kind are switched on and none of them is marked as the one unrouted bills go to. Posting to both would record the same expense twice, so the invoice waits. | On Settings → Connectors, switch on Unrouted bills go here for exactly one of them. Then retry the batch. |
unrouted_entity | A slice of a split invoice belongs to an ownership entity that names no ledger of that kind, and there is no default to fall back to. The whole invoice waits for that kind of ledger, not just the slice. | On Settings → Split coding, in Ownership entities → ledgers, pick the ledger for that entity. Or mark a default as above. |
not_entitled | Your plan does not include that kind of ledger. The row exists so you can see that the ledger did not receive the bill. | Upgrade, or switch that connection off on Settings → Connectors so it stops producing rows. |
None of the three is a refusal by a ledger, so "Open the invoice" is not the fix here — the invoice is fine. Each fix is a setting: two of them on a connection card, and unrouted_entity on the entity's own row in Ownership entities → ledgers. Once the setting is right, the same invoice goes out in the next batch. None of this can happen to a workspace with one ledger switched on; it becomes possible the moment a second one is.
Cause 2: the connector is not connected
Connectors are in one of three states.
| State | Means | Fix |
|---|---|---|
connected | Working | — |
action_required | The grant is no longer valid — usually an expired or revoked authorization | Reconnect the ledger |
disconnected | No connection at all | Connect it |
A batch you aim at a named ledger is refused before it is queued when that connection is not connected, so you get one clear sentence at the point of clicking rather than a failed batch; an ordinary run leaves it out altogether. Where an invoice fans out across several ledgers, a destination that cannot accept bills parks that one bill only — the others still get theirs. Authorizations expire, somebody changes the password at the ledger, or a colleague disconnects the app while tidying.
Reconnecting is an admin action, on Settings → Connectors. There is a connection test on each card that calls the ledger with the stored credentials and reports what came back, which is the quickest way to confirm you have fixed it before you retry forty bills. See connecting QuickBooks and connecting Xero.
One edition-level case belongs here rather than under validation: not every QuickBooks Online edition includes Accounts Payable. If the connected company cannot hold bills at all, the connection is recorded as such and no amount of retrying will change it. The fix is on the QuickBooks side.
Cause 3: a vendor or account the ledger does not have
inletAP maps your local records to their counterparts in the ledger — vendors and contacts, accounts and account codes, classes, departments, tracking options, tax codes and tax rates. If an invoice is coded to something with no counterpart, that invoice parks.
This happens for three reasons:
- The vendor is new and has never existed in that ledger.
- Somebody renamed or merged an account there, and the mapping now points at nothing.
- The reference data was pulled in before the account existed.
The fix is on Exports → Ledger setup, one panel per connected ledger: pull the reference data in again with Read lists from QuickBooks Online or Read lists from Xero, then map the record by hand where the automatic match cannot find it. Both are admin actions. Mapping is per ledger, so a vendor matched in one company is still unmatched in the next one. An unmapped vendor is the one export failure a customer can fix entirely without us, so it gets its own notification rather than being one line inside "six bills need attention" — and it is keyed on the vendor, so ten invoices from the same missing vendor are one email and one fix.
Cause 4: the bill is already in your ledger
If a bill with the same document number already exists in the target company, QuickBooks refuses the create. That is usually correct: either the invoice was posted from inletAP before, or somebody entered it by hand. inletAP then looks for the bill that already exists rather than forcing a second one — and adopts it only when the vendor on it matches too, because the same document number under a different vendor is a different bill. Where the vendor does not match, the invoice parks for a person.
That check is QuickBooks' own, and Xero does not have one. A supplier bill's invoice number is not unique in Xero, so Xero will create a second bill without complaint. What prevents a double post there is ours, in three layers: one posted bill per invoice per ledger is recorded and never posted again; every retry reuses the same idempotency key; and after any answer we cannot read — a timeout, a 5xx, a key Xero says it has seen with a different body — inletAP asks Xero whether the bill is already there before it creates anything, matching the invoice number, the total and the date together.
It only ever adopts a bill inletAP itself created. A bill you keyed into Xero by hand is deliberately not matched, because attaching your document to somebody else's record is the worse mistake. So if you entered it manually, mark the invoice exported rather than retrying it.
Each export item records the ledger's own identifier for the bill it produced, so the question "did this actually post" has an answer on the document rather than being a matter of opinion.
If the duplicate is legitimate — two genuinely different invoices carrying the same number, which some vendors manage — the invoice number needs to differ before it will post.
Cause 5: the ledger rejected the data
A ledger validates what it is given, and rejects what it will not accept. Common shapes:
- A required value the company's settings demand and the invoice does not carry — a tax code on a company that tracks sales tax, for example.
- A class or department on a company that does not track them.
- A posting date inside a closed period.
- A value outside a limit the ledger enforces.
We do not print invented error codes here, because the exact text and code come from the ledger and change with its platform. What inletAP records is the reason the ledger gave, against the item that failed, together with the correlation identifier that the ledger's own support asks for if the problem turns out to be at their end.
The fix is on the invoice: correct the coding, the date or the missing value, and retry. A validation rejection will not resolve itself, so inletAP parks the item rather than retrying it endlessly and burning your ledger's rate limit.
Cause 6: one ledger took it and another did not
An approved invoice posts to every ledger you have switched on, one bill in each, and a split invoice posts one bill per company where its ownership entities keep separate books. Those posts are independent, so they can also fail independently: two bills posted, one parked. The document's status is then partially_exported. It takes no split to reach this state — QuickBooks and Xero both switched on is enough.
The export screen shows one row per destination, each with its own amount, its own outcome and, where it posted, a link to the bill. Read those rows rather than the batch summary; the destination that failed usually failed for one of the ordinary reasons above, and the interesting detail is which one.
The most common cause is worth knowing in advance: reference data is mapped per ledger. A vendor, an account or a class that exists in your main company does not exist in the second one until it is mapped there. So a fan-out often fails first in the newest ledger, for an unmapped vendor, and the fix is on that ledger's panel.
Retry re-runs the batch, and a bill that already holds the ledger's own id is skipped rather than posted again — so in practice only the destination that failed is attempted. Retry the whole run does the same across a run of several batches: only the failed ones are re-queued. Each bill also carries its own idempotency key, so a retry cannot duplicate a posting that already succeeded. When the parked destination goes through, the document moves to exported.
If you would rather not retry, the honest fallback is to enter the missing bill by hand in that ledger and note it — but check the amount against the row on the export screen, because that figure is that ledger's exact share of the split, to the cent. See posting to several companies.
What retries automatically and what does not
Transient conditions — a timeout, a rate limit, a temporary server error at the ledger — are retried on a backoff without you doing anything. Rate limiting in particular is waited out rather than hammered.
Everything else is parked for a person, because it needs a change before it can succeed. This is the distinction worth understanding: a batch you can see sitting in failed has already finished trying.
Retrying safely
Retry is available on a failed batch, and Retry the whole run on a run that holds several. Either way it is a replay of the same logical post, not a new one — the idempotency key is preserved, so a post that actually reached the ledger returns the original bill instead of creating a second.
Some rules follow from that:
- A batch that is still
pendingorprocessingcannot be retried, because a run is either queued or in flight and a second one would work through the same items alongside the first. - A
succeededbatch cannot be replayed. There is nothing to gain and one obvious thing to lose. - Retrying twice in quick succession queues one retry. The second click is told the batch has already been re-queued.
Fix the cause first, then retry. A retry against an unchanged validation failure produces the same failure, more slowly.
CSV and SFTP exports
A CSV export has far less to go wrong: there is no remote ledger to reject anything. What is left is a rendering or storage failure, and — where we have set up an SFTP drop for you, which is done with us on request rather than from a screen — a destination refusing the connection or the credentials.
The file itself remains downloadable from the batch, so a delivery failure never means a lost close. You can take the file and import it by hand while you sort out the destination. If the columns are the problem rather than the delivery, change them in Exports → CSV mapping rather than editing files after the fact. See the exports reference.
What is recorded either way
Each batch keeps its own attempt history: every run records a start time, an end time and the error payload it ended with, and every parked invoice keeps the reason on its row. The audit trail records the same events in a form you can filter and export: exported when a document posts, export.failed when one stops — with the batch, the destination and the fault code or message the ledger gave — and export.retried, naming the person who pressed Retry. A batch that ends with bills needing a person writes an export.failed of its own, for the batch as a whole. One case writes no failure event on purpose: a bill your ledger already holds. It posted, so recording a failure beside it would describe a bill in your ledger as a bill that never got there. Read the batch for "what do I fix and where is the Retry button", and the audit trail for "what happened to this invoice, and who acted". See the audit trail.
Still stuck
Write to support@inlet-ap.com with the batch reference and the invoice that failed. If the ledger returned a correlation identifier we will have it against the item, and it is the fastest route to an answer when the problem is genuinely at the ledger's end.