Sales Transactions
Create Transactions
Section titled “Create 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.
/api/v1/transactionsQuery Parameters
Section titled “Query Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
store_name | string | Yes | Name 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_id | string | No | Deprecated — internal store identifier. Still accepted, but use store_name. Supply one or the other, not both. |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
data | array | Yes | Array of transaction line items (at least one). |
Transaction Line Item
Section titled “Transaction Line Item”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
sku | string | Yes | - | Exact SKU identifier the transaction applies to. |
warehouse | string | Yes | - | Warehouse name the transaction was fulfilled from. |
quantity | number | Yes | - | Number of units sold. Must be greater than 0. |
unit_price | number | Yes | - | Price per unit. Must be 0 or greater. |
timestamp | string | Yes | - | 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. |
currency | string | No | - | ISO 4217 currency code for unit_price (e.g. USD, EUR). |
order_id | string | No | - | External order identifier this line belongs to. |
channel | string | No | - | Sales channel the transaction came through. |
tags | string[] | 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_amount | number | No | 0 | Total discount applied to the line (absolute amount). |
Example Request
Section titled “Example Request”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 } ] }'import requests
response = requests.post( "https://api.prediko.io/api/v1/transactions", headers={"Authorization": "Bearer YOUR_API_KEY"}, params={"store_name": "Main Store"}, json={ "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, } ] },)result = response.json()const response = await fetch( "https://api.prediko.io/api/v1/transactions?store_name=Main%20Store", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ 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, }, ], }), });const result = await response.json();Response
Section titled “Response”Returns the import result.
{ "errors": []}Batch size
Section titled “Batch size”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.
Duplicates and retries
Section titled “Duplicates and retries”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.
Processing
Section titled “Processing”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.
Errors
Section titled “Errors”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:
| Condition | detail |
|---|---|
Neither store_name nor store_id supplied | store_name is required |
Both store_name and store_id supplied | Provide either store_name or store_id, not both |
store_name doesn’t match any store on your account | Unknown store_name '<name>'. Available stores: [...] |
store_name matches more than one store | store_name '<name>' matches more than one store; use store_id instead |
store_id doesn’t match any store on your account | Unknown 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.
Row validation (406 Not Acceptable)
Section titled “Row validation (406 Not Acceptable)”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://..."}