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

# External IDs

The Emidat API is built so you can drive it with **your own identifiers**, which are
considered `external_id` in the following. You attach your identifiers when you
create writable entities, filter list endpoints by those same codes, and receive
them back on every response. Read-only plants and production processes expose the
codes configured in the Emidat UI. References between entities in request bodies
use the Emidat `id` (UUID); you obtain each `id` from the create response or by
resolving your code with `GET …?external_id=`.

## Two kinds of identifier

| Identifier        | Who owns it | Scope            | Mutable                         | Example                    |
| ----------------- | ----------- | ---------------- | ------------------------------- | -------------------------- |
| **Emidat UUID**   | Emidat      | Global           | No                              | `8f3a1c…`                  |
| **`external_id`** | You         | Per-manufacturer | On writable entities, via `PUT` | `PLANT-DE-01`, `SKU-12345` |

## Defining an external\_id

`external_id` is **optional**. For writable entities, you set it when you create
the entity and can change it later through its `PUT` (see below). It is an
arbitrary string you choose — no format rules and no length limit — so your
existing ERP codes work as-is. Surrounding whitespace is trimmed, so `"ART-1 "`
and `"ART-1"` are the same code, and a string that is empty or whitespace-only is
treated as **no code at all**, exactly like `null`, rather than rejected. Plant
and production process codes are managed in the Emidat UI instead.

Within your manufacturer an `external_id` is unique among active entities of a
given type. It is **descriptive only** — not an idempotency key. Creating another
entity of the same type with a code that already exists is rejected with `409`
(see idempotency below).

## Changing an external\_id

Every writable entity that carries an `external_id` lets you change it through
its `PUT` endpoint. Plants and production processes are read-only in API v2, so
their `external_id` must be changed in the Emidat UI.

`PUT` is a **full replacement of editable fields**, so send `external_id` on every
update, carrying either the code you want or the entity's current one. Passing
`null` clears the code. Immutable parent and type fields may be omitted or echoed
from the current resource; a different value returns `422`.

Uniqueness is re-checked on update. A code held by another active entity of the
same type is rejected with `409 err_external_id_exists`; re-sending the entity's
**own** current code is not a conflict and succeeds unchanged.

Three consequences worth planning for:

* The old code **stops resolving** — `GET …?external_id=OLD-CODE` returns an empty
  page.
* The freed code becomes **available again** to another entity of the same type.
* **References are unaffected.** Other entities point at this one by its Emidat
  UUID, never by your code, so no BOM entry or `*_id` field needs updating.

## Addressing an entity

One rule, applied to every entity:

* **A path parameter is always the Emidat UUID** — `GET /v2/product-instances/{id}` returns
  that one instance, or `404`.
* **Your `external_id` is a filter on the collection** —
  `GET /v2/product-instances?external_id=SKU-12345` returns a page whose `data`
  holds the single instance carrying that code, or is empty if none does. The
  filter is **optional**: omitting it lists every instance you can access, one
  page at a time. See [Pagination](/pagination).

Because uniqueness makes the filtered result at most one item, a code lookup and a
full listing are the same endpoint — you never need a separate call shape for the
two.

## Idempotent creation

`external_id` is **not** an idempotency key — idempotency comes solely from the
`Idempotency-Key` header.

**`Idempotency-Key` header.** Generate a unique key (e.g. a UUID) per logical
create and send it as `Idempotency-Key: <key>`. A replay carrying the same key
returns the **original response unchanged** (`200`). Reusing a key with a
*different* request body is rejected with `409`. Keys are scoped to your
manufacturer and retained for **6 hours**; after that a replay is treated as a
new create.

So the status tells you what happened: `201` = created, `200` = a key replay
returned the original entity. The request body is **not** re-applied on a `200` —
to change an entity, use its `PUT` endpoint.

Because `external_id` is unique per type, a `POST` carrying an `external_id` that already
exists is rejected with `409` — it is never silently deduplicated.

## Referencing other entities

When a request body points at another entity, you reference it by its **Emidat
`id` (UUID)** — never by your own code. There are two ways to obtain that UUID:

* For an entity you **just created** this run, use the `id` from its create
  response (the create response always returns it).
* For an entity you **don't already have the UUID for**, resolve it first with
  `GET …?external_id=YOUR-CODE`, whose `data` holds at most one item with its `id`.
  This works only for entities that **have** an `external_id`; one created without
  a code can be referenced only via the `id` returned by its create response, or by
  walking the unfiltered list.

```http theme={"dark"}
GET /v2/supplier-products?external_id=CEMENT-SUPPLIER-DE-001
→ 200 {
    "data": [ { "id": "33333333-3333-3333-3333-333333333333", "external_id": "CEMENT-SUPPLIER-DE-001", … } ],
    "next_cursor": null
  }
```

Then use that `id` wherever a reference is needed — for example as a BOM entry's
`ref`:

```json theme={"dark"}
PUT /v2/product-instances/{id}/bom
{
  "bom": [
    { "type": "supplier_product", "ref": "33333333-3333-3333-3333-333333333333", "mass_kg": 0.18 }
  ]
}
```
