Skip to content

Delivery targets & retention

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.

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.

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.

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).

Terminal window
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.

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.

Terminal window
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.

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.

Terminal window
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.

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
Terminal window
curl https://api.batchrouter.com/v1/retention-settings \
-H "Authorization: Bearer $BATCHROUTER_API_KEY"

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

Terminal window
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
}'