---
title: "Get Batch by ID"
method: GET
path: "/v1/batches/{batch_id}"
tags: ["Batches"]
---

# Get Batch by ID

`GET /v1/batches/{batch_id}`

Retrieve a single batch by its ID without requiring the bucket_id. Returns full batch details including real-time progress (objects_processed, total_objects, items_per_second, eta_seconds) for in-flight batches.

## Path parameters

- `batch_id` string, required — The unique identifier of the batch.

## Response `200`

Successful Response

- BatchModel — Model representing a batch of objects for processing through collections. A batch groups bucket objects together for processing through one or more collections. Batches support multi-tier processing where collections are processed in dependency order (e.g., bucket → chunks → frames → scenes). Each tier has independent task tracking. Use Cases: - Process multiple objects through collections in a single batch - Track progress of multi-tier decomposition pipelines - Monitor and retry individual processing tiers - Query batch status and tier-specific task information Lifecycle: 1. Created in DRAFT status with object_ids 2. Submitted for processing → status changes to PENDING 3. Each tier processes sequentially (tier 0 → tier 1 → ... → tier N) 4. Batch completes when all tiers finish (status=COMPLETED) or any tier fails (status=FAILED) Multi-Tier Processing: - Tier 0: Bucket objects → Collections (bucket as source) - Tier N (N > 0): Collection documents → Collections (upstream collection as source) - Each tier gets independent task tracking via tier_tasks array - Processing proceeds tier-by-tier with automatic chaining Requirements: - batch_id: OPTIONAL (auto-generated if not provided) - bucket_id: REQUIRED - status: OPTIONAL (defaults to DRAFT) - object_ids: REQUIRED for processing (must have at least 1 object) - collection_ids: OPTIONAL (discovered via DAG resolution) - tier_tasks: OPTIONAL (populated during processing) - current_tier: OPTIONAL (set during processing) - total_tiers: OPTIONAL (defaults to 1, set during DAG resolution) - dag_tiers: OPTIONAL (populated during DAG resolution)
  - `batch_id` string — OPTIONAL (auto-generated if not provided). Unique identifier for this batch. Format: 'btch_' prefix followed by 12-character secure token. Generated using generate_secure_token() from shared.utilities.helpers. Used to query batch status and track processing across tiers. Immutable after creation.
  - `bucket_id` string, required — REQUIRED. Unique identifier of the bucket containing the objects to process. Must be a valid bucket ID that exists in the system. All object_ids must belong to this bucket. Format: Bucket ID as defined when bucket was created.
  - `namespace_id` string, nullable — Namespace this batch belongs to. Stored at creation time.
  - `status` 'PENDING' | 'QUEUED' | 'IN_PROGRESS' | 'PROCESSING' | 'COMPLETED' | 'COMPLETED_WITH_ERRORS' | 'FAILED' | 'CANCELED' | 'INTERRUPTED' | 'UNKNOWN' | 'SKIPPED' | 'DRAFT' | 'ACTIVE' | 'ARCHIVED' | 'SUSPENDED' | 'DEACTIVATED' — Enumeration of task statuses for tracking asynchronous operations. Task statuses indicate the current state of asynchronous operations like batch processing, object ingestion, clustering, and taxonomy execution. Status Categories: Operation Statuses: Track progress of async operations Lifecycle Statuses: Track entity state (buckets, collections, namespaces) Values: PENDING: Task is queued but has not started processing yet IN_PROGRESS: Task is currently being executed PROCESSING: Task is actively processing data (similar to IN_PROGRESS) COMPLETED: Task finished successfully with no errors COMPLETED_WITH_ERRORS: Task finished but some items failed (partial success) FAILED: Task encountered an error and could not complete CANCELED: Task was manually canceled by a user or system UNKNOWN: Task status could not be determined SKIPPED: Task was intentionally skipped DRAFT: Task is in draft state and not yet submitted ACTIVE: Entity is active and operational (for buckets, collections, etc.) ARCHIVED: Entity has been archived SUSPENDED: Entity has been temporarily suspended Terminal Statuses: COMPLETED, COMPLETED_WITH_ERRORS, FAILED, CANCELED are terminal statuses. Once a task reaches these states, it will not transition to another state. Partial Success Handling: COMPLETED_WITH_ERRORS indicates that the operation completed but some documents/items failed. The task result includes: - List of successful items - List of failed items with error details - Success rate percentage This allows clients to handle partial success scenarios appropriately. Polling Guidance: - Poll tasks in PENDING, QUEUED, IN_PROGRESS, or PROCESSING states - Stop polling when task reaches COMPLETED, COMPLETED_WITH_ERRORS, FAILED, or CANCELED - Use exponential backoff (1s → 30s) when polling
  - `queue_context` object, nullable — Populated on GET while the batch is PENDING/PROCESSING: how many earlier active batches are ahead in this namespace's queue and how long this batch has been waiting, so an in-flight status is never opaque.
  - `object_ids` string[] — List of object IDs to include in this batch. All objects must exist in the specified bucket_id. These objects are the source data for tier 0 processing. Collection-sourced batches may have empty object_ids. Objects are processed in parallel within each tier.
  - `dedup_strategy` 'skip' | 'replace' | 'force' — Controls how duplicate objects are handled during batch processing. Dedup is scoped to (bucket_id, collection_id): an object is considered a duplicate if the target collection already has documents produced from the same source object in any prior batch.
  - `submitted_by_key_id` string, nullable — key_id of the API key that submitted this batch, resolved server-side at creation. None for async/system-created batches with no request actor context. Audit only.
  - `submitted_by_key_prefix` string, nullable — Display prefix of the submitting API key. Audit only.
  - `submitted_by_is_internal` boolean, nullable — Whether the submitting key was server-verified internal AT SUBMIT TIME. None = no actor context (unattributable); never used for billing decisions (those read the live key marker).
  - `dedup_audit` object, nullable — Per-collection dedup decisions. Keys are collection_ids; values contain dedup_strategy, total_input, skipped, processed, and skipped_object_ids (up to 1000). Written at TWO stages, deep-merged per collection: (1) the API at manifest build, when smart-skip enforcement excludes already-complete objects before any engine submission (fields prefixed manifest_*), and (2) the Engine after its resume filter runs on whatever residue was submitted.
  - `collection_ids` string[], nullable — OPTIONAL. List of all collection IDs involved in this batch's processing. Automatically populated during DAG resolution from dag_tiers. Includes collections from all tiers (flattened view of dag_tiers). Used for quick lookups without traversing tier structure. Format: List of collection IDs across all tiers.
  - `error` string, nullable — OPTIONAL. Legacy error message field for backward compatibility. None if batch succeeded or is still processing. Contains human-readable error description from first failed tier. DEPRECATED: Use tier_tasks[].errors for detailed error information. For multi-tier batches, typically contains the error from the first failed tier. Check tier_tasks array for tier-specific error details and error_summary for aggregation.
  - `failure_reason` string, nullable — OPTIONAL. Human-readable explanation of why the batch failed. None if batch succeeded, is still processing, or is in DRAFT/PENDING state. Populated automatically when a batch transitions to FAILED status. Provides a concise, actionable summary of the root cause. Common reasons include: Ray job failure (spot preemption, OOM, code errors), 0 documents written (processing completed but produced no output), processing stall (no activity detected for extended period), or task exception (submission/validation failures). Use this field for user-facing error displays and alerting.
  - `error_summary` object, nullable — OPTIONAL. Aggregated summary of errors across ALL tiers in the batch. Maps error_type (category) to total count of affected DOCUMENTS across all tiers. None has THREE causes, not two: the batch succeeded, it is still processing, OR a tier FAILED AT TIER LEVEL and wrote zero documents, so no individual document carries an error and this histogram is legitimately empty. So None does NOT mean 'no errors', and None alongside COMPLETED_WITH_ERRORS is NOT a contradiction — read `failure_reason` and `tier_tasks[].error` for tier-level diagnosis. Provides quick batch-wide overview of error distribution. Example: {'dependency': 15, 'authentication': 25, 'validation': 5} means across all tiers, 15 documents failed with dependency errors, 25 with auth errors, 5 with validation errors. Automatically aggregated from tier_tasks[].error_summary. Used for batch health dashboard and error trend analysis.
  - `failure_category` 'timeout' | 'infrastructure' | 'orphaned' | 'pipeline' | 'validation' | 'unknown' — Batch-level failure classification. Coarser-grained than ErrorCategory (which classifies individual object errors). FailureCategory is set on the batch itself to tell users *why the batch as a whole failed* — timeout, infra, orphan, pipeline, or unknown. Drives the "Batch failed: <category>" badge in Studio and lets callers distinguish retryable infra blips from genuine pipeline bugs without parsing human-readable strings.
  - `failed_objects` FailedObjectRecord[] — OPTIONAL. List of per-object failure records from batch processing. Populated when individual objects fail while others succeed. Each record includes the object_id, error message, error classification (transient/permanent/resource), and timestamp. When this list is non-empty and some objects succeeded, batch status is COMPLETED_WITH_ERRORS. Enables targeted resubmission of only failed objects.
    - `object_id` string, required — ID of the object that failed processing.
    - `error` string, required — Human-readable error message describing what went wrong.
    - `error_type` 'transient' | 'permanent' | 'resource', required — Classification of the error for retry decisions. transient: network, timeout, temporary service issues (worth retrying). permanent: bad data, unsupported format (will never succeed). resource: GPU OOM, quota exceeded (may succeed with different resources).
    - `timestamp` string, required — ISO 8601 timestamp when the error occurred.
  - `failed_object_count` integer — OPTIONAL. Count of objects that failed during batch processing. Shorthand for len(failed_objects). Stored separately for efficient queries and sorting without loading full failed_objects array.
  - `unaccounted_object_count` integer, nullable — BACKE-3079: objects that produced NEITHER a document NOR a failure entry — the per-tier balance audits' summed `lost` (submitted - processed - failed - skipped), rolled up at completion so the gap is STATED on the record instead of inferred by subtraction. None when every submitted object is accounted for; per-tier detail lives at tier_tasks[].audit.
  - `type` 'BUCKET' | 'COLLECTION' — The type of batch.
  - `manifest_key` string, nullable — OPTIONAL. S3 key where the batch manifest is stored. Contains metadata and row data (Parquet) for Engine processing. For tier 0, points to bucket object manifest. For tier N+, points to collection document manifest. Format: S3 path (e.g., 'namespace_id/internal_id/manifests/tier_0.parquet'). Generated during batch submission.
  - `task_id` string, nullable — OPTIONAL. Primary task ID for the batch (typically tier 0 task). Used for backward compatibility with single-tier batch tracking. For multi-tier batches, prefer querying tier_tasks array for granular tracking. Format: Task ID as generated for tier 0.
  - `loaded_object_ids` string[], nullable — OPTIONAL. List of object IDs that were successfully validated and loaded into the batch. Subset of object_ids that passed validation. Used to track which objects are ready for processing. None if batch hasn't been validated yet.
  - `internal_metadata` object, nullable — OPTIONAL. Internal engine/job metadata for system use. May contain: job_id (provider-specific), engine_version, processing hints, last_health_check. last_health_check: Most recent health check results with health_status, enriched_documents, vector_populated_count, stall_duration_seconds, recommendations, missing_features. Populated asynchronously (non-blocking, best-effort). Used for troubleshooting batch processing issues via API. NOTE: In MongoDB, this is stored under '_internal.processing' path.
  - `metadata` BatchMetadata — Typed user-defined metadata for a batch. Known fields are validated and surfaced in API docs. Additional arbitrary keys are accepted via ``model_config extra="allow"``.
    - `campaign_id` string, nullable — Identifier linking this batch to a marketing or processing campaign.
    - `source` string, nullable — Origin of the batch data (e.g. an S3 prefix, partner name, or pipeline stage).
    - `tags` string[], nullable — Free-form tags for filtering and grouping batches.
    - `notes` string, nullable — Free-form notes about this batch (intent, context, special handling).
  - `tier_tasks` TierTaskInfo[] — OPTIONAL. List of tier task tracking information for multi-tier processing. Each element represents one tier in the processing pipeline. Empty array for simple single-tier batches. Populated during batch submission with tier 0 info, then appended as tiers progress. Each TierTaskInfo contains: tier_num, task_id, status, collection_ids, timestamps. Used for granular monitoring: 'Show me status of tier 2' or 'Retry tier 1'. Array index typically matches tier_num (tier_tasks[0] = tier 0, tier_tasks[1] = tier 1, etc.).
    - `tier_num` integer, required — REQUIRED. Zero-based tier number indicating the processing stage. Tier 0 = initial bucket-to-collection processing (bucket objects as source). Tier N (N > 0) = collection-to-collection processing (upstream documents as source). Used to determine processing order and identify which stage a task represents. Example: In a 5-tier pipeline (bucket → chunks → frames → scenes → summaries), chunks=tier 0, frames=tier 1, scenes=tier 2, summaries=tier 3.
    - `task_id` string, nullable — OPTIONAL. Unique task identifier for this tier's processing task. None if tier has not yet started (status=PENDING). Assigned when tier processing begins (status=IN_PROGRESS). Used to query task status via GET /v1/tasks/{task_id}. Format: 'task_' prefix followed by secure token. Generated using generate_secure_token() from shared.utilities.helpers.
    - `status` 'PENDING' | 'QUEUED' | 'IN_PROGRESS' | 'PROCESSING' | 'COMPLETED' | 'COMPLETED_WITH_ERRORS' | 'FAILED' | 'CANCELED' | 'INTERRUPTED' | 'UNKNOWN' | 'SKIPPED' | 'DRAFT' | 'ACTIVE' | 'ARCHIVED' | 'SUSPENDED' | 'DEACTIVATED' — Enumeration of task statuses for tracking asynchronous operations. Task statuses indicate the current state of asynchronous operations like batch processing, object ingestion, clustering, and taxonomy execution. Status Categories: Operation Statuses: Track progress of async operations Lifecycle Statuses: Track entity state (buckets, collections, namespaces) Values: PENDING: Task is queued but has not started processing yet IN_PROGRESS: Task is currently being executed PROCESSING: Task is actively processing data (similar to IN_PROGRESS) COMPLETED: Task finished successfully with no errors COMPLETED_WITH_ERRORS: Task finished but some items failed (partial success) FAILED: Task encountered an error and could not complete CANCELED: Task was manually canceled by a user or system UNKNOWN: Task status could not be determined SKIPPED: Task was intentionally skipped DRAFT: Task is in draft state and not yet submitted ACTIVE: Entity is active and operational (for buckets, collections, etc.) ARCHIVED: Entity has been archived SUSPENDED: Entity has been temporarily suspended Terminal Statuses: COMPLETED, COMPLETED_WITH_ERRORS, FAILED, CANCELED are terminal statuses. Once a task reaches these states, it will not transition to another state. Partial Success Handling: COMPLETED_WITH_ERRORS indicates that the operation completed but some documents/items failed. The task result includes: - List of successful items - List of failed items with error details - Success rate percentage This allows clients to handle partial success scenarios appropriately. Polling Guidance: - Poll tasks in PENDING, QUEUED, IN_PROGRESS, or PROCESSING states - Stop polling when task reaches COMPLETED, COMPLETED_WITH_ERRORS, FAILED, or CANCELED - Use exponential backoff (1s → 30s) when polling
    - `collection_ids` string[] — REQUIRED. List of collection IDs being processed in this tier. Flattened from extractor_jobs for convenience. Each tier can process one or more collections in parallel. Collections in the same tier have no dependencies on each other. Format: Collection IDs as defined when collections were created. Minimum 1 collection per tier. Example: Tier 1 might process ['col_frames_30fps', 'col_frames_60fps'] in parallel.
    - `extractor_jobs` ExtractorJobInfo[] — List of extractor jobs for this tier (one per unique feature_extractor_type). NEW as of 2025-12-31: Tiers now support multiple Ray jobs. Empty list for backwards compatibility with old batches. Tier completes when ALL extractor_jobs reach COMPLETED status.
      - `extractor_type` string, required — Feature extractor type (e.g., 'image_extractor', 'face_identity_extractor')
      - `collection_ids` string[] — Collections processed by this extractor job
      - `extractor_id` string, nullable — Concrete extractor identifier used for this job, e.g. 'universal_extractor_v1'.
      - `ray_job_id` string, nullable — Ray job ID for this extractor job
      - `celery_task_id` string, nullable — Background task ID that submitted this processing job
      - `callback_job_id` string, nullable — Job ID expected in the tier completion callback for this extractor job.
      - `execution_mode` string, nullable — Execution backend used for this extractor job.
      - `status` 'PENDING' | 'QUEUED' | 'IN_PROGRESS' | 'PROCESSING' | 'COMPLETED' | 'COMPLETED_WITH_ERRORS' | 'FAILED' | 'CANCELED' | 'INTERRUPTED' | 'UNKNOWN' | 'SKIPPED' | 'DRAFT' | 'ACTIVE' | 'ARCHIVED' | 'SUSPENDED' | 'DEACTIVATED' — Enumeration of task statuses for tracking asynchronous operations. Task statuses indicate the current state of asynchronous operations like batch processing, object ingestion, clustering, and taxonomy execution. Status Categories: Operation Statuses: Track progress of async operations Lifecycle Statuses: Track entity state (buckets, collections, namespaces) Values: PENDING: Task is queued but has not started processing yet IN_PROGRESS: Task is currently being executed PROCESSING: Task is actively processing data (similar to IN_PROGRESS) COMPLETED: Task finished successfully with no errors COMPLETED_WITH_ERRORS: Task finished but some items failed (partial success) FAILED: Task encountered an error and could not complete CANCELED: Task was manually canceled by a user or system UNKNOWN: Task status could not be determined SKIPPED: Task was intentionally skipped DRAFT: Task is in draft state and not yet submitted ACTIVE: Entity is active and operational (for buckets, collections, etc.) ARCHIVED: Entity has been archived SUSPENDED: Entity has been temporarily suspended Terminal Statuses: COMPLETED, COMPLETED_WITH_ERRORS, FAILED, CANCELED are terminal statuses. Once a task reaches these states, it will not transition to another state. Partial Success Handling: COMPLETED_WITH_ERRORS indicates that the operation completed but some documents/items failed. The task result includes: - List of successful items - List of failed items with error details - Success rate percentage This allows clients to handle partial success scenarios appropriately. Polling Guidance: - Poll tasks in PENDING, QUEUED, IN_PROGRESS, or PROCESSING states - Stop polling when task reaches COMPLETED, COMPLETED_WITH_ERRORS, FAILED, or CANCELED - Use exponential backoff (1s → 30s) when polling
      - `started_at` string, date-time, nullable — When this extractor job started processing
      - `completed_at` string, date-time, nullable — When this extractor job finished processing
      - `duration_ms` number, nullable — Processing duration in milliseconds
      - `documents_written` integer, nullable — Number of documents written by this extractor job
      - `pages_dropped` integer, nullable — OPTIONAL. Pages of multi-page inputs (PDFs) that were NOT indexed by this job — capped by max_document_pages or dropped by per-page failures. Only present when > 0. A COMPLETED job with pages_dropped > 0 indexed the input PARTIALLY; see pages_dropped_reasons and raise max_document_pages to index more.
      - `pages_dropped_reasons` object, nullable — OPTIONAL. Dropped-page counts by reason: max_document_pages_cap (input exceeded the collection's max_document_pages) or page_processing_failure (per-page extract/embed errors).
      - `segments_dropped` integer, nullable — OPTIONAL. Video segments that were NOT indexed by this job — capped by max_video_segments. Only present when > 0. A COMPLETED job with segments_dropped > 0 indexed the video PARTIALLY; see segments_dropped_reasons and raise max_video_segments to cover more of the video.
      - `segments_dropped_reasons` object, nullable — OPTIONAL. Dropped-segment counts by reason: config_cap_max_video_segments (video exceeded the collection's max_video_segments).
      - `documents_skipped` integer, nullable — OPTIONAL. Number of input rows skipped (not failed) — typically metadata-only objects with no embeddable content, content-flag filtered rows, or null-text chunks. Skipped rows count toward the tier-completion invariant (processed + failed + skipped == submitted) so a tier with 100 inputs and 100 skips still ends in a valid terminal state.
      - `errors` BatchErrorDetail[] — Detailed errors from this extractor job
        - `error_type` 'dependency' | 'authentication' | 'validation' | 'runtime' | 'network' | 'resource', required — Categories for batch processing errors. Used to classify errors for better observability, retry logic, and debugging. Helps distinguish between transient errors (worth retrying) and permanent errors.
        - `message` string, required — REQUIRED. Human-readable error message. Concise description of what went wrong. Should be actionable and help users understand the issue.
        - `component` string, nullable — OPTIONAL. Component or service where the error occurred. Helps identify which part of the system failed. Examples: service class names, module names, or feature names.
        - `stage` string, nullable — OPTIONAL. Processing stage where the error occurred. Identifies which pipeline stage failed. Examples: pipeline stage names from collection configuration.
        - `traceback` string, nullable — OPTIONAL. Full Python traceback for debugging. Includes stack trace for code-level troubleshooting. Should be truncated if too long (e.g., max 2000 chars).
        - `timestamp` string, date-time — REQUIRED. ISO 8601 timestamp when the error occurred. Used for chronological error tracking and debugging.
        - `affected_document_ids` string[] — OPTIONAL. List of document IDs affected by this error. For object-level errors: contains single document ID. For batch-level aggregation: contains all affected document IDs. Used to identify scope of impact.
        - `affected_count` integer — REQUIRED. Number of documents affected by this error. For object-level: typically 1. For batch-level aggregation: total count of affected documents. Used for error impact analysis.
        - `recovery_suggestion` string, nullable — OPTIONAL. Actionable suggestion for resolving the error. Helps users quickly fix common issues. Examples: install missing package, check credentials, update schema.
        - `metadata` object — OPTIONAL. Additional error context and metadata. Free-form dictionary for error-specific details. Examples: retry_count, last_retry_at, error_code, http_status.
      - `error` string, nullable — OPTIONAL. Simple error message string for quick debugging. Set when the Ray job fails with error details from JobStatusMonitor. For detailed error information, see errors array.
      - `last_activity_at` string, date-time, nullable — OPTIONAL. Timestamp of the last BatchJobPoller heartbeat confirming this extractor job was non-terminal (RUNNING or PENDING). Updated approximately every 10 seconds while IN_PROGRESS. A stale value (minutes old) may indicate a stuck or lost job.
      - `ray_job_status` string, nullable — OPTIONAL. Last observed Ray job status for this extractor job as of last_activity_at. Values: 'RUNNING', 'PENDING', 'SUCCEEDED', 'FAILED'. Use with last_activity_at to assess whether the job is actively running.
      - `submission_params` SubmissionParams — Parameters used when submitting a Ray/GKE job, persisted for post-hoc debugging.
        - `entrypoint` string, nullable
        - `deployment_mode` string, nullable — 'ray' (local/dev) or 'gke' (production).
        - `requires_gpu` boolean, nullable
        - `num_cpus` number, nullable
        - `num_gpus` number, nullable
        - `memory_bytes` integer, nullable
        - `priority` integer, nullable
        - `plugin_archives` string[], nullable
        - `plugin_dependencies` string[], nullable
        - `image_uri` string, nullable
        - `extractor_name` string, nullable
        - `extractor_version` string, nullable
        - `env_vars_keys` string[], nullable — Environment variable keys (values omitted for security).
        - `manifest_key` string, nullable — S3 key of the batch manifest (metadata.json) used for this job. The manifest survives Ray cluster rebuilds, so the poller can resubmit an in-cluster (raysubmit_*) job killed by a redeploy.
        - `submitted_at` string, date-time, nullable
    - `source_type` string, required — REQUIRED. Type of data source for this tier's processing. 'bucket': Tier 0 processing where source is bucket objects from the objects table. 'collection': Tier N+ processing where source is documents from upstream collection(s). Determines how the API prepares the input dataset manifest for the Engine. Bucket sources query the objects table and include file blobs. Collection sources query the documents table and include processed features.
    - `source_collection_ids` string[], nullable — OPTIONAL for tier 0 (must be None). REQUIRED for tier N+ (N > 0). List of upstream collection IDs that provide documents as input to this tier. Typically contains collection IDs from the previous tier (tier_num - 1). Used by the API to query documents from these collections for processing. These upstream documents are converted to a Parquet manifest for the Engine. Example: If tier 1 processes 'col_frames' and sources from tier 0's 'col_chunks', then source_collection_ids=['col_chunks'].
    - `parent_task_id` string, nullable — OPTIONAL. Task ID of the previous tier (tier_num - 1) that processed before this tier. Used to link tiers together for audit trail and lineage tracking. None for tier 0 (no parent). Enables queries like 'show all tiers that processed after tier 0' or 'trace back through all parent tiers to find the original batch'. Format: Same as task_id (e.g., 'task_tier0_abc123').
    - `started_at` string, date-time, nullable — OPTIONAL. ISO 8601 timestamp when this tier began processing. None if tier has not yet started (status=PENDING). Set using current_time() from shared.utilities.helpers when tier starts. Used to calculate tier processing duration and identify long-running tiers. Example: '2025-11-03T10:00:00Z'.
    - `completed_at` string, date-time, nullable — OPTIONAL. ISO 8601 timestamp when this tier finished processing (success or failure). None if tier has not yet completed (status=PENDING or IN_PROGRESS). Set using current_time() from shared.utilities.helpers when tier completes. Used to calculate tier processing duration (completed_at - started_at). Set for both COMPLETED and FAILED statuses. Example: '2025-11-03T10:05:00Z'.
    - `duration_ms` number, nullable — OPTIONAL. Processing duration in milliseconds for this tier. Calculated as (completed_at - started_at) when tier completes. None if tier has not yet completed or if started_at was not set. Provides a pre-computed duration for easy querying without timestamp math. Set for both COMPLETED and FAILED statuses.
    - `errors` BatchErrorDetail[] — OPTIONAL. List of detailed errors that occurred during tier processing. Empty list if tier succeeded or has not yet completed. Each error includes: error_type, message, component, stage, traceback, timestamp. Multiple errors may occur if different documents fail with different issues. Used for detailed error analysis, debugging, and intelligent retry logic. Example: Multiple documents failing with different errors (dependency vs auth). For backward compatibility, check if list is empty for success/in-progress status.
      - `error_type` 'dependency' | 'authentication' | 'validation' | 'runtime' | 'network' | 'resource', required — Categories for batch processing errors. Used to classify errors for better observability, retry logic, and debugging. Helps distinguish between transient errors (worth retrying) and permanent errors.
      - `message` string, required — REQUIRED. Human-readable error message. Concise description of what went wrong. Should be actionable and help users understand the issue.
      - `component` string, nullable — OPTIONAL. Component or service where the error occurred. Helps identify which part of the system failed. Examples: service class names, module names, or feature names.
      - `stage` string, nullable — OPTIONAL. Processing stage where the error occurred. Identifies which pipeline stage failed. Examples: pipeline stage names from collection configuration.
      - `traceback` string, nullable — OPTIONAL. Full Python traceback for debugging. Includes stack trace for code-level troubleshooting. Should be truncated if too long (e.g., max 2000 chars).
      - `timestamp` string, date-time — REQUIRED. ISO 8601 timestamp when the error occurred. Used for chronological error tracking and debugging.
      - `affected_document_ids` string[] — OPTIONAL. List of document IDs affected by this error. For object-level errors: contains single document ID. For batch-level aggregation: contains all affected document IDs. Used to identify scope of impact.
      - `affected_count` integer — REQUIRED. Number of documents affected by this error. For object-level: typically 1. For batch-level aggregation: total count of affected documents. Used for error impact analysis.
      - `recovery_suggestion` string, nullable — OPTIONAL. Actionable suggestion for resolving the error. Helps users quickly fix common issues. Examples: install missing package, check credentials, update schema.
      - `metadata` object — OPTIONAL. Additional error context and metadata. Free-form dictionary for error-specific details. Examples: retry_count, last_retry_at, error_code, http_status.
    - `error_summary` object, nullable — OPTIONAL. Aggregated summary of errors by error type. Maps error_type (category) to count of affected DOCUMENTS. None has THREE causes, not two: the tier succeeded, the tier has not yet completed, OR the tier FAILED AT TIER LEVEL — it wrote zero documents (infrastructure failure, job crash, no resolvable inputs), so no individual document carries an error and this histogram is legitimately empty. So None does NOT mean 'no errors', and None alongside COMPLETED_WITH_ERRORS is NOT a contradiction. For a tier-level failure the diagnosis is in `failure_reason` and `tier_tasks[].error`, which carry the Ray job id and where to look. Provides quick overview of error distribution without parsing full error list. Example: {'dependency': 5, 'authentication': 10, 'validation': 3} means 5 documents failed with dependency errors, 10 with auth errors, 3 with validation. Automatically generated from errors list for convenience. Used for batch health monitoring and error trend analysis.
    - `performance` object, nullable — OPTIONAL. Performance metrics summary for this tier's execution. Automatically populated after tier completion by collecting data from ClickHouse analytics. Contains: total_time_ms (total execution time), avg_latency_ms (average operation latency), bottlenecks (list of slowest operations), stage_count (number of profiled stages). Used for troubleshooting performance issues and identifying bottlenecks. None if tier has not completed or performance data collection failed. Populated asynchronously (non-blocking, best-effort).
    - `ray_job_id` string, nullable — OPTIONAL. Ray/Anyscale job ID for tracking the infrastructure-level processing job. None if tier has not yet started or if the job ID was not returned by the engine. Set when tier processing is submitted to Ray/Anyscale via the Engine. Used for cancelling running jobs and monitoring infrastructure-level status. Format: 'raysubmit_' prefix followed by job identifier (e.g., 'raysubmit_9pDAyZbd5MN281TB'). This is the job ID that appears in the Ray/Anyscale dashboard.
    - `requires_gpu` boolean, nullable — OPTIONAL. Whether this tier's Ray job was scheduled onto a GPU worker group. Populated at submit time from the engine's requires_gpu decision (built-in extractors default to GPU; custom plugins opt in via compute_profile). Lets users confirm a custom plugin actually landed on a GPU without kubectl access.
    - `worker_groups` string[], nullable — OPTIONAL. Names of the Ray worker groups this tier's job is eligible to run on. Derived from the compute profile chosen at submit time (e.g., ['gpu-workers'] for requires_gpu=True, ['cpu-workers'] otherwise). Surfaces resource allocation in the batch response so users don't need kubectl access to debug scheduling.
    - `celery_task_id` string, nullable — OPTIONAL. Background task ID for tracking the worker processing this tier. None if tier has not yet started or is not processed via background task. Set when the tier processing task is triggered. Used for revoking pending/running tasks during batch cancellation or deletion. Format: UUID string (e.g., 'a1b2c3d4-e5f6-7890-abcd-ef1234567890').
    - `source_documents_fetched` integer, nullable — OPTIONAL. Number of documents fetched from source collection(s) for Tier N processing. For Tier 0 (bucket source), this is the number of objects from the bucket. For Tier N+ (collection source), this is the count of documents from upstream collection(s). Set at the start of tier artifact building in build_tier_n_artifacts(). If 0, the source collection is empty - check upstream tier completion.
    - `documents_after_source_filter` integer, nullable — OPTIONAL. Number of documents remaining after applying source_filters. source_filters are optional conditions that exclude documents from processing. If this is 0 but source_documents_fetched > 0, your source_filters are too restrictive. Check that filter fields exist in source documents and conditions match expected values.
    - `documents_missing_input_fields` integer, nullable — OPTIONAL. Number of documents missing required input_mapping fields. input_mappings define which fields from source documents map to extractor inputs. If this equals documents_after_source_filter, ALL documents are missing required fields. Common cause: upstream extractor didn't produce expected output (e.g., video_segment_url). Check upstream extractor configuration and verify output field names.
    - `documents_submitted_to_engine` integer, nullable — OPTIONAL. Number of documents actually submitted to the Ray/Engine for processing. This is documents_after_source_filter minus documents_missing_input_fields. If 0, no documents were sent to the engine - check source_filters and input_mappings. If > 0 but documents_written = 0, the engine failed to process documents.
    - `documents_written` integer, nullable — OPTIONAL. Documents THIS BATCH wrote in this tier. Sourced from the tier's extractor jobs (extractor_jobs[].documents_written), which report per batch. It is NOT a collection total: do not subtract documents_before_processing from it. If 0 but documents_submitted_to_engine > 0, check tier errors for processing failures.
    - `documents_before_processing` integer, nullable — OPTIONAL. Document count in target collection(s) before this tier started processing. DIAGNOSTIC ONLY. Do NOT compute (documents_written - documents_before_processing): documents_written is already this batch's own output, and this field is a WHOLE-COLLECTION count, so the difference attributes concurrent batches' writes to this one (BACKE-2982). It is retained as the input to a fallback used only when no extractor reported a count.
    - `last_activity_at` string, date-time, nullable — OPTIONAL. Timestamp of the last BatchJobPoller heartbeat confirming the tier's Ray job(s) were non-terminal (RUNNING or PENDING). Updated approximately every 10 seconds while the tier is IN_PROGRESS. A stale last_activity_at (minutes old) indicates the job may be stalled or lost. Use this to distinguish an actively running batch from one that is silently stuck.
    - `ray_job_status` string, nullable — OPTIONAL. Last observed Ray job status as of last_activity_at. Values: 'RUNNING' (actively processing), 'PENDING' (queued, not yet started), 'SUCCEEDED', 'FAILED'. For multi-extractor tiers, see extractor_jobs[].ray_job_status for per-job granularity.
    - `ray_job_logs` string, nullable — OPTIONAL. Persisted Ray job logs captured when the job reached a terminal state (SUCCEEDED or FAILED). Contains the last 500 lines of the head pod's stdout. Populated automatically by the batch poller before pods are cleaned up by K8s, so logs remain available for debugging after the job's infrastructure is gone.
    - `ray_job_logs_captured_at` string, date-time, nullable — OPTIONAL. Timestamp when ray_job_logs were captured.
    - `submission_params` SubmissionParams — Parameters used when submitting a Ray/GKE job, persisted for post-hoc debugging.
      - `entrypoint` string, nullable
      - `deployment_mode` string, nullable — 'ray' (local/dev) or 'gke' (production).
      - `requires_gpu` boolean, nullable
      - `num_cpus` number, nullable
      - `num_gpus` number, nullable
      - `memory_bytes` integer, nullable
      - `priority` integer, nullable
      - `plugin_archives` string[], nullable
      - `plugin_dependencies` string[], nullable
      - `image_uri` string, nullable
      - `extractor_name` string, nullable
      - `extractor_version` string, nullable
      - `env_vars_keys` string[], nullable — Environment variable keys (values omitted for security).
      - `manifest_key` string, nullable — S3 key of the batch manifest (metadata.json) used for this job. The manifest survives Ray cluster rebuilds, so the poller can resubmit an in-cluster (raysubmit_*) job killed by a redeploy.
      - `submitted_at` string, date-time, nullable
    - `infrastructure_events` InfrastructureDetail[] — Infrastructure-level events correlated with this tier's execution (OOM, preemption, etc.).
      - `event_type` 'oom' | 'preemption' | 'node_failure' | 'ray_bug' | 'rolling_update' | 'unknown', required
      - `detected_at` string, date-time, nullable
      - `raw_signal` string, nullable — The error text that triggered classification.
      - `node_id` string, nullable
      - `pod_name` string, nullable
    - `audit` object, nullable — OPTIONAL. Tier completion invariant audit, persisted by the complete_tier callback. Shape: {tier_num, submitted, processed, failed, skipped, lost, balanced, notes}. Phase 1 of INGESTION_RELIABILITY_PLAN.md. ``lost > 0`` means objects were submitted but absent from both processed_objects and failed_documents — investigate via the /audit endpoint.
    - `audit_override_reason` string, nullable — OPTIONAL. Set when the audit overrode the orchestrator-supplied tier status (e.g. promoted COMPLETED → COMPLETED_WITH_ERRORS because of lost objects).
  - `current_tier` integer, nullable — OPTIONAL. Zero-based index of the currently processing tier. None if batch hasn't started processing (status=DRAFT or PENDING). Updated as batch progresses through tiers. Used to show processing progress: 'Processing tier 2 of 5'. Set to last tier number when batch completes. Example: If processing tier 1 (frames), current_tier=1.
  - `total_tiers` integer — OPTIONAL (defaults to 1). Total number of tiers in the collection DAG. Minimum 1 (tier 0 only = bucket → collection). Set during DAG resolution when batch is submitted. Equals len(dag_tiers) if dag_tiers is populated. Used to calculate progress: current_tier / total_tiers. Example: 5-tier pipeline (bucket → chunks → frames → scenes → summaries) has total_tiers=5.
  - `dag_tiers` array[], nullable — OPTIONAL. Complete DAG tier structure for this batch. List of tiers, where each tier is a list of collection IDs to process at that stage. Tier 0 = bucket-sourced collections. Tier N (N > 0) = collection-sourced collections. Collections within same tier have no dependencies (can run in parallel). Collections in tier N+1 depend on collections in tier N. Populated during DAG resolution at batch submission. Used for tier-by-tier processing orchestration. Example: [['col_chunks'], ['col_frames', 'col_objects'], ['col_scenes']] = 3 tiers where frames and objects run in parallel at tier 1.
    - string[]
  - `created_at` string, date-time — OPTIONAL (auto-set on creation). ISO 8601 timestamp when batch was created. Set using current_time() from shared.utilities.helpers. Immutable after creation. Used for batch age tracking and cleanup of old batches.
  - `progress` BatchProgress — Live progress snapshot written by ProgressPoller every ~10 seconds while a batch is IN_PROGRESS. Populated by the Ray ProgressActor running inside the engine job. None when the batch has not started processing yet (DRAFT/PENDING) or if the engine image pre-dates this field.
    - `processed` integer — Number of objects fully processed so far.
    - `total` integer, nullable — Total objects to process. None until the dataset is loaded by the engine.
    - `percent` number, nullable — Completion percentage (0–100). None until total is known.
    - `items_per_second` number, nullable — Current throughput. Averaged over elapsed time since job start.
    - `eta_seconds` number, nullable — Estimated seconds remaining. None until both total and throughput are known.
    - `batch_count` integer — Number of Ray map_batch micro-batches completed.
    - `errors` integer — Number of items that errored during processing.
    - `first_error` string, nullable — First item-level error captured during processing (truncated to ~500 chars), e.g. 'ValueError: cannot embed empty text'. Explains WHAT the `errors` counter is counting. None when no item-level error has been captured (or the engine image pre-dates this field). See also batch-level error_summary (error-type counts) and failed_objects (per-object detail).
    - `extraction_first_error` string, nullable — First map/extraction-stage error (rows dropped before the datasink). Distinct from datasink write errors. Typically equals first_error unless a later write-stage error was also captured.
    - `documents_skipped` integer — Number of input rows the processor explicitly marked skipped (metadata-only objects, missing required embedding fields, content-flag filtered, null-text chunks). Skipped rows are not failures and not successes; they count toward the tier-completion invariant via the audit. Phase 1.3 of INGESTION_RELIABILITY_PLAN.md.
    - `current_stage` BatchStageInfo — Current processing stage reported by the Ray engine. BACKE-762: ``name`` is now the honest *active* streaming phase — "dispatch" → "extraction" → "write" — derived from the phase machine (the true bottleneck), NOT the premature "writing" label that the datasink used to emit on the first row. It is a LABEL ONLY; per-phase counters live in ``BatchProgress.phases``. ``index``/``total`` stay as N-of-3. The stage name resets the stall timer, so users can see *what* the job is doing even when processed=0 (e.g., waiting for a model load).
      - `name` string, required — Honest active streaming phase: 'dispatch', 'extraction', or 'write' (BACKE-762). Equals the one phase in BatchProgress.phases whose status is 'active'.
      - `index` integer, required — 1-based stage index within the current job.
      - `total` integer, required — Total stages in the current job.
      - `stage_elapsed_seconds` number — Seconds spent in this stage so far.
      - `sub_stage` string, nullable — Sub-stage within 'processing': 'model_loading' while models are initializing, 'inferring' once the first batch completes. Helps explain why processed=0.
    - `phases` BatchPhases — Honest streaming-phase breakdown (BACKE-762) — the SOLE per-stage source. dispatch → extraction → write. Exactly one phase is ``active``, and its key equals ``BatchStageInfo.name``. Consumed by Studio's BatchProgressDetail.
      - `dispatch` BatchPhaseDetail — Per-phase progress for one streaming phase (BACKE-762). Ray Data streams, so extraction and write run concurrently. Each phase carries its own honest counters. ``status`` is one of pending|active| done|error. Exactly one phase is ``active`` at a time, and it matches ``BatchStageInfo.name``. ``total`` is null when indeterminate (the write phase stays null until extraction drains — expanding pipelines emit N output points per input object).
        - `processed` integer — Items completed in this phase.
        - `total` integer, nullable — Items expected in this phase; null when indeterminate.
        - `status` string — Phase status: 'pending', 'active', 'done', or 'error'.
      - `extraction` BatchPhaseDetail — Per-phase progress for one streaming phase (BACKE-762). Ray Data streams, so extraction and write run concurrently. Each phase carries its own honest counters. ``status`` is one of pending|active| done|error. Exactly one phase is ``active`` at a time, and it matches ``BatchStageInfo.name``. ``total`` is null when indeterminate (the write phase stays null until extraction drains — expanding pipelines emit N output points per input object).
        - `processed` integer — Items completed in this phase.
        - `total` integer, nullable — Items expected in this phase; null when indeterminate.
        - `status` string — Phase status: 'pending', 'active', 'done', or 'error'.
      - `write` BatchPhaseDetail — Per-phase progress for one streaming phase (BACKE-762). Ray Data streams, so extraction and write run concurrently. Each phase carries its own honest counters. ``status`` is one of pending|active| done|error. Exactly one phase is ``active`` at a time, and it matches ``BatchStageInfo.name``. ``total`` is null when indeterminate (the write phase stays null until extraction drains — expanding pipelines emit N output points per input object).
        - `processed` integer — Items completed in this phase.
        - `total` integer, nullable — Items expected in this phase; null when indeterminate.
        - `status` string — Phase status: 'pending', 'active', 'done', or 'error'.
    - `active_step` BatchStepInfo — Active pipeline step for multi-step extractors (e.g., GroundingDINO → SigLIP). Surfaces which step is currently running and its position in the pipeline, so users can track progress through complex multi-model pipelines.
      - `name` string, required — Processor class name (e.g., 'GroundingDINOProcessor', 'SigLIPProcessor').
      - `index` integer, required — 1-based step index within the pipeline.
      - `total` integer, required — Total number of steps in the pipeline.
      - `step_elapsed_seconds` number, required — Seconds spent in this step so far.
    - `overshoot_percent` number, nullable — When processed exceeds total (due to Ray Data retries or pipeline data expansion), this field shows the excess percentage above 100%. For example, 32.0 means 132% of items have been processed. None when processed <= total.
    - `queue_position` integer, nullable — 1-based position in the Ray job submission queue. Non-null only while the batch is waiting for a concurrency slot. Once a slot is acquired (job submitted to Ray), this becomes null.
    - `stage_history` object[], nullable — Completed stage timing breakdown. Each entry: name, index, total, started_at (epoch), ended_at (epoch), duration_seconds. Populated as stages complete; the current (in-progress) stage is in current_stage.
    - `documents_written` integer, nullable — Derived documents written across completed tier/extractor jobs.
    - `chunk_stats` BatchChunkStats — Realized chunking statistics reported by the text chunker (MHC-250). Surfaces what chunking ACTUALLY did, measured in the split strategy's own unit, so an inert or unit-confused chunking config (for example a chunk_size chosen in the wrong unit) is visible by reading the batch instead of forensically diffing two configs' outputs.
      - `unit` string, required — Realized unit the chunker grouped by: characters, words, sentences, paragraphs, pages, seconds (time_segments), or none (no chunking; one chunk per input row).
      - `total_chunks` integer — Total chunks produced across all processed rows.
      - `mean_units_per_chunk` number, nullable — Mean realized chunk size, measured in `unit`. A value near 1 for a count unit (sentences/paragraphs/pages) with a larger configured chunk_size means the configuration is not taking effect as intended.
    - `status_warnings` string[] — Read-time warnings that explain ambiguous terminal status or accounting gaps.
  - `documents_written` integer, nullable — OPTIONAL. Read-time aggregate of documents_written from tier_tasks and extractor_jobs. None means the completion callback has not reported write accounting yet.
  - `pages_dropped` integer, nullable — OPTIONAL. Read-time aggregate of pages NOT indexed across extractor jobs — multi-page inputs (PDFs) capped by max_document_pages or dropped by per-page failures. Only present when > 0: a COMPLETED batch with pages_dropped > 0 indexed its inputs PARTIALLY. See pages_dropped_reasons; raise the collection's max_document_pages to index more pages.
  - `pages_dropped_reasons` object, nullable — OPTIONAL. Read-time aggregate of dropped-page counts by reason: max_document_pages_cap (input exceeded the collection's max_document_pages) or page_processing_failure (per-page extract/embed errors).
  - `segments_dropped` integer, nullable — OPTIONAL. Read-time aggregate of video segments NOT indexed across extractor jobs — videos capped by max_video_segments. Only present when > 0: a COMPLETED batch with segments_dropped > 0 indexed its videos PARTIALLY. See segments_dropped_reasons; raise max_video_segments to cover more of each video.
  - `segments_dropped_reasons` object, nullable — OPTIONAL. Read-time aggregate of dropped-segment counts by reason: config_cap_max_video_segments (video exceeded the collection's max_video_segments).
  - `status_diagnostics` object — Read-time diagnostics explaining terminal status, error indicators, and document-write accounting.
  - `documents_resolvable` boolean — ALWAYS PRESENT (BACKE-2960). True only after the server CONFIRMED this terminal batch's just-written documents are actually queryable via the filtered document get path: a confirmation, never a prediction, timestamp, or elapsed time. A COMPLETED batch with documents_resolvable=false is still finalizing: its documents may 404 on direct GET even though they are durable (Studio renders status==COMPLETED && documents_resolvable==false as a 'Finalizing, indexing your results' state and keeps polling). Batches created before the feature shipped emit true (historical, long since resolvable); post-ship batches with no stored value emit false (fail toward not-ready). If the bounded post-completion confirm cannot succeed, documents_resolvable_error is set and this flag stays false.
  - `documents_resolvable_error` string, nullable — Terminal failure signal for the bounded resolvability confirm (BACKE-2960/BACKE-2987). Set when the post-completion confirm loop could not confirm this batch's documents were queryable within its bound, the case where documents may NEVER become resolvable. When set, documents_resolvable stays false and will not flip: stop polling and surface a 'taking longer than expected' state instead of a spinner. Null while the confirm is pending or after it succeeded.
  - `health` string, nullable — OPTIONAL. Computed health status for actively processing batches. Only populated when status is PROCESSING or IN_PROGRESS. Values: 'healthy' (recent activity detected), 'stalled' (no activity for 5+ minutes), 'unknown' (no heartbeat data yet). Computed from tier_tasks[].last_activity_at and updated_at. Use this to detect stuck batches before the internal stall detector kills them.
  - `cost` BatchCost — Dollar cost of a batch, derived from recorded credit usage. Mixpeek meters work in *credits* (recorded in the usage_records collection at credit-consume time, keyed by ``resource_id=batch_id`` / ``resource_type="batch"``). There is no separately-stored per-batch USD figure, so cost is derived at read time as ``credits_consumed * CREDIT_RATE_USD`` using the same canonical $0.001/credit rate the billing/invoicing path uses. This lets callers answer "what did this batch cost?" from the batch GET response without GCP billing labels or a separate billing query.
    - `credits_consumed` integer — Total credits recorded against this batch in usage_records (resource_type='batch'). 0 if no usage has been recorded yet (e.g. DRAFT batches, ENTERPRISE tiers that skip credit consumption, or batches submitted before metering existed).
    - `cost_usd` number — Derived dollar cost = credits_consumed * $0.001/credit (CREDIT_RATE_USD). Same rate used by monthly invoicing, so this matches what the batch contributes to the bill.
    - `credit_rate_usd` number — USD per credit used to derive cost_usd ($0.001/credit).
  - `last_activity_at` string, date-time, nullable — OPTIONAL. Timestamp of the most recent activity across all tier tasks. Aggregated from tier_tasks[].last_activity_at — the latest heartbeat from any tier. Updated approximately every 10 seconds by the BatchJobPoller while processing. A stale value (minutes old) while status is PROCESSING indicates the batch may be stalled. None for batches that have not started processing or have no heartbeat data.
  - `retry_count` integer — OPTIONAL (defaults to 0). Number of times this batch has been auto-retried due to transient infrastructure failures (spot node preemption, OOM, actor death). Incremented each time the batch is automatically requeued after a retryable failure. User-facing: lets users see that retries happened transparently.
  - `max_retries` integer — OPTIONAL (defaults to 3). Maximum number of automatic retries for transient failures. When retry_count reaches max_retries, the batch stays in FAILED state. Only transient/infrastructure failures trigger retries — validation and data errors do not.
  - `last_retry_at` string, date-time, nullable — OPTIONAL. ISO 8601 timestamp of the most recent auto-retry attempt. None if the batch has never been retried. Used to calculate exponential backoff for subsequent retries.
  - `retry_reason` string, nullable — OPTIONAL. Human-readable reason for the most recent auto-retry. None if the batch has never been retried. Describes the transient failure that triggered the retry (e.g., 'Spot node preempted', 'Ray actor died', 'OOM killed').
  - `webhook_url` string, nullable — OPTIONAL. URL to receive an HTTP POST notification when the batch reaches a terminal state (COMPLETED, FAILED, or CANCELED). Set at submit time via SubmitBatchRequest. The webhook is fire-and-forget: delivery failures are logged but never affect batch processing.
  - `updated_at` string, date-time — OPTIONAL (auto-updated). ISO 8601 timestamp when batch was last modified. Updated using current_time() whenever batch status or tier_tasks change. Used to track batch activity and identify stale batches.
  - `status_message` string, nullable — COMPUTED. Human-readable description of the current batch state. Examples: 'Processing 724/50,000 objects (1.4%)', 'Queued — 2 batches ahead', 'Completed in 5m 23s', 'Loading model (stage 1/3)'. Computed on read, not stored in the database.
  - `estimated_completion` string, date-time, nullable — COMPUTED. Estimated completion timestamp based on current throughput. Derived from progress.eta_seconds + now. None if throughput data is unavailable. Computed on read, not stored in the database.

## Other responses

- `400` — Bad Request
- `401` — Unauthorized
- `403` — Forbidden
- `404` — Not Found
- `422` — Validation Error
- `500` — Internal Server Error

---

[API](https://skmtc.net/mixpeek/apis/mixpeek-api.md) · [All operations](https://skmtc.net/mixpeek/apis/mixpeek-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/mixpeek/mixpeek-api/versions/220a3b263fda/schema)
