> ## Documentation Index
> Fetch the complete documentation index at: https://docs.solya.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Ingestion specs — anatomy

> The complete structure of an ingestion spec: how a file flows through detection, parsing, and promotion, plus scope, priority, and versioning.

An **ingestion spec** is the full contract that turns an incoming file into rows in
Solya's analytics model. This reference documents every part of it. For the user-facing
introduction, see the [data layer guide](/en/data-layer/ingestion-spec).

## How a file flows through a spec

<Steps>
  <Step title="Detection">
    Solya decides **which spec** applies to an incoming file, using the spec's
    [detection rules](/en/developers/ingestion-specs/detection).
  </Step>

  <Step title="Parsing">
    The file is read into a tabular form and its columns are mapped to typed fields, using
    the [parsing config](/en/developers/ingestion-specs/parsing).
  </Step>

  <Step title="Promotion">
    One or more [promotion pipelines](/en/developers/ingestion-specs/promotion-steps)
    transform the parsed data and write it to silver/gold tables.
  </Step>
</Steps>

```mermaid theme={null}
flowchart TB
  file["Incoming File"]
  det["Detection Config<br/>(match rules)"]
  parse["Parsing Config<br/>(field mapping)"]
  promo["Promotions<br/>(pipeline array)"]
  silver["Silver Tables"]
  gold["Gold Tables"]
  
  file --> det
  det --> parse
  parse --> promo
  promo --> silver
  promo --> gold
```

## Top-level shape

A spec is authored as JSON (snake\_case keys). Its fields:

| Field             | Required    | Description                                                                                                |
| ----------------- | ----------- | ---------------------------------------------------------------------------------------------------------- |
| `spec_id`         | ✓           | Canonical identifier, stable across versions (e.g. `polaris_sav`, `ginkoia_tickets`).                      |
| `spec_version`    | ✓           | Monotonic version number. Unique per `(spec_id, spec_version)`.                                            |
| `name`            | –           | Display name.                                                                                              |
| `description`     | –           | What the spec is for.                                                                                      |
| `pos_system`      | –           | Source system identifier (e.g. `polaris`, `ginkoia`).                                                      |
| `status`          | ✓           | `DRAFT` · `ACTIVE` · `DEPRECATED` · `ARCHIVED`.                                                            |
| `scope`           | ✓           | `GLOBAL` (platform-seeded, shared) or `ORG` (organization-owned).                                          |
| `organization_id` | conditional | Required for `ORG` scope; null for `GLOBAL`.                                                               |
| `is_default`      | ✓           | Whether this is the default spec for its `pos_system` + `scope`.                                           |
| `priority`        | ✓           | Tie-breaker when several specs match (higher wins). Default `0`.                                           |
| `detection`       | ✓           | The [detection config](/en/developers/ingestion-specs/detection).                                          |
| `parsing`         | ✓           | The [parsing config](/en/developers/ingestion-specs/parsing).                                              |
| `promotions`      | –           | An array of [promotion pipelines](/en/developers/ingestion-specs/promotion-steps), one per output dataset. |
| `promotion`       | –           | Deprecated single-promotion form; use `promotions`.                                                        |
| `tags`            | ✓           | `{ "systems": [...], "formats": [...] }` for UI filtering.                                                 |

```json theme={null}
{
  "spec_id": "ginkoia_tickets",
  "spec_version": 2,
  "name": "Ginkoia — Tickets",
  "pos_system": "ginkoia",
  "status": "ACTIVE",
  "scope": "GLOBAL",
  "is_default": true,
  "priority": 0,
  "detection": { "match_mode": "composite", "rules": [ /* … */ ] },
  "parsing":   { "parser": { /* … */ }, "mapping": { /* … */ } },
  "promotions": [ { /* … */ } ],
  "tags": { "systems": ["ginkoia"], "formats": ["excel"] }
}
```

## Scope: GLOBAL vs ORG

* **GLOBAL** specs are seeded by the platform and available to every organization. They
  cover the standard POS formats (Polaris, Ginkoia, Kezia, …).
* **ORG** specs belong to a single organization (`organization_id` set) and are only
  visible to it.

## Deployment

Existence isn't the same as activation for an org. A spec is **deployed** to an organization
via an activation record (`enabled` flag) — so an org turns specs on/off without anyone
editing the spec itself. In the API/DTO this surfaces as `isDeployed` on each spec.

## Priority & conflict resolution

When more than one spec matches a file:

1. Specs are ranked by **`priority`** (descending).
2. Newer **`spec_version`** takes precedence.
3. The **first matching** spec is applied.

## Lifecycle & versioning

```
DRAFT → ACTIVE ⇄ DEPRECATED → ARCHIVED
```

* A `spec_id` can have **multiple versions**; `(spec_id, spec_version)` is unique.
* Seeding is **idempotent** (upsert on that pair); GLOBAL specs removed from the seed set
  are **archived**, not deleted, to preserve history.

```mermaid theme={null}
stateDiagram-v2
  [*] --> DRAFT
  DRAFT --> ACTIVE
  ACTIVE --> DEPRECATED
  DEPRECATED --> ACTIVE
  DEPRECATED --> ARCHIVED
  ARCHIVED --> [*]
```

<Note>
  Continue to the three building blocks:
  [Detection](/en/developers/ingestion-specs/detection),
  [Parsing](/en/developers/ingestion-specs/parsing), and the
  [Promotion steps](/en/developers/ingestion-specs/promotion-steps) catalog — then see
  full [examples](/en/developers/ingestion-specs/examples).
</Note>
