BatchRouter Docs

Delivery targets & retention

Choose how BatchRouter delivers results — poll, webhook, or your own S3 bucket — register and test a delivery target, and set how long results are kept.

BatchRouter gives you three ways to get results out of a completed batch, and one policy that controls how long those results stay available. This guide covers when to use each delivery mode, how to register and test a delivery target (a customer-owned S3-compatible bucket), how to re-deliver a batch whose delivery failed, and how to set your retention policy.

Delivery options

Every batch produces a results artifact once it reaches completed. How you receive it is up to you:

ModeHow it worksBest for
Poll (default)Call GET /v1/batches/{id} until terminal, then read GET /v1/batches/{id}/results (paginated) or GET /v1/batches/{id}/artifact-url (signed URL). Nothing to configure.Scripts, cron jobs, anything that can wait and ask.
WebhookBatchRouter POSTs a signed event to your URL when the batch finishes. Set per-batch (webhook: { url, secret }) or org-wide with PUT /v1/auth/account/delivery-webhook.Event-driven pipelines that react on completion.
Delivery targetBatchRouter writes the results artifact directly into your own S3-compatible bucket.Keeping outputs inside your own storage/VPC, large-volume archival, no egress through your app.

These are not mutually exclusive — you can poll and receive a webhook, or have a delivery target and still fetch results over the API.

Polling always works and needs zero setup. Reach for webhooks when you want to be notified, and for delivery targets when you want results to land in storage you control. For webhook signing and verification, see the webhooks guide.

Delivery targets (customer buckets)

A delivery target is an S3-compatible bucket you own (AWS S3, Cloudflare R2, MinIO, or any S3-API service). When a batch with a target reaches completed, BatchRouter writes the results artifact into that bucket under the target's prefix.

Customer-bucket delivery is gated behind the CUSTOMER_BUCKET_DELIVERY capability. If it isn't enabled for your org, registering and testing a target still works, but batches won't be delivered to the bucket and redeliver returns 409. Check the API reference or contact support to enable it.

Register a target

POST /v1/delivery-targets registers a bucket. The secret_access_key is write-only — it's stored encrypted and is never returned by any endpoint. The endpoint must be a public HTTPS URL; internal and metadata hosts are rejected.

Required fields: endpoint, region, bucket, access_key_id, secret_access_key. Optional: prefix, verify_before_gc (default true), is_default (default false).

curl -X POST https://api.batchrouter.com/v1/delivery-targets \
  -H "Authorization: Bearer $BATCHROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "endpoint": "https://s3.us-east-1.amazonaws.com",
    "region": "us-east-1",
    "bucket": "my-batch-results",
    "prefix": "batchrouter/",
    "access_key_id": "AKIAEXAMPLE",
    "secret_access_key": "wJalrXUtnFEMI/EXAMPLEKEY",
    "is_default": true
  }'

A 201 returns the created delivery_target (id, kind, endpoint, region, bucket, prefix, access_key_id, verify_before_gc, is_default, timestamps) — never the secret. Setting is_default: true makes this the target used for batches that don't name one explicitly.

GET /v1/delivery-targets lists your configured targets (no secrets). DELETE /v1/delivery-targets/{id} removes one.

Test connectivity

Before relying on a target, run a connectivity probe. POST /v1/delivery-targets/{id}/test performs a tiny write-then-delete against the bucket — because delivery uses PUT, a read-only credential will fail the probe, which is intentional.

curl -X POST https://api.batchrouter.com/v1/delivery-targets/$TARGET_ID/test \
  -H "Authorization: Bearer $BATCHROUTER_API_KEY"

The response is { "test": { "ok": <bool>, "checked": "write", "error"?: { code, message } } }. On failure, error.code classifies the problem so you can fix it precisely:

CodeMeaning
credential_errorThe access key / secret pair was rejected.
permission_deniedThe credential can't write to the bucket/prefix.
bucket_not_foundThe bucket doesn't exist at this endpoint/region.
bucket_errorThe bucket exists but the write failed for another reason.
unreachableThe endpoint couldn't be reached.

Re-deliver a failed batch

If a batch completed but its delivery to your bucket failed (an expired credential, a transient outage), re-enqueue it with POST /v1/batches/{batchId}/redeliver.

curl -X POST https://api.batchrouter.com/v1/batches/$BATCH_ID/redeliver \
  -H "Authorization: Bearer $BATCHROUTER_API_KEY"

A 202 re-enqueues delivery. The call is guarded against double-delivery via the batch's delivery_status: it returns 409 if the batch is already delivered or delivering, or if customer-bucket delivery isn't enabled / the batch has no configured target.

Result retention

Retention controls how long BatchRouter keeps a batch's results before garbage-collecting the stored bytes. It's an org-wide policy you read and set via GET|PUT /v1/retention-settings. The default is the free grace_only tier.

TierWhat it keepsCost
grace_only (default)Results for a short free grace window after completion, then GC.Free
extended_fixedResults for a fixed extended_days (1–3650).Metered storage charge
until_deletedResults until you delete them.Metered storage charge

extended_fixed and until_deleted are metered — once storage fees are enabled they incur a storage_charge against your prepaid wallet. Set never_charge: true to guarantee zero storage spend; this forces the free grace_only tier. extended_fixed requires extended_days. See the API reference for the exact charge model and the fee_acknowledged / auto_extend flags.

Read the current policy

curl https://api.batchrouter.com/v1/retention-settings \
  -H "Authorization: Bearer $BATCHROUTER_API_KEY"

Set the policy

retention_tier is the only required field. The example below keeps results for 90 days.

curl -X PUT https://api.batchrouter.com/v1/retention-settings \
  -H "Authorization: Bearer $BATCHROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "retention_tier": "extended_fixed",
    "extended_days": 90,
    "fee_acknowledged": true
  }'

Retention governs how long results stay fetchable over the API and via signed artifact URLs. Once a batch's bytes are GC'd, a results download returns 410 (expired), distinct from a 404 for a batch that never existed. Delivering to your own bucket sidesteps this — the copy in your storage is yours to keep regardless of BatchRouter's retention tier.

Next steps

On this page