# Get a submission by ID

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

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

Returns one immutable IGP24 submission-history record and its current per-position validation results.

The record belongs to the immutable team recorded at the competition deadline. Every eligible deadline-team member can read it; an owner role and current active-team membership are not required. Obtain the opaque `submissionId` from [Submit polynomials](./submit-polynomials.md) or [List my submissions](./list-my-submissions.md). Do not construct it from a batch ID or another resource.

Submitted coefficient strings are intentionally omitted from JSON. Use [Download a submission](./download-submission.md) when the original lines and comments are needed.

## Path parameter

| Parameter | Type | Required | Description |
| :--- | :--- | :---: | :--- |
| `submissionId` | string | Yes | Exact, case-sensitive public history-record ID returned by a submission endpoint. |

## Response

```ts
type GetIgp24SubmissionResponse = {
  ok: true;
  data: Igp24SubmissionDetail;
};

type Igp24SubmissionDetail = {
  submissionId: string;
  competitionId: 'igp24';
  kind: 'igp24-polynomial';
  batchId?: string;
  submissionStatus: 'queued' | 'complete';
  totalCount: number;
  submittedCount: number;
  filteredPublishedCount: number;
  queuedForEvaluationCount: number;
  queuedCount: number;
  verifiedCount: number;
  failedCount: number;
  sourcePhase: 'competition' | 'discovery_only';
  officialScoringEligible: boolean;
  polynomials: Igp24SubmissionPolynomial[];
  meta: {
    description?: string;
  };
  createdAt: string;
  updatedAt: string;
};

type Igp24SubmissionPolynomial =
  | {
      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;
    };
```

### Submission fields

| Field | Description |
| :--- | :--- |
| `submissionId` | Stable public ID of this append-only history record. |
| `competitionId` | Always `igp24`. |
| `kind` | Always `igp24-polynomial`. |
| `batchId` | Persistent validation batch ID. Omitted when every original position was malformed and no batch was created. |
| `submissionStatus` | `queued` while at least one original position awaits validation; otherwise `complete`. |
| `totalCount` | Original request positions represented in `polynomials[]`, including malformed, duplicate, filtered, and evaluated positions. |
| `submittedCount` | Well-formed positions accepted by admission, including duplicates and filtered positions. |
| `filteredPublishedCount` | Positions matched to the published Open Data release and not evaluated again. |
| `queuedForEvaluationCount` | Distinct canonical polynomials admitted to asynchronous evaluation. This admission-time count is fixed. |
| `queuedCount` | Original positions still waiting for a verdict, including duplicate positions. |
| `verifiedCount` | Original positions whose current status is `ok`. |
| `failedCount` | Original positions whose current status is `invalid` or `error`, including malformed positions. |
| `sourcePhase` | Phase frozen when the submission was accepted. New post-event records use `discovery_only`. |
| `officialScoringEligible` | Whether this record 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`. |
| `meta.description` | Optional stored batch note. |
| `createdAt` | ISO 8601 UTC acceptance time. |
| `updatedAt` | ISO 8601 UTC time when this visible detail last changed. |

The current-position counts satisfy:

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

`submissionStatus: "complete"` means every position is terminal, not that every polynomial verified successfully. Inspect the counts and every item in `polynomials[]`.

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

`polynomialIndex` is the position in the original request, starting at `0`. Duplicate inputs remain separate positions even when the server evaluates one canonical polynomial and fans the verdict out to every duplicate.

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

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`. Top-level `officialScoringEligible` remains authoritative for official impact.

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

## Example request

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

## Example response

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

```json
{
  "ok": true,
  "data": {
    "submissionId": "sub_01JIGP24000000000000000001",
    "competitionId": "igp24",
    "kind": "igp24-polynomial",
    "batchId": "igp24_lab_batch_8c1f932a72984ff9b51a63c1a62fd997",
    "submissionStatus": "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"
      }
    ],
    "meta": {
      "description": "Search batch 12."
    },
    "createdAt": "2026-08-31T12:34:56Z",
    "updatedAt": "2026-08-31T12:40:03Z"
  }
}
```

## Resource behavior

- `submissionId`, the original lines, `meta`, `createdAt`, `sourcePhase`, and `officialScoringEligible` are fixed when the record is accepted. Validation results, current counts, `submissionStatus`, and `updatedAt` advance as asynchronous work finishes.
- When `batchId` is present, this response and [Get batch status](./get-batch-status.md) use the same positional result and count semantics. Values observed at different times may differ while validation is running.
- The record remains readable after the official competition closes and after the caller leaves or changes an active team, provided the caller belongs to the record's deadline team.
- Moving a valid `submissionId` to another competition path, using another deadline team's ID, or requesting an unknown ID produces the same not-found response.

## Errors

| HTTP | Code | When |
| :---: | :--- | :--- |
| `404` | `NOT_FOUND` | IGP24 is not publicly visible, or the submission does not exist in IGP24 or is not visible to the caller's deadline team. |
| `503` | `IGP24_SERVICE_UNAVAILABLE` | The authoritative service cannot return a current and complete positional detail. |

These not-found cases intentionally share one response so submission IDs cannot be used to discover another team's work. See [Errors](../../../errors.md) for shared authentication, scope, and account-rate-limit errors.
