Skip to content

Sales Transactions

Import sales transactions (e.g. wholesale sales) into Prediko so they feed into your demand and inventory pipelines.

The tenant is resolved automatically from your API key — you don’t need to specify it. The referenced SKUs and warehouses are validated and resolved by Prediko before the transactions are ingested.

POST/api/v1/transactions
ParameterTypeRequiredDescription
store_namestringYesName of the store the transactions belong to, exactly as it appears in Prediko. Matching ignores case and surrounding whitespace; URL-encode names containing spaces.
store_idstringNoDeprecated — internal store identifier. Still accepted, but use store_name. Supply one or the other, not both.
FieldTypeRequiredDescription
dataarrayYesArray of transaction line items (at least one).
FieldTypeRequiredDefaultDescription
skustringYes-Exact SKU identifier the transaction applies to.
warehousestringYes-Warehouse name the transaction was fulfilled from.
quantitynumberYes-Number of units sold. Must be greater than 0.
unit_pricenumberYes-Price per unit. Must be 0 or greater.
timestampstringYes-When the transaction occurred, as an ISO 8601 datetime (e.g. 2025-01-15T10:30:00Z). Include an offset such as Z or +05:30 to be explicit; a value without one is treated as UTC.
currencystringNo-ISO 4217 currency code for unit_price (e.g. USD, EUR).
order_idstringNo-External order identifier this line belongs to.
channelstringNo-Sales channel the transaction came through.
tagsstring[]No-Optional labels to attach to the transaction. Individual tags must not contain commas — tags are stored as a comma-separated list, so a comma inside a tag splits it in two.
discount_amountnumberNo0Total discount applied to the line (absolute amount).
Terminal window
curl -X POST "https://api.prediko.io/api/v1/transactions?store_name=Main%20Store" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": [
{
"sku": "SKU_ABC",
"warehouse": "Main Warehouse",
"quantity": 10,
"unit_price": 19.99,
"timestamp": "2025-01-15T10:30:00Z",
"currency": "USD",
"order_id": "WS-1001",
"channel": "wholesale",
"tags": ["b2b", "priority"],
"discount_amount": 0
}
]
}'

Returns the import result.

{
"errors": []
}

There is no fixed cap on the number of line items in data (the minimum is one). The whole batch is validated and resolved within the request, so very large payloads are bounded in practice by the request timeout rather than by a row count. Split bulk backfills into batches of a few thousand rows and send them sequentially — each batch is validated independently, so a failure only forces you to resend that batch.

This endpoint is not idempotent. There is no idempotency key, and re-sending a payload submits those lines again rather than replacing the previous submission. Only retry a request that failed — if you receive a success response, treat the batch as submitted even if your client times out afterwards.

Supply an order_id on each line where you can. Lines are identified by order_id plus sku, and without one the identifier is derived from sku, warehouse and timestamp, so a stable order_id keeps a given sale identifiable across systems.

A success response means the batch was accepted — validation and SKU/warehouse resolution have completed, but ingestion finishes asynchronously. Transactions may take a short time to appear in Prediko and to be reflected in demand and inventory figures. The response carries no job handle, so there is nothing to poll: verify in Prediko rather than re-sending the batch.

Store resolution (422 Unprocessable Entity)

Section titled “Store resolution (422 Unprocessable Entity)”

The store is resolved before any rows are read, so these errors mean nothing was imported:

Conditiondetail
Neither store_name nor store_id suppliedstore_name is required
Both store_name and store_id suppliedProvide either store_name or store_id, not both
store_name doesn’t match any store on your accountUnknown store_name '<name>'. Available stores: [...]
store_name matches more than one storestore_name '<name>' matches more than one store; use store_id instead
store_id doesn’t match any store on your accountUnknown store_id. Available stores: [...]

The Unknown store_name / Unknown store_id responses list the store names available on your account, so you can correct the value without a separate lookup:

{
"detail": "Unknown store_name 'Main Stor'. Available stores: ['Main Store', 'EU Store']"
}

Only stores that are active on your account can be matched — a removed store is never resolvable.

If row validation fails, the endpoint returns the per-row errors reported by Prediko:

{
"errors": [
{ "row": 1, "column": "sku", "reason": "RESOURCE_NOT_FOUND" }
]
}

For larger imports the response may also carry an errors_file_url — a downloadable file with the full error report, useful when there are too many failing rows to work through inline:

{
"errors": [
{ "row": 1, "column": "sku", "reason": "RESOURCE_NOT_FOUND" }
],
"errors_file_url": "https://..."
}