Skip to main content

Overview

Batch imports run asynchronously: the POST stages an import job and responds immediately with 202 and a jobId. Your system then polls the job (GET /api/v1/import-jobs/{jobId}) until the import finishes and collects the full result there — including the reasons for skipped or failed rows. The import runs through the same engine as the in-app CSV importer: same validation, same idempotent upsert, same result shape.
A POST with a single object (no rows array) stays synchronous and returns 201 unchanged. Only the batch form (array or envelope) is async.This flow applies to the endpoints with a 5,000-row cap — the three above and POST /api/v1/ppwr/quantities. The PPWR master-data endpoints and POST /api/v1/ppwr/tonnage (1,000-row cap; above it 400, not 413) are synchronous: 200 with the result in the body, no job is created. See PPWR.

Flow

  1. Send the batch — POST a bare array of rows or an envelope object:
    On success: 202 with { "jobId": "…", "status": "queued", "statusUrl": "/api/import-jobs/…" } (also sent as a Location header; statusUrl is host-relative — join it with the origin only). Nothing is imported at this point. Row validation runs synchronously — a bad payload is rejected with 400 right away and no job is staged. The first element of path in each details entry is the 0-based index of the failing row in rows, e.g. [17, "quantity"]. One invalid row rejects the whole request and nothing is staged — fix or drop that row and resend the batch.
  2. Poll — GET /api/v1/import-jobs/{jobId} every ~2 seconds, backing off for large jobs (e.g. exponentially up to ~30 s). completed and failed are terminal — stop polling then.
  3. Collect the result — once status is "completed", result holds the merged import result (imported, updated, failed, skipped, … per entity). Each entry in failed names its record (orderNumber, internalArticleNr or name) and carries error.key — a stable, non-localized key — and sometimes error.params. A failed job carries its reason in error instead (see the table below).

Limits

  • Max 5,000 rows per request. Above that: 413 { "error": "batchTooLarge", "maxBatchSize": 5000 } — nothing is staged. Page a larger sync across multiple requests.
  • ~4.5 MB request body (platform limit, enforced before our application runs).
  • For a large initial load, send batches of roughly 100 to 500 orders (each with all its lines, up to the 5,000-row cap) rather than one request per order: each request becomes one job, a company’s jobs run only a few at a time, and fewer jobs means fewer jobIds to poll.
  • An empty array or a malformed envelope → 400.

Partial accept and idempotency

  • Valid rows commit even when other rows land in failed.
  • A job writes in chunks. If it fails part-way (workerError, or lineReplacementRequired after a concurrent change), earlier chunks may already be written. Replaying the batch is safe.
  • Replaying an identical import is safe: matched rows come back as skipped/updated, no duplicates are created.
  • Article fields on order rows (hsCode, articleDescription) are create-only — see Orders.

Error keys (stable, non-localized)

A failed job’s error is a string: either a bare key, or JSON whose key is the stable part. Row and order keys appear as failed[].error.key in the result of a completed job. The order-specific keys (lineReplacementRequired, positionIdMixed, duplicatePositionId, conflictingHsCodeForArticle, conflictingDuplicateOrderRows) are explained in Orders. An order key fails the whole order; nothing of it is written.

Retention

The job result stays pollable; the submitted input is deleted ~24 hours after the job finishes, and the job record is deleted ~30 days after creation. Poll and persist the result on your side within that window.

Permissions

The POST requires the entity’s write permission (articles:write, suppliers:write, orders:write); polling requires the matching read permission (implied by write). Jobs are strictly tenant-bound: an unknown jobId and another company’s jobId return the same 404.

HS code on order rows with per-order article copies (hsCode)

This section moved to Orders.

Per-order origin via ERP

This section moved to Orders.

Positions (positionId)

This section moved to Orders.

Order status (status)

This section moved to Orders.

Orders: replaceLines and the completeness rule

This section moved to Orders.

Delta sync (changedSince)

This section moved to Orders.