Ingestion APIAPI v1 ReferenceIngestion

Create new ingestion

Creates a new ingestion job for processing product data.

POST
/v1/ingestion/

Creates a new ingestion job for processing product data.

Which processing mode should I use?

ScenarioUse this mode
Webhook updates (single product or small batch)insert
Incremental sync (only changed products)insert
Full catalog sync (you are sending the entire active catalog)soft_replace
Hourly / daily catalog dumpsoft_replace

Set the mode via the X-PSYKHE-PROCESSING-MODE header.

  • insert (default) - Upserts products present in the feed. Products not in the feed are left untouched. This is the safe default for webhooks and incremental batches because it never disables anything.
  • soft_replace - Upserts products present in the feed and marks every other active product in your catalog as inactive (excluded from recommendations; data is preserved, not hard-deleted). Only use this when the payload contains your complete catalog. Sending a partial catalog with soft_replace will disable every product you omitted.

Request body is newline-delimited JSON (JSONL), one product per line. Gzip compression is supported via Content-Encoding: gzip.

The ingestion is processed asynchronously - the response returns immediately with an ingestion_id you can poll via GET /ingestion/{id}.

Authorization

PrivateApiKeyHeader
X-PSYKHE-API-KEY<token>

Private API key (sk_...). Server-to-server only; never expose in client-side code.

In: header

Header Parameters

X-PSYKHE-PROCESSING-MODE?string

Controls how products absent from the feed are handled.

  • insert (default) - use for webhooks and incremental updates. Missing products are left unchanged.
  • soft_replace - use only for full catalog syncs. Products not present in the payload are marked inactive.
Default"insert"
Value in"insert" | "soft_replace"
Content-Encoding?string

Set to gzip if the request body is gzip-compressed.

Value in"gzip"

Request Body

application/x-ndjson

JSONL product feed. One JSON object per line, each with operation and product_data.

TypeScript Definitions

Use the request body type in TypeScript.

body*string

JSONL formatted product data, one product per line.

Response Body

application/json

application/json

application/json

curl -X POST "https://api.psykhe.dev/v1/ingestion/" \  -H "X-PSYKHE-PROCESSING-MODE: insert" \  -H "Content-Type: application/x-ndjson" \  -d '{"operation":"insert","product_data":{"product_identifier":"111F-UU1","variant_identifier":"111F-UU1","title":"Antioxidant Essence NAC Y2","images":["https://is4.revolveassets.com/images/p4/n/z/111F-UU1_V1.jpg"],"designer":"111Skin","categories":["Beauty: Skincare","Beauty: Skincare: Moisturizers: Moisturizers"],"sizes":["all"],"color":"","price":105.0}}  {"operation":"insert","product_data":{"product_identifier":"222F-AB2","variant_identifier":"222F-AB2","title":"Hydration Serum","images":["https://cdn.example.com/222F-AB2.jpg"],"designer":"AcmeBeauty","categories":["Beauty: Skincare: Serums"],"sizes":["all"],"color":"","price":58.0}}  '
{
  "errors": null,
  "meta": null,
  "data": {
    "ingestion_id": 477021
  }
}
{
  "errors": [
    {
      "code": "payload_empty",
      "message": "Payload is empty",
      "context": {
        "ingestion_id": 477021
      }
    }
  ],
  "meta": null,
  "data": null
}
Empty
{
  "errors": [
    {
      "code": "payload_too_large",
      "message": "Payload is too large",
      "context": {
        "ingestion_id": 477021
      }
    }
  ],
  "meta": null,
  "data": null
}