File batching

File batching

Some payment partners do not accept one API call per payment. They accept one file that holds every payment for the day, and they answer later with a file of their own. Debit order files, EFT collection files and bank submission files all work this way.

File batching is the collection module path for those partners. Root groups the due payments into a batch and hands the whole batch to your module. Your module builds the file in whatever format the partner needs, sends it to the partner, and tells Root what it sent. Root stores a copy of the file, records the outcome against each payment, and routes the partner's reply back to your module so it can settle those payments.

📘

File batching builds on scheduled payments

The batch itself comes from the scheduled payments path. Read that first. File batching is
what you add when your partner takes a file instead of an API call.

Is file batching right for your module?

Your module has one submission function either way. What changes is what that function returns.

API submissionFile submission
The partner acceptsOne request per paymentOne file per batch
Your function returnsA result per paymentA list of actions
Who sends to the partnerYour moduleYour module
Root stores a copy of what was sentNoYes
The partner's replyArrives per payment, usually by webhookArrives as a file, later
Turned on bybatching.enabledbatching.enabled plus fileConfig

If your partner takes an API call per payment, stop here and use the scheduled payments path. Add
fileConfig only when a file is what the partner actually wants.

How it works

flowchart TD
    A[Due payments become pending payments] --> B[Root groups them into a batch]
    B --> C[Root calls your submission function]
    C --> D[Your module builds the file and sends it to the partner]
    D --> E[Your module returns move_to_outbox with the file and references]
    E --> F[Root stores the file and records each payment as submitted]
    F --> G[Root files an archive copy of what was sent]
    G --> H[The partner replies with a file]
    H --> I[Root stores the reply and calls your response function]
    I --> J[Your module returns update_payment for each payment in the reply]
  1. Root collects the payments that are due for your module on an hourly tick, and only inside the
    submission window you configured. What it collects is split into batches of submitBatchSize.

  2. Your submission function is called once per batch. It receives every payment in that batch, with
    the policy, policyholder and payment method attached to each one.

  3. Your module builds the file and sends it to the partner over HTTPS. The format is yours. Root
    never reads inside the file, so fixed width, CSV, XML and JSON all work the same way.

  4. Your module returns a list of actions describing what it sent. The one that matters is
    move_to_outbox, which carries the file content and the reference your module wrote for each
    payment. Root stores the file, stamps those references onto the payments, and records the batch
    as delivered.

  5. When your module also returns move_to_archive, Root copies the file into an archive folder
    dated by the day it went out. That copy is your record of what the partner received.

  6. The partner replies. Your module either captures the reply in the response to its own delivery
    request, or the partner posts the file to a Root endpoint. You choose which with responseVia.

  7. Root calls your response function with the reply, the payments on the batch, and the references
    your module wrote for them. Your module parses the file and returns one update_payment action
    per payment it can resolve.

What you build, and what Root does

You write two functions and one file format:

  • The submission function that builds the file and sends it to the partner
  • The file layout the partner expects
  • The response function that parses the partner's reply
  • The reference on each payment that ties it to a line in the file

Root handles everything around them:

  • Grouping payments into batches, and calling your function on schedule
  • Storing the file, the archive copy, and every reply that arrives
  • Stamping the references, the delivery time and the batch status
  • Stopping a batch from being sent twice
  • Matching a reply back to the batch it belongs to
  • Writing the final outcome to each payment

Where your files are stored

Root keeps three folders for your module, inside a Root bucket:

FolderHolds
OutboxThe file your module reported sending. Kept for the life of the batch.
ArchiveA copy of the delivered file, in a folder dated by the day it went out.
InboxEvery raw reply from the partner, exactly as it arrived, stamped with the time it was received.
📘

The folders are not configurable, and your partner never sees them

Root derives them from your organisation and your module key, and keeps each environment separate.
Delivery to the partner is your module's own HTTPS request carrying the file, not a read from a
bucket. Nothing in your configuration can point at another module's files.

Batch statuses

StatusMeaning
pendingThe batch is built, and nothing has been sent
submittingA run is in progress and the file may be on its way to the partner
submittedYour module reported the file as sent
acceptedThe partner confirmed it received the file
failedThe batch ended without being collected. Final
archivedThe batch is finished. Final

Your module moves a batch to submitted, accepted, failed or archived with the
update_payment_batch action. pending and submitting belong to Root, and your module cannot
write them.

❗️

A batch stuck in submitting needs a person

submitting means a run may have had the file on its way to the partner when something broke.
Root cannot tell a file that was never sent from one that was sent but never acknowledged, so it
stops and raises an alarm rather than guessing. Somebody has to confirm with the partner what they
hold before that batch moves again.

The rules that matter most

Getting these wrong means money moves twice, or money is recorded as uncollected when the partner is
about to collect it.

❗️

Only report a file as sent when the partner has it

move_to_outbox is your module saying the file reached the partner. Root treats it as proof of
delivery and records every payment in it as submitted. Never return it when your request failed.

❗️

Only ask for a retry when the request never left your module

release_batch_for_retry sends the batch again. Use it for a refused connection, a DNS failure or
a TLS failure before the request was written. A timeout, a dropped connection mid-request, or any
status code coming back all mean the partner may hold the file. In those cases return neither
action and let a person decide. Asking for a retry when the partner already has the file debits
every payment in it twice.

⚠️

Give every payment a reference

The reference your module writes into the file is the only thing tying a line in the partner's
reply back to a payment in Root. A batch sent without a reference for every payment cannot be
settled from the reply, and has to be fixed by hand.

⚠️

A partner that rejects the file is a failure, not a retry

Report it with update_payment_batch and a failed status, and do not return move_to_outbox.
The payments then settle under the reason your module gave.

In this section


Did this page help you?