Upload a resumable full-run batch
const url = 'http://localhost:8080/v1/ingest/runs/example/batch';const options = { method: 'PUT', headers: { 'X-Antares-Environment-Fingerprint': 'example', 'Idempotency-Key': 'example', Authorization: 'Bearer <token>', 'Content-Type': 'application/json' }, body: '{"schemaVersion":1,"batchIndex":1,"defaultCurrency":"EUR","currencies":["EUR"],"priceContexts":[{"ctx":"c0193d1ae60d471e7d0712efda7de10115a","label":"example","customerGroupId":"example","ruleIds":["example"],"tierBoundaries":[1]}],"products":[{"id":"example","sku":"example","ean":"example","name":"example","description":"example","url":"example","images":[{"src":"example","thumb280":"example","thumb560":"example","alt":"example"}],"manufacturer":{"id":"example","name":"example","number":"example"},"categories":[{"id":"example","name":"example","path":"example"}],"prices":{"additionalProperty":{"gross":1,"net":1,"list":1}},"priceRange":{"additionalProperty":{"min":1,"max":1}},"purchasePrice":1,"priceContexts":{"additionalProperty":{"prices":{"additionalProperty":{"gross":1,"net":1,"list":1}},"priceRange":{"additionalProperty":{"min":1,"max":1}},"tiers":{"additionalProperty":[{"qty":1,"gross":1,"net":1}]}}},"hasTierPrices":true,"stock":{"available":1,"isCloseout":true},"properties":{"additionalProperty":["example"]},"technicalData":"example","searchKeywords":["example"],"variant":{"count":1,"axes":["example"],"skus":["example"],"eans":["example"]},"flags":{"active":true,"isNew":true,"topseller":true},"releasedAt":"2026-04-15T12:00:00Z","custom":{}}]}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request PUT \ --url http://localhost:8080/v1/ingest/runs/example/batch \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --header 'Idempotency-Key: example' \ --header 'X-Antares-Environment-Fingerprint: example' \ --data '{ "schemaVersion": 1, "batchIndex": 1, "defaultCurrency": "EUR", "currencies": [ "EUR" ], "priceContexts": [ { "ctx": "c0193d1ae60d471e7d0712efda7de10115a", "label": "example", "customerGroupId": "example", "ruleIds": [ "example" ], "tierBoundaries": [ 1 ] } ], "products": [ { "id": "example", "sku": "example", "ean": "example", "name": "example", "description": "example", "url": "example", "images": [ { "src": "example", "thumb280": "example", "thumb560": "example", "alt": "example" } ], "manufacturer": { "id": "example", "name": "example", "number": "example" }, "categories": [ { "id": "example", "name": "example", "path": "example" } ], "prices": { "additionalProperty": { "gross": 1, "net": 1, "list": 1 } }, "priceRange": { "additionalProperty": { "min": 1, "max": 1 } }, "purchasePrice": 1, "priceContexts": { "additionalProperty": { "prices": { "additionalProperty": { "gross": 1, "net": 1, "list": 1 } }, "priceRange": { "additionalProperty": { "min": 1, "max": 1 } }, "tiers": { "additionalProperty": [ { "qty": 1, "gross": 1, "net": 1 } ] } } }, "hasTierPrices": true, "stock": { "available": 1, "isCloseout": true }, "properties": { "additionalProperty": [ "example" ] }, "technicalData": "example", "searchKeywords": [ "example" ], "variant": { "count": 1, "axes": [ "example" ], "skus": [ "example" ], "eans": [ "example" ] }, "flags": { "active": true, "isNew": true, "topseller": true }, "releasedAt": "2026-04-15T12:00:00Z", "custom": {} } ] }'One ordered batch of a full run. batchIndex must be exactly lastBatchIndex + 1; a byte-identical replay of an already recorded batch is idempotent, anything else is batch_out_of_order.
Authorizations
Abschnitt betitelt „Authorizations“Parameters
Abschnitt betitelt „Parameters“Path Parameters
Abschnitt betitelt „Path Parameters“Index run identifier, a ULID with an ir_ prefix.
Header Parameters
Abschnitt betitelt „Header Parameters“SHA-256 over the shop’s APP_URL plus its platform version. APP_URL lives in .env and is not carried by a database dump, which is exactly why it is the discriminator between a production install and a clone of it.
Caller-generated key. Replaying the same key with the same body replays the stored response; replaying it with a different body is 409 idempotency_conflict.
Request Bodyrequired
Abschnitt betitelt „Request Bodyrequired“One ordered batch of an open run. channel and lang are omitted because the run already fixes them.
object
Strictly lastBatchIndex + 1. A byte-identical replay of an already recorded batch is idempotent; anything else is 409 batch_out_of_order.
Optional restatement of the run’s default currency. When present it must equal the value the run was opened with.
Optional restatement of the run’s currency assignment. When present it must equal the list the run was opened with.
Additional or restated price-context registry entries, upserted idempotently. This is how a connector registers a context it only discovers part-way through a catalogue without having to abort the run.
One entry of the channel’s price-context registry, carrying the structured input of the fingerprint rather than the canonical string. The API rebuilds the canonical string from these fields, hashes it, and compares the result to ctx; a mismatch fails the whole batch with price_context_fingerprint_mismatch. That comparison is the only mechanism that catches a PHP↔Go disagreement on the day it appears instead of six months later in a support ticket about wrong prices.
object
The fingerprint the connector computed. The literal default must not appear here: the default context is implicit and always exists.
Merchant-facing description of the situation, for example Händler B2B · Preisliste 2026. Panel only, never reaches the widget, and deliberately not part of the fingerprint so renaming a price list does not orphan every materialised price field.
The raw platform customer-group id, as the platform spells it. Omit it when the platform has no customer groups. The API normalises it (trim, ASCII-only case fold, remove every -) before hashing, so the dashed and undashed spellings of the same UUID are the same context.
The raw platform ids of the price rules that matched for this situation, in any order. The API normalises, deduplicates and sorts them, so the platform’s priority ordering — which is meaningless for identity — never reaches the fingerprint.
Quantity thresholds contributed by this context’s own quantity-conditioned price rules, which make the matching rule set itself quantity-dependent. The value 1 is dropped because quantity 1 is always implied. Not the per-product price ladder: that is per (product, rule), varies from product to product, and lives in PriceContext.tiers as a value inside the context.
The neutral product schema. The platform ends at this boundary; nothing downstream ever sees a Shopware concept. Server-derived fields (margin_pct, margin_norm, boost_conv, boost_sales, weighted_score, embedding, price_val_*, price_sort_*, pc_*) must never be sent and are rejected by additionalProperties: false.
object
Stable platform id. The document id in the index.
Plain text, already stripped of markup by the connector.
SEO path relative to the channel domain, per (channel, lang).
object
280 px thumbnail, used by the instant dropdown.
560 px thumbnail, used by the result grid.
object
Manufacturer part number. Indexed for infix matching.
object
Breadcrumb path, >-separated, used as the facet’s display value.
Default-context price per ISO currency. Every currency the sales channel assigns and the product actually prices is carried, so the storefront can switch currency without a reindex. A missing defaultCurrency entry rejects the product; a missing secondary currency simply drops the product from that currency’s results. A currency the channel does not assign is 422 currency_not_indexed.
object
object
Strike-through reference price. Omit when the product is not on sale.
The variant “from–to” gross span per currency. Every currency present in prices must have an entry, because the API indexes one facet range per priced currency and a price without its range would silently vanish from the price facet. For a product without variants min == max == gross.
object
object
Net purchase price in the shop’s house currency. The only margin input the connector supplies. Stored unindexed, used to derive marginNorm, and never present in any response to a public credential.
Materialised customer prices keyed by price-context fingerprint. The literal default is reserved and rejected here — the default prices are prices and priceRange.
Only contexts that deviate from the default are sent. A context whose prices equal the default in every currency must be omitted; this is the sizing rule the whole feature depends on, because in a normal catalogue almost every product costs the same for almost every context and omitting them is what keeps documents inside the per-tenant RAM budget. At most 64 contexts and at most 128 (context × currency) families per product, or the product is rejected with price_context_budget_exceeded.
The API returns at most the single context a request proves entitlement to, and never the map itself.
object
One context’s deviating prices for one product. It may cover a subset of the product’s currencies; a currency absent here simply has no context price and falls back to the default for that currency. Pricing a currency the product’s default context does not price rejects the product, because such a price has nothing to fall back to and would leak through the default field family.
object
Prices per upper-case ISO 4217 currency.
object
object
Strike-through reference price. Omit when the product is not on sale.
Defaults to the product’s own priceRange when omitted.
object
object
Quantity ladders per upper-case ISO currency, ascending by qty, at most 32 rungs. These are the per-product ladders and are values inside the context, never part of its identity.
object
Quantity-scaled price. qty is the lowest quantity at which the price applies.
object
Display hint for the widget’s tier_prices badge, derived by the connector from “any context carries tiers for this product”.
object
Units on hand. Thresholded into in_stock/low/out before serving.
The product cannot be ordered beyond the available quantity, so zero stock means unavailable rather than backorderable.
The shop’s own property axes, keyed by normalised name, for example {"farbe": ["blau"]}. Each becomes a properties.<name> facet.
object
Flattened technical specification text, indexed for infix matching.
Merchant-maintained additional search terms.
Parent-level strategy: one document per parent, carrying the identifiers and axes of all its variants so a variant SKU still finds the parent. Channels that need one document per variant set indexVariantsSeparately.
object
object
false is not a validation failure: the id is moved to the delete list of the same request and removed from the live and shadow collections.
Release date, the input to the novelty ranking signal.
Connector-specific passthrough. Indexed as-is and never interpreted.
object
Responses
Abschnitt betitelt „Responses“Resumable batch stored in the shadow collection.
The connector’s view of a run, keyed by channel key rather than by UUID.
object
created — the shadow collection exists and no batch has landed. indexing — at least one batch was accepted. committed — sanity checks passed and the alias was swapped. aborted — the run failed or was superseded; the previous collection stayed live.
Documents written to the shadow collection so far.
What the connector announced when it opened the run, if it did.
Index of the last accepted batch, -1 before the first. A connector resuming after a crash sends lastBatchIndex + 1.
Products refused by validation during this run. The detail is in GET /v1/admin/index-runs/{runId}/errors.
Documents embedded so far. Only moves after the alias swap, because embedding is the second phase.
Whether the collection’s vectors are complete, so a hybrid query will hit populated vectors rather than silently return an empty vector side. Flipped by the embedding worker, per collection.
The embedding model the collection was built with. Absent until the first embedding pass. A model change requires a rebuild, because the dimension is part of the Typesense collection schema.
Version of the text template the embeddings were produced from.
Vector dimension of the collection.
Failure detail for an aborted run. Absent otherwise.
Example
{ "status": "created"}contract_validation_failed — the OpenAPI request validator rejected the request before the handler ran — or invalid_request when the body is unreadable or is not valid JSON for the declared operation.
RFC 9457 problem details. type is always https://api.antares.commergy.de/problems/{code} and is built in exactly one place. type, code and title are stable and never localised; only detail is localised, and only where the registry marks the audience as merchant.
object
Absolute problem type URI. Stable identifier, never localised.
Stable English summary of the problem type.
The HTTP status code, repeated in the body.
Human-readable explanation of this occurrence. The only localised field; may be German or English depending on Accept-Language.
The request path this occurrence relates to.
The registry code, for example price_context_token_expired. This is what a client branches on; never branch on title or detail.
Present only for per-item validation failures, above all ingest batches.
object
Dotted path of the offending field inside the request body, for example prices.CHF.gross.
What is wrong with it, in English.
0-based index into the request’s products array. Present only for ingest batches, and the only way to map an error back to a product without echoing it.
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "code": "example", "errors": [ { "field": "example", "message": "example", "productIndex": 1 } ]}unauthorized — the credential is missing, malformed, unknown or revoked. For /v1/admin this also covers an actor JWS that fails signature, exp or kid verification, and a kid that no key in the panel’s JWKS matches.
RFC 9457 problem details. type is always https://api.antares.commergy.de/problems/{code} and is built in exactly one place. type, code and title are stable and never localised; only detail is localised, and only where the registry marks the audience as merchant.
object
Absolute problem type URI. Stable identifier, never localised.
Stable English summary of the problem type.
The HTTP status code, repeated in the body.
Human-readable explanation of this occurrence. The only localised field; may be German or English depending on Accept-Language.
The request path this occurrence relates to.
The registry code, for example price_context_token_expired. This is what a client branches on; never branch on title or detail.
Present only for per-item validation failures, above all ingest batches.
object
Dotted path of the offending field inside the request body, for example prices.CHF.gross.
What is wrong with it, in English.
0-based index into the request’s products array. Present only for ingest batches, and the only way to map an error back to a product without echoing it.
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "code": "example", "errors": [ { "field": "example", "message": "example", "productIndex": 1 } ]}index_run_not_found — unknown runId, or the run belongs to another shop.
RFC 9457 problem details. type is always https://api.antares.commergy.de/problems/{code} and is built in exactly one place. type, code and title are stable and never localised; only detail is localised, and only where the registry marks the audience as merchant.
object
Absolute problem type URI. Stable identifier, never localised.
Stable English summary of the problem type.
The HTTP status code, repeated in the body.
Human-readable explanation of this occurrence. The only localised field; may be German or English depending on Accept-Language.
The request path this occurrence relates to.
The registry code, for example price_context_token_expired. This is what a client branches on; never branch on title or detail.
Present only for per-item validation failures, above all ingest batches.
object
Dotted path of the offending field inside the request body, for example prices.CHF.gross.
What is wrong with it, in English.
0-based index into the request’s products array. Present only for ingest batches, and the only way to map an error back to a product without echoing it.
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "code": "example", "errors": [ { "field": "example", "message": "example", "productIndex": 1 } ]}One of index_run_active (a full run is already open for this channel and language), index_run_closed (batch or commit against a committed or aborted run), batch_out_of_order (batchIndex is not lastBatchIndex + 1 and not an exact replay), batch_conflict (the batch could not be recorded idempotently), idempotency_conflict (the same Idempotency-Key with a different body) or environment_mismatch.
RFC 9457 problem details. type is always https://api.antares.commergy.de/problems/{code} and is built in exactly one place. type, code and title are stable and never localised; only detail is localised, and only where the registry marks the audience as merchant.
object
Absolute problem type URI. Stable identifier, never localised.
Stable English summary of the problem type.
The HTTP status code, repeated in the body.
Human-readable explanation of this occurrence. The only localised field; may be German or English depending on Accept-Language.
The request path this occurrence relates to.
The registry code, for example price_context_token_expired. This is what a client branches on; never branch on title or detail.
Present only for per-item validation failures, above all ingest batches.
object
Dotted path of the offending field inside the request body, for example prices.CHF.gross.
What is wrong with it, in English.
0-based index into the request’s products array. Present only for ingest batches, and the only way to map an error back to a product without echoing it.
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "code": "example", "errors": [ { "field": "example", "message": "example", "productIndex": 1 } ]}request_too_large — the body exceeds this endpoint’s byte limit. Also returned for chunked bodies without Content-Length once the limit is passed mid-stream.
RFC 9457 problem details. type is always https://api.antares.commergy.de/problems/{code} and is built in exactly one place. type, code and title are stable and never localised; only detail is localised, and only where the registry marks the audience as merchant.
object
Absolute problem type URI. Stable identifier, never localised.
Stable English summary of the problem type.
The HTTP status code, repeated in the body.
Human-readable explanation of this occurrence. The only localised field; may be German or English depending on Accept-Language.
The request path this occurrence relates to.
The registry code, for example price_context_token_expired. This is what a client branches on; never branch on title or detail.
Present only for per-item validation failures, above all ingest batches.
object
Dotted path of the offending field inside the request body, for example prices.CHF.gross.
What is wrong with it, in English.
0-based index into the request’s products array. Present only for ingest batches, and the only way to map an error back to a product without echoing it.
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "code": "example", "errors": [ { "field": "example", "message": "example", "productIndex": 1 } ]}One of validation_failed (envelope-level failure, or one or more products failed; errors[] carries productIndex and no partial batch is committed), channel_not_found (the (channel, lang) pair is not mapped for this shop), currency_not_indexed (a product prices a currency the channel does not assign), price_context_fingerprint_mismatch (the API recomputed the fingerprint from the registry entry’s structured input and got a different value — the PHP↔Go drift alarm, which fails the whole batch), price_context_budget_exceeded (more than 64 contexts or more than 128 context × currency families on one product) or document_count_sanity_failed (a commit would drop the live document count below 50 % of the previous run; the old collection stays live).
RFC 9457 problem details. type is always https://api.antares.commergy.de/problems/{code} and is built in exactly one place. type, code and title are stable and never localised; only detail is localised, and only where the registry marks the audience as merchant.
object
Absolute problem type URI. Stable identifier, never localised.
Stable English summary of the problem type.
The HTTP status code, repeated in the body.
Human-readable explanation of this occurrence. The only localised field; may be German or English depending on Accept-Language.
The request path this occurrence relates to.
The registry code, for example price_context_token_expired. This is what a client branches on; never branch on title or detail.
Present only for per-item validation failures, above all ingest batches.
object
Dotted path of the offending field inside the request body, for example prices.CHF.gross.
What is wrong with it, in English.
0-based index into the request’s products array. Present only for ingest batches, and the only way to map an error back to a product without echoing it.
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "code": "example", "errors": [ { "field": "example", "message": "example", "productIndex": 1 } ]}One of rate_limit_exceeded, authentication_admission_full (the pre-auth admission guard for large connector bodies), connector_admission_full (this sk already occupies its single large-body slot) or request_admission_full (global large-body validation slots exhausted). All three admission codes are retryable after a short backoff.
RFC 9457 problem details. type is always https://api.antares.commergy.de/problems/{code} and is built in exactly one place. type, code and title are stable and never localised; only detail is localised, and only where the registry marks the audience as merchant.
object
Absolute problem type URI. Stable identifier, never localised.
Stable English summary of the problem type.
The HTTP status code, repeated in the body.
Human-readable explanation of this occurrence. The only localised field; may be German or English depending on Accept-Language.
The request path this occurrence relates to.
The registry code, for example price_context_token_expired. This is what a client branches on; never branch on title or detail.
Present only for per-item validation failures, above all ingest batches.
object
Dotted path of the offending field inside the request body, for example prices.CHF.gross.
What is wrong with it, in English.
0-based index into the request’s products array. Present only for ingest batches, and the only way to map an error back to a product without echoing it.
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "code": "example", "errors": [ { "field": "example", "message": "example", "productIndex": 1 } ]}Headers
Abschnitt betitelt „Headers“Seconds to wait before retrying.
internal_error — unhandled failure. detail is always generic; the cause goes to the log with a correlation id.
RFC 9457 problem details. type is always https://api.antares.commergy.de/problems/{code} and is built in exactly one place. type, code and title are stable and never localised; only detail is localised, and only where the registry marks the audience as merchant.
object
Absolute problem type URI. Stable identifier, never localised.
Stable English summary of the problem type.
The HTTP status code, repeated in the body.
Human-readable explanation of this occurrence. The only localised field; may be German or English depending on Accept-Language.
The request path this occurrence relates to.
The registry code, for example price_context_token_expired. This is what a client branches on; never branch on title or detail.
Present only for per-item validation failures, above all ingest batches.
object
Dotted path of the offending field inside the request body, for example prices.CHF.gross.
What is wrong with it, in English.
0-based index into the request’s products array. Present only for ingest batches, and the only way to map an error back to a product without echoing it.
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "code": "example", "errors": [ { "field": "example", "message": "example", "productIndex": 1 } ]}search_backend_unavailable — Typesense failed an ingest, alias, synonym or collection operation.
RFC 9457 problem details. type is always https://api.antares.commergy.de/problems/{code} and is built in exactly one place. type, code and title are stable and never localised; only detail is localised, and only where the registry marks the audience as merchant.
object
Absolute problem type URI. Stable identifier, never localised.
Stable English summary of the problem type.
The HTTP status code, repeated in the body.
Human-readable explanation of this occurrence. The only localised field; may be German or English depending on Accept-Language.
The request path this occurrence relates to.
The registry code, for example price_context_token_expired. This is what a client branches on; never branch on title or detail.
Present only for per-item validation failures, above all ingest batches.
object
Dotted path of the offending field inside the request body, for example prices.CHF.gross.
What is wrong with it, in English.
0-based index into the request’s products array. Present only for ingest batches, and the only way to map an error back to a product without echoing it.
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "code": "example", "errors": [ { "field": "example", "message": "example", "productIndex": 1 } ]}One of idempotency_unavailable (the idempotency advisory lock could not be taken), index_lock_unavailable (the per-channel advisory lock could not be taken), analytics_unavailable (ClickHouse rejected a synchronous, business-critical write) or service_unavailable. All are retryable.
RFC 9457 problem details. type is always https://api.antares.commergy.de/problems/{code} and is built in exactly one place. type, code and title are stable and never localised; only detail is localised, and only where the registry marks the audience as merchant.
object
Absolute problem type URI. Stable identifier, never localised.
Stable English summary of the problem type.
The HTTP status code, repeated in the body.
Human-readable explanation of this occurrence. The only localised field; may be German or English depending on Accept-Language.
The request path this occurrence relates to.
The registry code, for example price_context_token_expired. This is what a client branches on; never branch on title or detail.
Present only for per-item validation failures, above all ingest batches.
object
Dotted path of the offending field inside the request body, for example prices.CHF.gross.
What is wrong with it, in English.
0-based index into the request’s products array. Present only for ingest batches, and the only way to map an error back to a product without echoing it.
Examplegenerated
{ "type": "example", "title": "example", "status": 1, "detail": "example", "instance": "example", "code": "example", "errors": [ { "field": "example", "message": "example", "productIndex": 1 } ]}