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

# Ingest API

> Push external data into your workspace: API keys, endpoint pattern, idempotent upserts, errors, and Make.com templates.

The Ingest API is the write path for **external systems** — shop syncs, Make.com scenarios, transcription tools, scripts. It is a plain HTTPS endpoint on your Concierca AI app (not an MCP tool): one URL pattern for every ingest-enabled table, authenticated with a scoped API key.

```http theme={null}
POST /api/ingest/<slug>
Authorization: Bearer pk_...
Content-Type: application/json
```

`<slug>` is the table's view slug (e.g. `orders`, `suppliers`, `meeting_notes`). Which tables are ingest-enabled is registry-driven — a newly registered table needs zero code, and the current set is always listed in **Settings → Data Sources**.

## Generating a key

Keys are managed in **Settings → Data Sources** (Company-Admin only):

1. Open the card of the table you want to fill and click **Generate API Key**.
2. Give it a label (e.g. "Make.com Shopify Flow") — the key is created scoped to exactly that table.
3. **Copy the key immediately.** It is shown once; only a SHA-256 hash and the first characters (prefix) are stored.

Keys look like `pk_` followed by 48 hex characters. A key's **scopes** decide which slugs it may write: a per-table key is the least-privilege default; a key with the wildcard scope `*` is a **Master Import Key** that covers every ingest table, including tables added after the key was issued. Keys can be revoked at any time — a revoked key gets `401` on the next call.

## Request semantics

* **One JSON record per request.** Send arrays as one request per element.
* **Idempotent upsert on `(tenant_id, external_id)`.** Re-sending the same `external_id` updates the existing row — retries and re-deliveries are always safe. Use the source system's stable id (Shopify gid, meeting id, PO line id) as `external_id`. Exception: [Products](/for-agents/data-model/products) upsert on `identity_key = COALESCE(external_id, sku)` so SKU-only integrations keep working.
* **Only registered columns are accepted.** The payload is sanitized against the table's registered column set; unknown fields are dropped, and values are coerced to the column type. Each table page in the [Data Model](/for-agents/data-model/orders) section lists the accepted fields.
* **Tenant identity comes from the key.** `tenant_id` and `workspace_id` are stamped from the API-key context — never send them in the body.
* **Schema discovery:** `GET /api/ingest/<slug>` returns the expected columns, the required scope, and the conflict key for that table.

## Responses

| Status | Body                                                                            | Meaning                                                                                                                 |
| ------ | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| 200    | `{"ok": true, "event_type": "ingest.<slug>.received"}`                          | Record upserted                                                                                                         |
| 400    | `{"error": "invalid JSON body"}`                                                | Body is not valid JSON                                                                                                  |
| 401    | `{"error": "missing Bearer token"}` / `{"error": "invalid or revoked api key"}` | Authentication failed                                                                                                   |
| 403    | `{"ok": false, "error": "missing scope: <slug>"}`                               | Key is valid but not scoped for this table                                                                              |
| 404    | `{"ok": false, "error": "unknown ingest type: <slug>"}`                         | No ingest-enabled table under this slug                                                                                 |
| 422    | `{"ok": false, "error": "..."}`                                                 | The database rejected the record (duplicate key, missing required field, unknown reference, or a validation constraint) |
| 500    | `{"ok": false, "error": "..."}`                                                 | Transient fault (registry lookup, event emit, or database error) — safe to retry thanks to the idempotent upsert        |

## Failed imports

A record that reaches the database but fails to materialize (422/500) is never silently lost: it is written to the **Failed Imports** dead-letter with its raw payload and the error. Review them in the app or over MCP with `failed_import_list`, fix the payload, and re-send — the upsert semantics make the retry safe.

## Make.com flow templates

For the Shopify-shaped tables (Orders, Customers, Products, Order Line Items), **Settings → Data Sources** offers a downloadable **Make.com flow template** (a ready-made scenario blueprint JSON). Import it into Make, paste your API key into the HTTP module, and the field mapping is pre-wired to the ingest schema.

## Ingest-enabled tables

| Table            | Endpoint                                | Reference                                                   |
| ---------------- | --------------------------------------- | ----------------------------------------------------------- |
| Orders           | `POST /api/ingest/orders`               | [Orders](/for-agents/data-model/orders)                     |
| Order Line Items | `POST /api/ingest/order_line_items`     | [Order Line Items](/for-agents/data-model/order-line-items) |
| Customers        | `POST /api/ingest/customers`            | [Customers](/for-agents/data-model/customers)               |
| Products         | `POST /api/ingest/products`             | [Products](/for-agents/data-model/products)                 |
| Suppliers        | `POST /api/ingest/suppliers`            | [Suppliers](/for-agents/data-model/suppliers)               |
| Purchase Orders  | `POST /api/ingest/purchase_order_lines` | [Purchase Orders](/for-agents/data-model/purchase-orders)   |
| Meeting Notes    | `POST /api/ingest/meeting_notes`        | [Meeting Notes](/for-agents/data-model/meeting-notes)       |

The computed views ([Product Demand](/for-agents/data-model/product-demand), [Procurement Queue](/for-agents/data-model/procurement-queue), [Customer Reorder Due](/for-agents/data-model/customer-reorder-due), [Family Trend](/for-agents/data-model/family-trend), [Weekly Sales](/for-agents/data-model/weekly-sales), [Seasonality](/for-agents/data-model/seasonality)) have no ingest endpoints — they are derived automatically from the tables above. [Procurement Config](/for-agents/data-model/procurement-config), [Strategy](/for-agents/data-model/strategy), and [SOP](/for-agents/data-model/sop) are maintained in the app or via MCP tools.

<Note>
  Your tenant may have additional ingest-enabled tables (e.g. Decisions) — Settings → Data Sources always shows the live set, and `GET /api/ingest/<slug>` describes any of them.
</Note>
