After close to two decades of building on Pega, I've noticed almost every integration-heavy project eventually produces the same sentence from a client: "we have a feed of records from another system — create a case for every one." It sounds like a single requirement. It isn't. It's several different integration shapes hiding behind one sentence, and which one you actually have changes almost every design decision downstream — capacity, failure handling, even whether you should be creating a full Pega case at all. This post is the reasoning I use to tell them apart, including the two approaches I tried and dropped on a recent project before landing on the right one.

Three side-by-side diagrams. Pattern 1: the out-of-the-box case API, where each source record becomes its own HTTP POST and creates one case. Pattern 2: a file in a cloud repository read by a File Data Set and processed by a partitioned Data Flow, with each record creating a case. Pattern 3: event producers publishing to a partitioned Kafka topic that a Data Flow consumes, creating cases. A footer notes that the transport choice does not remove per-case processing, persistence, validation or duplicate-handling requirements.

Whichever door the records come through, every case still has to be initialised, validated and written to the database. The transport changes how records reach that work and how you recover when it fails; it doesn't make the work disappear.

Bulk case creation is an architecture decision

The shapes I see most often are a file drop (a batch lands on disk or in a bucket on a schedule), a synchronous call (an external system wants a case created now and expects an ID back), and a continuous stream (events arrive indefinitely with no natural batch boundary). Pega has a way to handle each, but they are not interchangeable, and they differ in latency, ordering, failure recovery and operational cost far more than in anything you can read off a feature list.

Here is the summary table. Treat it as a set of starting points, not a performance ranking: the right choice depends on latency, volume, failure recovery, ordering, what the source can actually provide, and how mature your operations are.

PatternBest fitMain trade-off
File ListenerFile-arrival-driven processing with manageable operational constraintsFile lifecycle and processing limitations
File Data Set + Data FlowLarge batch files with a suitable source and a need for partitioned processingData Flow configuration, capacity and recovery
Repeated OOTB case API callsStraightforward integrations that can manage individual requestsRequest overhead, concurrency control and retry management
Custom asynchronous bulk APIA caller needs to submit a batch without waiting for every case to be createdAdditional service, tracking and recovery design
Kafka + Data FlowContinuous events, decoupled producers and consumers, scalable stream processingInfrastructure and operational complexity

What I tried and dropped first

This is what happened on one project; it is an account of my experience, not a controlled benchmark. The ask was "take this partner's daily claims file and create a case per row." My first instinct — because it was fastest to build and demo — was to have the orchestration layer read the file, loop over the rows and call a create-case REST resource once per row. It worked well in the demo with 50 rows. With a feed of about 40,000 rows it became a problem for us. I didn't capture proper timings, so I won't quote any, but what I concluded was that I had built a sequential loop with no real concurrency control, retry handling or way to see which rows had made it. The API wasn't doing anything wrong; I had picked the shape meant for "one case, right now, caller waits for the answer" and asked it to behave like a batch processor.

Second attempt: swap the loop for a File Listener pointed at the claims file. That is closer to a mechanism designed for "batch file, scheduled drop", but in our setup the partner's files landed in an S3-style bucket, and I could not point the File Listener at it directly. I was about to write a sidecar job just to copy the file to local disk first, purely to satisfy the listener — which is exactly the kind of extra moving part that suggests you're fighting the tool. (Whether a File Listener can read from a repository depends on your Pega version, so check the documentation for yours; this was the constraint in our environment.)

What I landed on was a Data Flow reading the file directly from the repository through a File Data Set, partitioned across nodes, with a case-creation destination. No sidecar download, parallel processing across the rows, and a trade-off I was able to live with: the Data Flow doesn't move or delete the source file afterwards. In our case that didn't matter, because the partner's bucket already expired old files under its own retention policy. For that workload and that environment, this was the right call. It isn't a claim that Data Flow beats a File Listener in general. The real lesson was that I had spent two iterations assuming a mechanism before writing down the feed's actual shape and constraints.

Option 1: the out-of-the-box case API

The standard Pega case-creation API creates one case per request; it does not expose a native bulk-create operation that creates multiple cases in one call (see the Case Management documentation and the Pega API specification). That does not mean APIs are unsuitable for bulk ingestion. It means there are three distinct patterns, and it's worth keeping them apart:

Diagram A: out-of-the-box case API, one request per case Three source records and a final record N each send their own HTTP POST request to the Pega case-creation API. Each request produces exactly one case: Case 1, Case 2 and Case N. Source system Pega Pega case persistence Record 1 Record 2 ⋮ Record N Case-creation API one case per request HTTP POST HTTP POST HTTP POST Case 1 Case 2 ⋮ Case N One request per case — the client owns concurrency, retries and reconciliation.

Diagram A: with the OOTB case API, each source record triggers its own request and produces one case.

Option 2: File Listener

A File Listener is a sensible choice when files arrive at a defined location, file arrival should trigger processing, the volume and processing constraints fit what the listener can do, and you understand the file lifecycle and error handling you need. Its strength is that the model is simple and well suited to "a file showed up, process it".

Where it gets awkward is everything around the file. Before committing, work out how you will handle:

I would also be careful about assumptions around concurrency. The processing model of a File Listener is oriented around the file rather than around spreading rows across many threads or nodes, and that is the limitation I ran into. How much parallelism you can get depends on your Pega version and listener and node configuration, so verify it against the documentation for your release rather than assuming a deployment is globally single-threaded — or that it isn't. A File Listener and a File Data Set with a Data Flow are different mechanisms with different strengths, not two settings for the same feature, so don't treat one as a drop-in replacement for the other.

Option 3: File Data Set and Data Flow

A file-based Data Flow is worth considering when the feed contains many records, the source is supported by your version's File Data Set configuration (a repository such as a cloud bucket, rather than a local path), parallel processing would help, and the source repository can support the operational model you need. The Data Flow reads the file through the Data Set, can split the work into partitions, and runs it in parallel across threads and nodes, with case creation as the destination.

Diagram B: File Data Set and partitioned Data Flow A file in a supported repository is read by a Pega File Data Set. A partitioned Data Flow runs several parallel workers, and each worker creates individual cases: Case 1, Case 2 and Case N. Data source Pega processing Pega case persistence File in a repository File Data Set reads the file Data Flow (partitioned) Worker 1 Worker 2 ⋮ Worker N Case 1 Case 2 ⋮ Case N Batch processing; throughput depends on workload and configuration.

Diagram B: a file read through a File Data Set and processed by a partitioned Data Flow, with parallel workers creating individual cases.

I won't promise a throughput figure for this: it depends on your case-initialisation logic, the database, the number of partitions and threads, and the nodes you give it. What it did give me on that project was parallelism and no copy step. What you give up, depending on your version and source, is the file-handling conveniences around the file itself (moving, archiving), so check what your configuration supports and plan the lifecycle yourself — for example with a retention policy on the bucket, as we did. Malformed records, partial failure and restart need the same thought as with a listener: decide in advance whether a bad record fails the run or is set aside, and how a run is resumed or re-run without creating duplicates.

Option 4: Kafka and Data Flow

Kafka is a durable event-streaming platform that decouples producers from consumers. It is most compelling when the integration is a continuous stream of events, or when producers and consumers need durable buffering and need to scale independently. That is a different problem from "a partner sends us a file once a day", and I would not reach for it just because a feed is large. If a feed has a natural batch boundary, a well-partitioned file Data Flow may serve you without any broker to run.

In Pega, the pattern I'd look at first is a Kafka Data Set, backed by a Kafka configuration instance pointing at your cluster, feeding a Data Flow that creates the cases. That is not necessarily the same thing as a separate listener or service-rule approach, and the exact configuration and the options available differ by Pega version. Check the documentation for the version you run before copying anyone's setup, mine included.

Diagram C: Kafka topic and Data Flow Event producers publish to a Kafka topic with several partitions. A Pega Data Flow consumes the partitions and creates individual cases. Consumer lag is monitored. Failed records go to a quarantine and are replayed back to the stream after correction. Event producers Kafka topic Pega stream processing Pega case persistence App / service Database / CDC Other system Partition 1 Partition 2 Partition N Data Flow (Kafka Data Set) Consumer 1 Consumer 2 Consumer N Case 1 Case 2 Case N Monitoring consumer lag and backlog Failed records quarantine, correct, then replay replay after correction

Diagram C: producers publish to a partitioned Kafka topic; a Data Flow consumes it and creates individual cases, with lag monitoring and a failure and replay path.

What deserves design time with Kafka, more than the wiring:

Kafka is not automatically faster than file processing or an API. It buys you decoupling, buffering and replay; whether it buys you throughput depends on the whole pipeline.

Production considerations: beyond throughput

On the project above, raw throughput was the part I worried about first and the part that caused least trouble afterwards. The things that took the time were the ones below, and they apply whichever pattern you pick.

One caution: don't promise exactly-once case creation because the transport advertises delivery guarantees. End-to-end duplicate prevention depends on how the integration and the case-creation logic are designed, and a guarantee at the transport layer says little about what happens after a retry, a restart or a replay.

A decision framework, not a rule of thumb

Before picking a mechanism, I now answer these questions about the feed, in this order:

Only then does volume come into it, and mostly to decide whether the file-based option needs partitioned processing or whether something simpler is good enough. The table near the top is where I'd start; it is not a ranking.

Lessons learned