flush forces the SDK to attempt immediate delivery of any currently queued events instead of waiting for the normal batch-size or timer-based flush conditions.
It is mainly used when callers need stronger delivery guarantees before a boundary such as:
- app backgrounding / termination
- script exit
- serverless request completion
- tests that need deterministic delivery
both — client and server SDKs commonly expose flush, but with different operational meaning.
| Concern | Client-side | Server-side |
|---|---|---|
| Primary use | Best-effort immediate upload of queued events before app/lifecycle transitions. | Drain the in-process event queue before process/request shutdown. |
| Return type | Often void on native/mobile SDKs; Promise on js-core-based SDKs. |
Often Promise / blocking call until the queue drains. |
| Replay/snapshot queues | Some SDKs flush both the normal analytics queue and replay/snapshot queues. | Usually just the analytics/event queue. |
| Failure surfacing | Native client SDKs often swallow/log errors; js-core Promise can reject. | Server SDKs more commonly surface failure through rejection or by blocking until worker completion. |
flush(): void | Promise<void>- posthog-js core / react-native:
flush(): Promise<void> - Flutter:
flush(): Future<void> - Node:
flush(): Promise<void> - Ruby:
flush(): void - iOS:
flush(): void - Android:
flush(): void - Unity:
Flush(): void - Python:
flush(): void
- Check whether there is anything pending to send. If the queue is empty, return immediately.
- Bypass normal wait conditions. Do not wait for
flushAtorflushInterval; attempt delivery now. - Build and send batches until the queue is drained or a failure stops progress.
- Flush related queues when the SDK has them. Native mobile/client SDKs may also flush replay/snapshot queues.
- Return when the current flush cycle is complete.
- Promise-based SDKs resolve/reject when the cycle finishes.
- Void-returning SDKs fire-and-forget the flush on their internal queue/thread/coroutine.
- pending event queue
- batching configuration (
maxBatchSize, queue state) - SDK enabled / opt-out state
- replay/snapshot queue state in SDKs that have separate channels
- pending queue contents (successful sends remove items)
- retry/backoff state if the flush encounters retryable failures
- internal in-flight flush promise / lock state
flush()is often called automatically during shutdown/background transitions.- Explicit caller-triggered flush uses the same queue and transport machinery as timer/threshold-triggered flushes.
- On Promise-based SDKs, repeated flush calls are usually serialized/deduplicated around the current in-flight flush.
- Client/native SDKs often log and swallow flush failures.
- js-core-based Promise APIs may reject on transport/HTTP failures.
- Python blocks on
queue.join(); failure handling primarily happens in the consumer thread rather than by raising fromflush()itself. flush()should not crash application code under normal conditions.
- Flush drains queued events in FIFO batch order, subject to the surrounding batcher/retry queue behavior.
- Concurrent flush requests are commonly serialized so only one drain runs at a time.
- Events enqueued during a flush may be included in the current drain or left for the next cycle depending on implementation timing.
- retry-queue / event-batcher —
flush()forces those internal components to attempt immediate delivery. - shutdown / close — often implemented as
flush()plus worker/timer teardown. - session replay / snapshot queues — some client SDKs flush these alongside normal analytics events.
- opt-out / consent gating — if the SDK is opted out, future enqueue is blocked; already-queued events may or may not still be flushed depending on SDK policy.
The SDK SHALL implement the canonical flush behavior described by this spec. Implementations MAY adapt method names, parameter casing, type syntax, and lifecycle hooks to platform idioms where this spec explicitly allows variation, but MUST preserve the observable outcomes in the scenarios below.
- GIVEN a fresh SDK acceptance test harness
- AND the SDK clock is fixed at "2025-01-01T00:00:00Z"
- AND persistent storage is empty
- AND the mock PostHog server is reset
- GIVEN the SDK is initialized with token "test-token"
- AND the event queue contains events: | event | distinct_id | | First | user-123 | | Second | user-123 |
- WHEN flush is called
- THEN the mock server should receive a batch containing events: | event | | First | | Second |
- AND the event queue should be empty after a successful flush
- GIVEN a fresh SDK acceptance test harness
- AND the SDK clock is fixed at "2025-01-01T00:00:00Z"
- AND persistent storage is empty
- AND the mock PostHog server is reset
- GIVEN the SDK is initialized with token "test-token"
- AND the event queue is empty
- WHEN flush is called
- THEN the call should not throw
- AND no network request should be sent
- GIVEN a fresh SDK acceptance test harness
- AND the SDK clock is fixed at "2025-01-01T00:00:00Z"
- AND persistent storage is empty
- AND the mock PostHog server is reset
- GIVEN the SDK is initialized with token "test-token"
- AND the event queue contains events: | event | distinct_id | | Save | user-123 |
- AND the mock server will fail the next ingestion request with status 503
- WHEN flush is called
- THEN the call should not throw
- AND the event named "Save" should remain queued for retry