Stripe Webhook Events That Never Reach QuickBooks
Dozens of Stripe events, no QuickBooks records, no errors. Three of the four reasons are working as designed. How to tell which one you are looking at.
You opened your Stripe event log looking for one missing sale and found something worse. Dozens of charge.succeeded events, all of them real, none of them anywhere in QuickBooks. No error, no warning, no failed row. The events simply exist in Stripe and have no counterpart in your books.
The reasonable conclusion is that the sync is broken. In most cases it is not. Three of the four reasons an event produces no QuickBooks record are working as designed, and only one of them is a problem you need to act on. The difference matters, because the four look identical from the Stripe side and completely different from the Acodei side. If you are still choosing how Stripe should reach your books, you can start a free trial.
This post covers why a sync deliberately ignores certain events, the two configuration states that silently stop everything, and the specific tell that separates each case from an actual failure.
First: that list is not a delivery log
The Events view in your Stripe account is a record of things that happened to your data. Stripe creates an Event object whenever the state of an API resource changes, and those objects exist whether or not anybody is listening. As Stripe's API reference puts it, you configure an event destination to listen for events, and separately you can retrieve an individual event or a list of events from the API. The list came first. The subscription is a different thing.
Delivery is tracked somewhere else entirely. In Stripe's Workbench you select a specific webhook endpoint and open its Event deliveries tab, which shows each event as Delivered, Pending, or Failed along with the HTTP status code of the attempt. That tab is per endpoint. An event with no delivery row against a given endpoint was never sent there.
Here is the part that catches people out with Acodei specifically. Acodei is a Stripe Connect platform, and your Stripe account is a connected account on it. That is how Acodei receives your events at all. The consequence is that the webhook endpoint receiving your events does not live in your Stripe account, so it does not appear in your endpoint list and you cannot open its Event deliveries tab. When you scan your own events and see no evidence of Acodei, that is the expected view, not a symptom.
One practical limit while you investigate: Stripe says you can access events through the Retrieve Event API for 30 days. Anything older than that is gone from the log regardless of what happened to it.
Reason 1: your account is on daily summary, and the type is skipped on purpose
This is the most common explanation, and it is entirely benign.
Acodei has two sync modes, set per account by a field called payout_sync_method. Real-time posts each charge and refund to QuickBooks individually. Daily summary posts one aggregated record per day instead. If your account is on daily summary, ten per-charge event types are dropped at the receiver before they ever enter the processing queue:
| Dropped for daily-summary accounts |
|---|
charge.succeeded, charge.captured, charge.updated, charge.refunded |
invoice.finalized, invoice.paid, invoice.voided, invoice.marked_uncollectible |
credit_note.created, credit_note.voided |
Nothing is lost when they are dropped, and this is the part worth understanding rather than taking on faith. Every one of those ten types already does nothing in the worker for a daily-summary account. Their sync is payout-driven: the numbers are re-fetched from the Stripe API when a payout.* event or a reporting.report_run.succeeded arrives, and the daily summary is built from that fetch. Processing the individual charge event first would mean loading it, routing it, and discarding it, so the work is skipped one step earlier instead.
A sync that processes every event is not a better sync. It is the same result with more moving parts between you and it.
Events that are always kept, on any account: payout.*, reporting.*, topup.succeeded, customer.updated, and account lifecycle events. If the missing item is a payout, this section is not your explanation.
Where the money actually is. A charge whose event was skipped is not missing from your books. It is inside the daily summary record for the day its payout covers, along with every other charge in that batch. That is the whole point of the mode, and it is a different set of tradeoffs from real time, which we cover in daily summary vs real-time Stripe sync. If what you actually want is one QuickBooks record per sale, the fix is to change the mode, not to chase the events.
Reason 2: the event is a test-mode event
In production, Acodei skips events where livemode is false, and logs the skip.
This is a one-line rule with an outsized share of the confusion it causes, because a test payment looks exactly like a real one in the Stripe interface if you are not watching the mode toggle. Charges made in test mode, or triggered with the Stripe CLI while you were verifying something, will never produce a QuickBooks record and are not waiting in a queue somewhere. Nothing is stuck. Switch your Stripe view to live mode and check whether the events you are worried about are still there.
Reason 3: the connection itself is filtered out
This is the one that needs action, and it is the reason to read the other three carefully rather than assuming the sync is fine.
Acodei keeps an eligibility denylist and drops events at the receiver for any connected account that is in one of three states:
- The connection is paused.
- The account never linked a QuickBooks company.
- The account has a dangling QuickBooks row, meaning a connection record that no longer points at a live QuickBooks company.
All three produce the same symptom as reason 1, which is events in Stripe and silence in QuickBooks, but the cause is configuration rather than design and the volume is different. A daily-summary skip drops ten specific types and keeps payouts. A denylisted account drops everything on the v1 Connect route, so a total absence of records, payouts included, points here rather than at the skip list.
Check the connection state in Acodei before checking anything else. Two of these three states are things a person did, and the third usually follows a QuickBooks disconnection or a company file change.
There is one deliberate exception. The v2 route that carries Financial Account events is not filtered at all, precisely because paused accounts still receive those events. So a paused connection can still show Financial Account activity moving while ordinary charge and payout syncing has stopped, which reads as a half-working sync and is really two different routes with two different rules.
Reason 4: it arrived, it was processed, and the write failed
If none of the first three apply, the event probably did reach Acodei and became a Transaction record, which is the internal row for one syncable Stripe object. It carries the event type, the extracted amounts, the currency, and the balance-transaction detail flattened into columns.
A failure at this stage looks nothing like the first three cases, because it leaves evidence. When a job fails to write to QuickBooks, the Transaction records an error_msg, moves to a failed status, clears its queue and executing flags, and pushes an update to your dashboard. Some failures also send an email.
The Data Feed in your dashboard is where you see this. It is the ledger of every synced and errored transaction, and it is the single most useful screen for this entire question: an event that produced a Transaction appears there, and an event that was filtered never does. That distinction alone resolves most of these investigations.
Statuses worth recognising when you get there:
| Status | What it means |
|---|---|
| Synced | Written to QuickBooks successfully |
| Sync failed | The write was attempted and rejected; read error_msg |
| Payout pending | Waiting on other transactions to sync first |
| Half-synced | The QuickBooks entity was created but the job died before finishing, so the row is parked deliberately to stop a retry from duplicating it |
A real failure usually names its own cause. Stripe fee product errors are a good worked example: three exact messages, three different root causes, and a sync that stops on purpose rather than writing a record it cannot complete correctly.
What the filtering deliberately does not do
Filtering events before processing them is only safe if the filter is conservative in the right direction, and it is worth knowing which direction that is.
The filters fail open. If the cache backing them misses, times out, or errors, the event dispatches anyway. The failure mode is a redundant event getting processed, never a real one getting silently dropped because a lookup was slow.
Account lifecycle events always bypass both filters. Authorization and deauthorization are how the system learns about the account in the first place, so filtering them on the basis of stale account state would be circular.
The account data behind the filters is not cache-only. The authoritative state lives in the database, the cache is written through whenever a QuickBooks connection is saved or deleted, and a reconcile job runs every fifteen minutes as a backstop. A stale filter decision has a bounded lifetime.
Both filters apply only to the v1 Connect route. The v2 Financial Account route is unfiltered by design, as covered above.
Which of the four is yours
| What you observe | Most likely cause | Where to look |
|---|---|---|
| Charges and invoices absent, payouts present | Daily-summary type skip | The daily summary record for the payout date |
| Everything absent, payouts included | Denylisted connection: paused, no linked QuickBooks company, or a dangling row | Connection state in Acodei |
| Only some recent events absent, all around the same time | Test-mode events | The live/test toggle in Stripe |
| The record exists in the Data Feed with an error | A genuine sync failure | error_msg on the row |
Work down that list in order. It is roughly the order of likelihood, and each check is faster than the one below it.
A last note on counting. Even when everything is working, the number of Stripe events will not match the number of QuickBooks records, and it is not supposed to. Modes aggregate, some Stripe activity is not a sale at all, and one payout can absorb hundreds of charges. If the gap you are chasing is a count rather than a specific missing sale, Stripe payments vs balance transactions is the better starting point.
Frequently asked questions
Can I see whether Acodei received a specific event? Not from your Stripe account, because the receiving endpoint belongs to the Acodei platform rather than to your connected account. The equivalent check is the Data Feed: if the event produced a Transaction, it is there. If it is not there, the event was either filtered or never processed.
Does a skipped event mean the sale is missing from my books? No. On a daily-summary account the sale is inside the aggregated record for its payout date. The event was skipped, not the money.
Why does Acodei not just process everything and let the worker decide? Because the worker's decision for those ten types on a daily-summary account is already to do nothing. Dropping them at the receiver reaches the same result without queueing and discarding work first.
My connection is paused. Should I expect events to keep syncing? Treat a paused connection as a state where you should not rely on syncing continuing, and unpause it before investigating anything else. Pausing is one of the three denylist conditions.
Can an event be dropped because the cache was down? No. Both filters fail open, so an unavailable or slow cache causes the event to dispatch rather than to be dropped.
Stripe shows a failed delivery to an endpoint. Is that the same thing? No, that is a transport failure and it is visible on the Event deliveries tab of the endpoint in question. Stripe retries failed deliveries for up to three days with exponential backoff in live mode. The cases in this post are events that were delivered and then deliberately not turned into records, which is a different layer.
Why do I see Financial Account activity but no charges? The v2 Financial Account route is deliberately unfiltered while the v1 Connect route is filtered, so a denylisted connection can stop one and not the other.
The takeaway
An event in Stripe with no QuickBooks record behind it is one of four things, and only one of them is broken. Start with the Data Feed, because the presence or absence of a row there splits the four cleanly: a row with an error is a real failure worth fixing, and no row at all means the event was filtered, which sends you to your sync mode, your live/test toggle, and your connection state, in that order.
The design principle underneath all of it is that a sync should do the smallest amount of work that produces correct books, and should be loud when it cannot. Silence in QuickBooks is usually the first of those. An error in the Data Feed is the second.
Want Stripe activity in QuickBooks without reverse-engineering your event log to find out what happened? Start a free trial.
Automate your Stripe to QuickBooks sync
Save hours every month. Acodei automatically syncs your Stripe transactions, invoices, and payouts to QuickBooks Online.
How Acodei handles this in your stack
Stripe QuickBooks Integration
See how Acodei syncs Stripe payments, fees, refunds, invoices, and payouts into QuickBooks Online automatically.
Or go straight to a capability
Advanced Product Mapping
Map Stripe products to QuickBooks with rule-based logic on product ID, price ID, metadata, and account. Set rule priority and extend mapping to refunds and fees.
Automated Invoice Sync
Bring Stripe invoices into QuickBooks and auto-apply payments and credit memos, with numbering, invoice matching, and quantity tracking to cut double-entry.
Multi-Currency Mastery
Sync Stripe transactions across currencies with automatic exchange rate handling, currency-specific customer records, and invoice-level multicurrency.
Class Mapping
Map Stripe products to QuickBooks classes for scalable categorization and multi-entity reporting, enabling precise insights without manual effort.
Historical Data Import
Backfill historical Stripe data into QuickBooks by month range. Preview volume and cost before syncing so reporting starts from a complete baseline.
How to Connect Stripe to QuickBooks Online
Connect Stripe to QuickBooks Online in minutes. Acodei links both accounts with secure OAuth and syncs payments, fees, refunds, and payouts automatically.
Reconcile Stripe Payments in QuickBooks
Reconcile Stripe in QuickBooks Online automatically. Acodei splits out fees, matches payouts to deposits, and keeps every charge audit-ready.
Get more operational finance guides like this one
We will only send high-value product and finance content.