# TOVIA import contract — v0

**Status: in development.** This is the shape TOVIA is building against,
published early so you can check an export against it before there is
anything to import into. It will change; the version number is how you will
know that it did.

Five entities. CSV or JSON Lines, UTF-8, one file per entity. Every import
goes through staging and preview before it is committed, and every committed
import is reversible.

Generated from `src/lib/import-contract.ts` in the tovia-web repository — do
not edit this file by hand.

## `orders` — one row per order

| Field | Required | Meaning |
| --- | --- | --- |
| `order_id` | yes | The id the channel uses. Unique per channel. |
| `channel` | yes | Where the order came from: shop, marketplace, B2B. |
| `market` | yes | The country the goods are placed in — not the buyer's language. |
| `currency` | yes | ISO 4217, three letters. |
| `total_gross` | yes | Order total including tax, in the order's currency. |
| `total_net` | yes | Order total excluding tax. |
| `total_tax` | yes | Tax total, so the two above can be reconciled rather than trusted. |
| `status` | yes | The channel's own status string. Mapped on import, never overwritten. |
| `placed_at` | yes | ISO 8601 with an offset. Local time without one cannot be reconciled. |
| `updated_at` | no | ISO 8601 with an offset. |

## `order_lines` — one row per line of an order

| Field | Required | Meaning |
| --- | --- | --- |
| `order_id` | yes | Foreign key to orders. |
| `line_id` | yes | Stable within the order, so a re-import updates rather than duplicates. |
| `sku` | yes | Your SKU. Matched to a variant in the product master. |
| `gtin` | no | GTIN or EAN, where the channel carries one. |
| `quantity` | yes | Integer. Negative quantities belong in refunds. |
| `unit_price_net` | yes | Per unit, excluding tax. |
| `tax_rate` | yes | Percentage applied to the line. |

## `refunds` — one row per refunded amount

| Field | Required | Meaning |
| --- | --- | --- |
| `order_id` | yes | Foreign key to orders. |
| `line_id` | no | Empty for an order-level refund. |
| `amount_gross` | yes | Positive number; the sign is carried by the entity, not the value. |
| `reason` | no | The channel's reason code, or free text. |
| `refunded_at` | yes | ISO 8601 with an offset. |

## `customers` — one row per customer, only where you already hold the data

| Field | Required | Meaning |
| --- | --- | --- |
| `customer_id` | yes | Your id, or the channel's. Pseudonymous is fine. |
| `type` | yes | B2C or B2B. Drives price context and tax treatment. |
| `country` | yes | ISO 3166-1 alpha-2. |
| `vat_id` | no | B2B only, for reverse-charge treatment. |

## `products` — one row per sellable variant

| Field | Required | Meaning |
| --- | --- | --- |
| `sku` | yes | The variant's identifier. Unique in your catalogue. |
| `gtin` | no | GTIN, EAN or UPC. Validated on import, check digit included. |
| `mpn` | no | Manufacturer part number. |
| `parent_sku` | no | The product master this variant belongs to. Empty for a single-variant product. |
| `title` | yes | One column per locale: title.en, title.ro, title.es. |
| `attributes` | no | One column per attribute, per locale, in the same key.locale shape. |
| `price` | no | One column per price context: price.retail.EUR, price.b2b.EUR. |

## What happens to a file you send

1. **Staging** — the file is parsed and validated against this contract.
   Nothing touches your catalogue.
2. **Preview** — you see what would change: rows created, rows updated, rows
   rejected and why.
3. **Commit** — you approve. The import is recorded, and it is reversible.

Your platform is not one we list? That is what this contract is for. Tell us
what you can export.
