Skip to main content
Batches

Create a batch

Create an asynchronous batch job from a JSONL input file.

POST /api/v1/forward/batches Forward reads the input file, parses the JSONL structure, and persists the initial task counts before returning validating. External dependencies such as file resources are pre-validated in the background; only tasks that pass pre-validation can wait for scheduling. By default, a Batch is eligible for scheduling only inside the idle window. Set ignore_idle_window=true to remove that time-window restriction. When processing finishes, Forward produces output.jsonl and, when failed lines exist, error.jsonl.

Headers

HeaderRequiredDescription
AuthorizationYesBearer <PAT or SAT>
Content-TypeYesapplication/json
Idempotency-KeyNoOptional idempotency key for unsafe requests.

Prerequisite: upload the input file

Creating a batch requires a JSONL input file uploaded through the CAS Files API to obtain a file_id. JSONL format — one JSON object per line, one line per independent task:
{"custom_id":"task-001","template_id":"tmpl_example001","identity_id":"idn_example001","body":{"input":"analyze the report","resources":[{"type":"file","file_id":"opaque-report-id","mount_path":"/data/input/report.pdf"}]}}
{"custom_id":"task-002","template_id":"tmpl_example001","identity_id":"idn_example001","body":{"input":"summarize the meeting notes","resources":[{"type":"file","file_id":"opaque-notes-id"}]}}
FieldTypeRequiredDescription
custom_idstringYesCaller-defined identifier. Must be unique within a single batch and is used to map result lines.
template_idstringYesForward Template ID that drives execution.
identity_idstringYesForward Identity ID that provides the execution identity.
bodyobjectYesRequest body passed to Session. It currently contains required input and optional resources.
body.inputstringYesInput sent to the Agent for this task.
body.resourcesarrayNoFile resources added to this task. Omit it or pass an empty array to add no line-level files.
body.resources[].typestringYesResource type. Currently only file is supported. A missing or different value makes the line an invalid_line.
body.resources[].file_idstringYesFile ID returned by the Files API. Treat it as an opaque string; after trimming, it must not be empty.
body.resources[].mount_pathstringNoMount path in the Agent container. It must be a canonical absolute path. When omitted, /data/workspace/<filename> is generated from the filename.
Each resources item must be a JSON object containing only type, file_id, and mount_path. A wrong field type, unknown field, relative path, non-canonical path containing .., or control character makes the entire line an invalid_line; the resource is not silently ignored. Line-level files are appended to the effective Template and Identity resources; they do not replace existing resources. A duplicate file_id, duplicate mount path, or path overlap with a Template, Identity, or repository resource causes the task to fail with config_error.
The file must be accessible to the identity that creates the Batch and must be in a mountable ready state. A Batch does not copy or lock files or extend their lifetime. If a file is deleted, expires, or changes after pre-validation, Session creation validates it again and the task may still fail.
Upload the file:
curl -X POST 'https://api.qoder.com/api/v1/cloud/files' \
  -H "Authorization: Bearer $QODER_ACCESS_TOKEN" \
  -F "file=@batch_input.jsonl" \
  -F "purpose=session_resource"
purpose=session_resource is required for Batch input files. If this field is omitted, the Files API stores the file with user_upload; CAS does not allow server-side downloads of such files, and Create Batch returns 400 invalid_input_file.
The returned id is the input_file_id for Create Batch.

Body parameters

ParameterTypeRequiredDescription
input_file_idstringYesJSONL file ID uploaded via the Files API.
completion_windowstringYesCompletion window: 24h, 48h, or 72h. The batch moves to expired when the window elapses.
metadataobjectNoCaller-supplied metadata. Up to 16 keys, any JSON value; the serialized object must be ≤ 2KB, each key ≤ 64 characters, and no NUL (U+0000) is allowed.
ignore_idle_windowbooleanNoWhether to ignore the idle window. Defaults to false when omitted. Only JSON booleans true and false are accepted; strings and numbers are not coerced.
ignore_idle_window is a strict boolean field. null, "true", 1, [], and {} return HTTP 400 invalid_request_error / invalid_request with the message ignore_idle_window must be a boolean.

Example requests

Regular Batch (uses the idle window by default)

Omitting ignore_idle_window is equivalent to explicitly passing false:
curl -s -X POST 'https://api.qoder.com/api/v1/forward/batches' \
  -H "Authorization: Bearer $QODER_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "input_file_id": "file_input001",
  "completion_window": "24h"
}'

Batch that ignores the idle window

curl -s -X POST 'https://api.qoder.com/api/v1/forward/batches' \
  -H "Authorization: Bearer $QODER_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: idem_batch_001" \
  -d '{
  "input_file_id": "file_input001",
  "completion_window": "24h",
  "metadata": {
    "source": "data_pipeline",
    "job_id": "12345"
  },
  "ignore_idle_window": true
}'

Example response

HTTP 200 OK
{
  "id": "batch_example001",
  "object": "batch",
  "status": "validating",
  "input_file_id": "file_input001",
  "completion_window": "24h",
  "ignore_idle_window": true,
  "created_at": "2026-07-07T07:25:01Z",
  "expires_at": "2026-07-08T07:25:01Z",
  "request_counts": {
    "total": 2,
    "pending": 2,
    "running": 0,
    "completed": 0,
    "failed": 0,
    "cancelled": 0,
    "expired": 0
  },
  "usage": null
}
Create Batch returns a validating snapshot after synchronously parsing the JSONL structure, checking existing unattended Template/Identity policies, and persisting tasks. Therefore, request_counts contains the real initial counts rather than all-zero placeholders. Individual file lookups and merged-resource conflict checks run in the background; only after they pass can the Batch move to queued. output_file_id and error_file_id appear only after the Batch reaches a terminal state and are omitted from the create response. If 3 of 100 lines fail synchronous structural parsing, the create response has total=100, pending=97, and failed=3. Background resource pre-validation may subsequently move some tasks from pending to failed, but the counter invariant always holds.

When global capacity is full

When the global number of processing Batches has reached capacity, Create Batch still returns HTTP 200 OK with validating. A dynamic snapshot reports the current waiting reason:
{
  "id": "batch_capacity_waiting001",
  "object": "batch",
  "status": "validating",
  "input_file_id": "file_input001",
  "completion_window": "24h",
  "ignore_idle_window": true,
  "queue_reason": "global_capacity",
  "created_at": "2026-07-07T07:25:01Z",
  "expires_at": "2026-07-08T07:25:01Z",
  "request_counts": {
    "total": 2,
    "pending": 2,
    "running": 0,
    "completed": 0,
    "failed": 0,
    "cancelled": 0,
    "expired": 0
  },
  "usage": null
}

Response fields

FieldTypeDescription
idstringBatch ID with prefix batch_.
objectstringAlways batch.
statusstringBatch status. See the status table below.
input_file_idstringInput JSONL file ID.
completion_windowstringCompletion window: 24h, 48h, or 72h.
ignore_idle_windowbooleanAlways returned. Whether the Batch ignores the idle window. Historical Batches and Batches created without the field use false.
queue_reasonstringOptional dynamic snapshot of the current queue reason. May be returned only in validating or queued.
created_atstringCreation time, RFC 3339.
expires_atstringExpiration time, created_at + completion_window.
request_countsobjectAggregate task counters.
usageobject/nullnull in the create response. In subsequent batch detail, list, and cancellation responses, returns aggregate Credit usage after at least one subtask has valid CAS Session usage.
usage.total_creditsnumberSum of persisted subtask total_credits. The unit is CAS Credit, not tokens or currency. Explicit zero values are preserved.
metadataobjectCaller-supplied metadata.

Batch status

StatusDescriptionType
validatingJSONL-line and synchronous configuration results have been persisted; effective configuration and file resources are being pre-validated in the background. Tasks have not entered the execution queue.Non-terminal
queuedResource pre-validation has completed and at least one executable task exists. Waiting for the Scheduler to activate it according to the idle window, capacity, and owner mutual-exclusion rules.Non-terminal
processingExecuting tasks.Non-terminal
cancellingCancellation in progress. Waiting for running tasks to finish.Non-terminal
expiringExpiration in progress. Waiting for running tasks to finish.Non-terminal
finalizingGenerating output files.Non-terminal
completedAll tasks executed. Output files generated.Terminal
failedBatch-level failure, such as an unreadable input file, persistence failure, or unrecoverable resource-validation process.Terminal
cancelledCancelled by the user.Terminal
expiredThe completion_window elapsed.Terminal

Scheduling and queue reasons

owner is the business ownership scope associated with the credential: a PAT is scoped to the current user, while an administrator SAT is scoped to its organization and workspace. ignore_idle_window=true changes only the Batch's time-window eligibility. It does not mean immediate execution or higher priority. The following rules still apply:
  • At most one processing Batch is allowed for the same owner.
  • Global Batch capacity and global Task capacity are unchanged.
  • Only Batches that finish resource validation and move from validating to queued can be activated.
  • Inside the idle window, regular Batches and Batches that ignore the window share FIFO order by created_at and ID.
  • completion_window still starts at created_at and includes validation, queueing, and execution time; ignoring the idle window does not extend or reset it.
queue_reason is a dynamic snapshot calculated at read time. It may change immediately after a response and is omitted if the snapshot cannot be loaded, without affecting the main API response. The first matching value in the following priority order is returned:
PriorityValueMeaning
1idle_windowThe current time is outside the idle window and this Batch does not ignore it.
2owner_processingAnother Batch for the same owner is already processing.
3global_capacityThe global number of processing Batches has reached capacity.
4scheduler_pendingThe Batch is queued, none of the first three conditions blocks it, and it is waiting for Scheduler activation.
queue_reason applies only to validating and queued. In validating, only the first three confirmed external blockers can be returned. When none applies, the field is omitted; scheduler_pending is not returned. It is omitted for processing, finalizing, cancelling, expiring, and all terminal states. When global Batch capacity is full, Create Batch can still successfully return validating. After validation, the Batch remains queued and reports global_capacity as the current waiting reason.

Request counts

FieldTypeDescription
totalintegerTotal lines, including lines that failed validation.
pendingintegerLines that have not started. During validating, these are accepted tasks still awaiting resource pre-validation and have not entered the execution queue.
runningintegerLines currently executing.
completedintegerLines that finished successfully.
failedintegerLines that failed permanently, including validation failures.
cancelledintegerLines terminated by cancellation.
expiredintegerLines terminated by expiration.
The invariant total = pending + running + completed + failed + cancelled + expired always holds.

Validation and enqueue gate

Create Batch synchronously performs the following work inside the request:
  • Read the input file and parse each JSON line.
  • Validate required fields, custom_id uniqueness, and the body.resources structure.
  • Resolve the Template and Identity, and apply the existing unattended tool-permission policy checks.
  • Persist lines that pass synchronous validation as pending, and persist failed lines as failed with invalid_line, config_error, or permission_denied.
  • Persist the real initial total, pending, and failed counts.
After the response, Forward uses the Batch creator's identity to resolve effective Template/Identity resources in the background, then performs file lookups, default-path generation, and merged-resource conflict checks. During pre-validation the Batch remains validating; it cannot enter the execution queue or create a Session. When pre-validation finishes:
  • At least one valid task remains: validating → queued → processing.
  • Every task failed: validating → finalizing → completed, and result files are generated.
  • Cancellation or expiration happened first: resource validation does not reactivate or enqueue the Batch.
A failed line does not block other lines. It remains in output.jsonl and is also written to error.jsonl. If no Session was created, session_id is omitted and body.resources preserves the original request content.

Task error classifications

CodeMeaningResource lookup retries
invalid_lineInvalid JSONL structure, resource fields, type, file_id, or explicit mount path.No
config_errorThe file returns 404/410, is not ready, cannot be mounted, or conflicts with another merged resource.No
permission_deniedThe upstream service explicitly returns 401/403.No
transient_errorA file lookup times out or returns 429/5xx after up to three backoff retries.Up to 3
To prevent enumeration, the upstream service may use 404 for both “file does not exist” and “file is not visible to this caller.” Forward does not infer file ownership or existence from a 404. It returns a safe config_error that does not disclose resource metadata; only an explicit 401/403 is classified as permission_denied.

Error codes

HTTPTypeCodeTrigger
400invalid_request_errorinvalid_requestinput_file_id missing, invalid completion_window, metadata too large, or ignore_idle_window is not a JSON boolean.
400invalid_request_errorinvalid_input_fileThe input file cannot be downloaded by Batch. Upload it with purpose=session_resource.
409conflict_errorbatch_already_processingignore_idle_window=true and another Batch for the same owner has the exact status processing.
429rate_limit_errorrate_limit_exceededThe user has reached the maximum number of unfinished batches.
401authentication_errorauthentication_requiredPAT or SAT is invalid or expired.

Same-owner processing conflict

Create Batch returns HTTP 409 only when the request has ignore_idle_window=true and another Batch for the same owner has the exact status processing:
{
  "type": "error",
  "request_id": "req_example001",
  "error": {
    "type": "conflict_error",
    "code": "batch_already_processing",
    "message": "Another Batch is already processing for this owner.",
    "batch_id": "batch_existing001"
  }
}
error.batch_id is the ID of the currently processing Batch for the same owner. This conflict check runs before the unfinished-Batch quota check and before all file and persistence side effects. A rejected request does not create a Batch or Task, does not read, parse, or validate the input file, and does not send an expiration message. validating, queued, finalizing, cancelling, expiring, and terminal states do not trigger this 409.

Notes

  • A single batch supports up to 10,000 JSONL lines.
  • Global concurrency is capped at 50 tasks.
  • By default, scheduling runs inside the idle window (default 22:00–08:00, configurable server-side). ignore_idle_window=true removes only this time-window restriction.
  • After creating a batch, the client should poll GET /api/v1/forward/batches/{batch_id} until a terminal state is reached.