# Get batch status

```http
GET /api/public/v1/competitions/igp24/submissions/batch/{batchId}
```

**Scope**: `competition.read`.

Polls a persistent IGP24 validation batch created by [Submit polynomials](./submit-polynomials.md). The response preserves every original request position while asynchronous validation moves entries from `queued` to a terminal result.

The batch belongs to the immutable team recorded at the competition deadline. Every eligible deadline-team member can read it; an owner role is not required. An unknown batch and a batch outside the caller's deadline team both return `404 BATCH_NOT_FOUND` so batch IDs cannot be used to discover another team's work.

New batches currently run in post-event validation mode. Their mathematical verdicts may create Discovery records, but cannot change the frozen official score, rank, awards, or results.

## Parameters

| Parameter | Location | Type | Required | Description |
| :--- | :--- | :--- | :---: | :--- |
| `batchId` | path | string | Yes | Batch ID returned when a persistent submission was accepted. |

## Response

```ts
type GetIgp24SubmissionBatchStatusResponse = {
  ok: true;
  data: {
    batchId: string;
    batchStatus: 'queued' | 'complete' | 'failed' | 'empty';
    totalCount: number;
    submittedCount: number;
    filteredPublishedCount: number;
    queuedForEvaluationCount: number;
    queuedCount: number;
    verifiedCount: number;
    failedCount: number;
    sourcePhase: 'competition' | 'discovery_only';
    officialScoringEligible: boolean;
    polynomials: Igp24BatchPolynomial[];
  };
};

type Igp24BatchPolynomial =
  | {
      polynomialIndex: number;
      status: 'queued';
      submittedAt: string;
    }
  | {
      polynomialIndex: number;
      status: 'filtered';
      submittedAt: string;
      reasonCode: 'ALREADY_PUBLISHED_IGP24_POLYNOMIAL';
      message: string;
    }
  | {
      polynomialIndex: number;
      status: 'ok';
      submittedAt: string;
      label: string;
      t: number;
      r: number;
      scoreable: boolean;
      scoringStatus: 'pending' | 'scoreable' | 'not_scoreable' | 'no_score';
      scoringReason?: string;
      noScoreReason?: string | null;
      reason?: string;
      inBaseline: boolean;
      baselineUnlocked: boolean;
      baselineDiscAbs?: string;
      fieldDiscAbs?: string;
      discSource?: 'exact_nfdisc' | 'mixed_disc';
    }
  | {
      polynomialIndex: number;
      status: 'invalid' | 'error';
      submittedAt?: string;
      reason: string;
    };
```

### Batch summary

| Field | Description |
| :--- | :--- |
| `batchId` | Polled batch ID. |
| `batchStatus` | Aggregate state defined below. |
| `totalCount` | Original request positions represented in `polynomials[]`, including malformed, duplicate, filtered, and evaluated positions. |
| `submittedCount` | Well-formed request positions accepted by batch admission, including duplicates and filtered positions; malformed positions are excluded. |
| `filteredPublishedCount` | Positions matched to the published Open Data release and therefore not evaluated again. |
| `queuedForEvaluationCount` | Distinct canonical polynomials admitted to asynchronous evaluation. This admission-time count does not decrease as work finishes. |
| `queuedCount` | Original request positions still waiting for a verdict, including duplicate positions. |
| `verifiedCount` | Original request positions whose current status is `ok`. |
| `failedCount` | Original request positions whose current status is `invalid` or `error`, including malformed positions rejected before storage. |
| `sourcePhase` | Phase frozen when the batch was first accepted. New post-event batches use `discovery_only`. |
| `officialScoringEligible` | Whether the batch can affect official scoring. It is permanently `false` when `sourcePhase` is `discovery_only`. |
| `polynomials[]` | One result per original request position, ordered by zero-based `polynomialIndex`. Duplicate inputs remain separate positions even when evaluated once. |

The current-position counts satisfy:

```text
queuedCount + verifiedCount + failedCount + filteredPublishedCount = totalCount
```

`totalCount`, `submittedCount`, `filteredPublishedCount`, and `queuedForEvaluationCount` are fixed at admission. Only `queuedCount`, `verifiedCount`, and `failedCount` change while validation runs.

### Aggregate status

| Value | Meaning |
| :--- | :--- |
| `queued` | At least one original position is still queued. |
| `complete` | Nothing is queued and at least one position is `ok` or `filtered`; failed positions may also be present. |
| `failed` | Nothing is queued, no position is `ok` or `filtered`, and at least one position is `invalid` or `error`. |
| `empty` | The batch contains no positions. This is a terminal legacy state; a new all-malformed submission has no `batchId` and is read through submission history instead. |

`complete`, `failed`, and `empty` are terminal.

### Per-polynomial results

| Status | Meaning |
| :--- | :--- |
| `queued` | The position is waiting for asynchronous validation. |
| `filtered` | The polynomial already exists in the published Open Data release. It is terminal and is not evaluated again. |
| `ok` | Mathematical validation completed successfully. Scoring fields describe the phase-specific scoring state. |
| `invalid` | The input was malformed or failed mathematical validation. |
| `error` | Validation could not complete because of an execution or service error. |

`submittedAt` is the ISO 8601 UTC time at which a well-formed position entered the persistent batch. It is omitted when a malformed position was rejected before storage. Duplicate positions may share the same timestamp and verdict.

For `status: "ok"`, `label`, `t`, and `r` identify the computed transitive group and signature. In post-event mode, the result uses `scoringStatus: "no_score"`, `scoringReason: "post_event_validation_only"`, and `scoreable: false`. Batch-level `officialScoringEligible` remains the authoritative indication of official impact.

`baselineDiscAbs` and `fieldDiscAbs` are decimal strings because their integer values may exceed JavaScript's safe integer range. `discSource` identifies the discriminant source when one is available.

## Example

```bash
curl "https://api.sair.foundation/api/public/v1/competitions/igp24/submissions/batch/igp24_lab_batch_8c1f932a72984ff9b51a63c1a62fd997" \
  -H "Authorization: Bearer $SAIR_API_KEY"
```

The IDs and timestamps below are illustrative. This batch is still `queued` because one position has not reached a terminal result.

```json
{
  "ok": true,
  "data": {
    "batchId": "igp24_lab_batch_8c1f932a72984ff9b51a63c1a62fd997",
    "batchStatus": "queued",
    "totalCount": 4,
    "submittedCount": 3,
    "filteredPublishedCount": 1,
    "queuedForEvaluationCount": 2,
    "queuedCount": 1,
    "verifiedCount": 1,
    "failedCount": 1,
    "sourcePhase": "discovery_only",
    "officialScoringEligible": false,
    "polynomials": [
      {
        "polynomialIndex": 0,
        "status": "ok",
        "submittedAt": "2026-08-31T12:34:56Z",
        "label": "24T25000",
        "t": 25000,
        "r": 0,
        "scoreable": false,
        "scoringStatus": "no_score",
        "scoringReason": "post_event_validation_only",
        "noScoreReason": "post_event_validation_only",
        "reason": "Post-event validation results never affect official scoring or ranking",
        "inBaseline": false,
        "baselineUnlocked": false
      },
      {
        "polynomialIndex": 1,
        "status": "filtered",
        "submittedAt": "2026-08-31T12:34:56Z",
        "reasonCode": "ALREADY_PUBLISHED_IGP24_POLYNOMIAL",
        "message": "This polynomial is already included in the published IGP24 dataset and was not evaluated."
      },
      {
        "polynomialIndex": 2,
        "status": "invalid",
        "reason": "expected 25 coefficients, found 3"
      },
      {
        "polynomialIndex": 3,
        "status": "queued",
        "submittedAt": "2026-08-31T12:34:56Z"
      }
    ]
  }
}
```

## Polling

Continue polling while `batchStatus` is `queued`. Use bounded exponential backoff with jitter and honor the general rate-limit headers described in [Rate limiting](../../../rate-limiting.md). Reuse the same `batchId`; a slow batch is not a reason to create another submission.

Stop when the batch reaches `complete`, `failed`, or `empty`. Inspect every polynomial result rather than treating `complete` as meaning that every position verified successfully.

## Errors

| HTTP | Code | When |
| :---: | :--- | :--- |
| `404` | `NOT_FOUND` | IGP24 does not exist or is not publicly visible. |
| `404` | `BATCH_NOT_FOUND` | The batch does not exist or is not visible to the caller's deadline team. |
| `503` | `IGP24_SERVICE_UNAVAILABLE` | The authoritative batch-status service is temporarily unavailable or cannot return a complete positional result. |

See [Errors](../../../errors.md) for shared authentication, scope, and account-rate-limit errors.
