How to prove every Bubble batch item completed without duplicates or missing work

The short answer
Create one Batch record and one immutable Batch Item record for every unit of work before scheduling the workers. Give each item a deterministic idempotency key and an explicit status. A batch is complete only when every expected item reaches a terminal state. Never infer completion from the scheduling action, the last item in the source list, or a fixed delay.
This addresses two forum patterns: duplicate external calls caused by repeated scheduling and batches that appear to process fewer items than expected. See duplicate calls from scheduled work and scheduled workflows that appear incomplete.
Bubble's documentation says Schedule API Workflow on a List creates separate parallel workflows. It also notes that tracking progress is difficult because execution order is not guaranteed. The solution is a durable ledger, not a guess about execution timing.
The data model
Create two Bubble data types.
Batch:
batch_id— stable unique text;tenant_id— stable organization identifier;purpose— allowlisted operation name;expected_count— frozen before scheduling;status— preparing, running, reconciling, succeeded, completed_with_failures;created_at,completed_at;requested_by;optional source checksum or query description.
Batch Item:
batch_id;item_key— deterministic within the batch;source_record_id;status— pending, running, succeeded, failed;attempts,max_attempts;started_at,finished_at;result_reference;sanitized_error_code.
Enforce logical uniqueness on (batch_id, item_key). Bubble does not provide a general relational unique constraint for arbitrary field combinations, so the workflow must search for the deterministic key before creation and treat an existing item as the same unit of work. For stronger concurrency control, let an integration service or database with atomic conditional writes own the execution claim.
Prepare before scheduling
The coordinator should:
authorize the operator and tenant;
freeze the source list or record a reproducible query boundary;
create the Batch with
status = preparing;create one Batch Item per source ID;
confirm the actual item count equals
expected_count;change the Batch to
running;schedule one worker per Batch Item.
If item creation stops halfway, the batch remains preparing and no worker should run. This avoids a state where 100 records were expected, 63 item records were created, and the interface calls the batch complete after those 63 finish.
Bubble's bulk operations guide explains that scheduling on a list runs one workflow per item in parallel. Pass the Batch Item itself to the worker, not the original search expression. That gives every worker a durable identity and prevents changes in the source list from changing the scheduled unit.
Claim each item idempotently
At worker start:
load the Batch Item by its stable ID;
stop successfully if it is already
succeeded;stop if
attempts >= max_attempts;claim it by changing
pendingor retryablefailedtorunningand incrementing attempts;perform the external action using
batch_id:item_keyas the provider idempotency key when supported;record
succeededand the bounded provider reference, orfailedand a sanitized error code.
If two workers can claim the same item concurrently, move the claim to a service with transactional compare-and-set behavior. Searching and then updating in two separate Bubble actions can race under parallel execution.
Reconcile from states, not counters
Counters updated by parallel workers can lose increments when two workflows read the same value and write back competing totals. Reconciliation should count Batch Items by status from durable records.
The tested reference function in forum-operations-reference.mjs calculates:
expected items;
completed terminal items;
counts by status;
whether the batch is terminal;
whether the outcome is succeeded or completed with failures;
which failed item IDs remain eligible for retry.
Its negative test proves that a batch with a running item cannot report success, even when every other item is terminal.
Run reconciliation after each worker finishes and through a periodic repair workflow. The periodic pass matters because the worker may complete its external action but fail before scheduling the next coordinator step.
Retry only failed items
Do not reschedule the original list. Query Batch Items where status = failed and attempts < max_attempts. Keep successful items terminal. Before retrying an uncertain provider result, use the idempotency key or provider lookup to determine whether the action already happened.
Classify errors:
retryable: timeout, 429, temporary 5xx;
terminal: invalid input, authorization denial, unsupported file type;
uncertain: connection ended after the provider may have accepted the action.
Uncertain outcomes require reconciliation, not immediate replay.
Completion is a state transition
The batch may transition to:
succeededwhen every item succeeded;completed_with_failureswhen every item is terminal and at least one failed;reconcilingwhen there are stale running items or uncertain outcomes;remain
runningwhile pending or active items exist.
Set completed_at only on the first terminal transition. A repeated reconciliation request should return the same result without changing history.
Verification checklist
Schedule the coordinator twice with the same batch key; only one logical batch exists.
Attempt to create the same Batch Item twice; one logical item remains.
Run the same worker twice; the external action occurs once or is idempotently acknowledged.
Leave one item pending; the batch cannot become terminal.
Fail one item below its retry limit; only that item is returned for retry.
Fail one item at its retry limit; the batch ends with a bounded failure.
Simulate an external success followed by a local timeout; reconciliation finds the existing result.
Verify tenant A cannot read or retry tenant B's batch.
Confirm logs exclude payload bodies, tokens, and customer file contents.
The reconciliation model passed local automated tests on 2026-10-02. A full Bubble acceptance test should exercise two concurrently finishing workers and prove that the ledger remains correct.
For inbound delivery deduplication, see Bubble webhooks without duplicate work.
Primary CTA: Tell us which Bubble batch operation currently lacks a reliable completion signal.
Sources checked: Bubble Scheduling API workflows, Bulk operations, Bulk operation methods compared, and API workflow scheduler, checked 2026-10-02.
