Getting Started
Every request to protected endpoints requires a valid JWT token in the header. Below is a three-step quick start.
POST /api/token/ with your username and password. You'll receive an access token (valid for 5 min) and a refresh token.Authorization: Bearer <access_token>POST /api/token/refresh/ with the refresh field to get a new access token without logging in again.JWT Authorization
The API uses JWT (JSON Web Token). Pass the token in the Authorization header with every request.
POST /api/token/ Content-Type: application/json { "username": "your_username", "password": "your_password" }
{ "access": "eyJhbGci...", // <-- use this one, expires after 5 min "refresh": "eyJhbGci..." // <-- for refreshing, valid for 1 day }
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
access token expires after 5 minutes. Refresh it via POST /api/token/refresh/ with the body {"refresh": "..."}.Products — Overview
The API provides several product endpoints optimized for different use cases: full data, your prices, stock levels, and updates.
indexid)Full Product List
The /api/products-index/ endpoint returns active products with all related data nested in a single response.
GET /api/products-index/ // list of all GET /api/products-index/ABC-123/ // single product by index Authorization: Bearer <token>
What a single product contains:
| Field | Type | Description |
|---|---|---|
index | string | Unique product code (URL key) |
quantity | integer | Current stock level |
active | boolean | Whether the product is active |
names[] | array | Names by language: name_pl, name_en, name_de, name_fr, name_cz |
descriptions[] | array | Multi-language descriptions |
prices[] | array | Prices: pln, eur, chf, czk |
madeof[] | array | Material composition (multi-language) |
image[] | array | Product image URLs |
additional[] | array | Additional attributes (key–value) |
future_delivery[] | array | Planned deliveries: date + quantity |
marking_data[] | array | Marking data (nested: places, options) |
category / subcategory | integer | Category and subcategory ID |
new_product, promotion, sale | boolean | Product status flags |
Products with Your Prices
The /api/client-products/ endpoint returns the same data as /api/products-index/ — all fields, filters and search — extended with the catalogue price and your purchase price. Handy when building an offer or a cart and you need product data together with the price in a single request.
GET /api/client-products/AP800001-01/?qty=100 Authorization: Bearer <token> // Response: { "id": 142, "index": "AP800001-01", "quantity": 4520, ... // all fields as in products-index "currency": "PLN", "catalogue_price": "10.00", // net catalogue price "purchase_price": "7.90", // your net purchase price "price_source": "individual", "no_discount": true, "discount_percent": "10.00" }
Price fields (in addition to products-index):
| Field | Type | Description |
|---|---|---|
currency | string | Your account currency (PLN / EUR / CHF / CZK). |
catalogue_price | decimal | null | Net catalogue unit price, before discount. |
purchase_price | decimal | null | Your net unit purchase price — what you pay in an order. |
price_source | string | base / discount / individual / promo — where the purchase price comes from. |
no_discount | boolean | true — individual or promotional price, no discount applied. |
discount_percent | decimal | Your discount for this product (0 when the product is not discounted). |
?qty= equal to the quantity you order. Details and price tiers: Your prices.Stock Levels
A lightweight endpoint returning only the index and quantity — ideal for frequent stock synchronization.
GET /api/stock-info/ Authorization: Bearer <token> // Response: { "count": 1250, "next": "/api/stock-info/?page=2", "previous": null, "results": [ { "index": "ABC-123", "quantity": 450 }, { "index": "XYZ-007", "quantity": 0 }, ... ] }
/api/last-update/ endpoint returns products changed in the last 45 minutes — use it for incremental synchronization instead of fetching the entire catalogue.Filtering and Search
All product endpoints support filtering via GET parameters and pagination (100 results per page).
Parameters for /api/products-index/:
// Products from category ID=5 with a discount GET /api/products-index/?category=5&discount_prices=true // New arrivals - page 2 GET /api/products-index/?new_product=true&page=2 // Search by partial index GET /api/products-index/?search=ABC // Categories and subcategories GET /api/categories/ GET /api/subcategories/
Product Response Structure
Full JSON structure of a single product from /api/products-index/{index}/.
{ "id": 142, "index": "ABC-123", "quantity": 450, "active": true, "new_product": false, "promotion": false, "sale": false, "discount_prices": true, "category": 5, "subcategory": 12, "names": [ { "id": 1, "language": "name_pl", "title": "Kubek termiczny 350ml" }, { "id": 2, "language": "name_en", "title": "Thermal mug 350ml" } ], "prices": [ { "pln": "24.90", "eur": "5.80", "chf": "5.50", "czk": "138.00" } ], "image": [ { "url": "https://cdn.example.com/products/abc-123-1.jpg" }, { "url": "https://cdn.example.com/products/abc-123-2.jpg" } ], "future_delivery": [ { "quantity": 200, "date": "2026-03-15" } ], "additional": [ { "item": "dimensions", "value": "8.5 x 8.5 x 12 cm" }, { "item": "weight", "value": "210g" } ], "marking_data": [ /* see the Marking section */ ], // ... names, descriptions, madeof, marking (text) }
Marking — Data Architecture
Marking data has a hierarchical structure. Each product can have multiple marking places, and each place can have several options with different prices.
One product → multiple places (e.g. “Front”, “Back”) → multiple options (e.g. “Screen printing”, “Engraving”) → the price depends on the ordered quantity.
Key concepts:
| Object | Description | Key Fields |
|---|---|---|
MarkingData | The main marking node for a product. Contains the default method. | product, default_marking_code |
MarkingPlace | A place on the product where a marking can be applied. | code, name_pl |
MarkingOption | A specific marking technique at a given place. | marking_code, option_code, price_code, max_colors |
MarkingVariant | A variant of the option (e.g. marking area size). | variant_code, variant_label |
MarkingPriceCode | Price key — links the option to price tiers. | code |
PriceCode | Price tier for a given ordered quantity. | from_qty, to_qty, price_pln |
AdditionalService | Additional marking services (e.g. setup, artwork shipping). | service_id, service_price_pln |
Marking Endpoints
Two access modes: nested (full hierarchy in a single request) or flat (solo — each object separately, lighter responses).
Nested mode:
product__index)Flat mode (solo — for detailed queries):
?search=<index>?marking_data=ID?marking_place=ID?marking_data=ID// Step 1 — find the product ID GET /api/products-index/ABC-123/ // → the response contains "id": 142 and nested "marking_data" // Step 2 — or directly via marking-data GET /api/marking-data/?product=142 // Response (nested): { "id": 18, "product": 142, "default_marking_code": "TS-F1", "marking_place": [ { "id": 55, "code": "F1", "name_pl": "Przód", "marking_option": [ { "id": 201, "marking_code": "TS-F1", "option_code": "TS", // technique code "price_code": 7, // ID → /api/marking-price/ "max_colors": 4, "realisation_time_id": 3, "marking_variant": [] } ] } ], "additional_service": [ { "service_id": "SETUP", "service_price_pln": "35.00" } ] }
Marking Price Tiers
The price per marked unit depends on the ordered quantity. Fetch the tiers via the price_code from the marking option.
GET /api/marking-price/?code=P3 // Response: { "id": 7, "code": "P3", "main_marking_price": [ { "from_qty": 25, "to_qty": 49, "price_pln": "2.80" }, { "from_qty": 50, "to_qty": 99, "price_pln": "2.20" }, { "from_qty": 100, "to_qty": 249, "price_pln": "1.60" }, { "from_qty": 250, "to_qty": 499, "price_pln": "1.10" } ] }
Typical Marking Integration Flow
How to build a marking configurator step by step.
GET /api/products-index/ABC-123/ — the response includes nested marking_data with places and options.marking_place[] choose a place (e.g. “Front”), from marking_option[] choose a technique (e.g. “Screen printing”). Note the price_code and marking_code.GET /api/marking-price/?code=P3 (or /api/marking-tierprice/?marking_option=ID) — display the price tiers for the customer's quantity.GET /api/marking-additional/?marking_data=ID — add setup costs, artwork shipping, etc.POST /api/orders/ with items — including marking (marking_place_code, marking_option_code, marking_colors). See Order with Marking./api/orders/ support marking — you pass the place and technique codes in the order items and upload design files separately.Your prices
The /api/my-prices/ endpoint returns the prices that apply to your account — in your currency and for the given quantity. These are the same prices applied when you place an order.
| source | Meaning |
|---|---|
| base | Catalogue price (product without discount or 0% discount). |
| discount | Catalogue price after your percentage discount. |
| individual | Your individual price — final price, no discount. |
| promo | Promotional price — final price, no discount. |
products-index + your prices/api/client-products/ — see Products with Your Prices.GET /api/my-prices/AP800001-01/?qty=100 Authorization: Bearer <token> // Response: { "index": "AP800001-01", "currency": "PLN", "qty": 100, "price": "7.90", // applicable net unit price "source": "individual", // base | discount | individual | promo "no_discount": true, // final price "base_price": "10.00", "discount_percent": "10.00", "discounted_price": "9.00", "individual_price": "7.90", "promo_price": null, "individual_tiers": [ { "from_qty": 1, "to_qty": 99, "price": "8.40" }, { "from_qty": 100, "to_qty": null, "price": "7.90" } ], "promo_tiers": [] }
Response fields:
| Field | Type | Description |
|---|---|---|
price | decimal | null | Net unit price you will pay. null — no price for this product. |
source | string | Where the price comes from (table above). |
no_discount | boolean | true — individual or promotional price, no discount applied. |
base_price / discounted_price | decimal | null | Catalogue price before and after your discount. |
individual_price / promo_price | decimal | null | Individual / promotional price for the given quantity, if any. |
individual_tiers[] / promo_tiers[] | array | Quantity tiers (only /api/my-prices/{index}/). |
?special=1 returns only products with an individual price or an ongoing promotion. Tiered prices depend on quantity — pass ?qty= equal to the quantity you intend to order.Promotional prices
The /api/promo-prices/ endpoint returns promotional product prices. Promotions are addressed by product index — just like /api/products-index/. Prices are tiered: each tier defines a quantity range and a unit price in PLN/EUR/CHF/CZK.
"no_discount": true — do not subtract your percentage discount from this price.GET /api/promo-prices/ // all current promotions GET /api/promo-prices/ABC-123/ // one promotion by index GET /api/promo-prices/?index=ABC-123,XYZ-007 Authorization: Bearer <token> // Response: { "index": "ABC-123", "active": true, "date_from": "2026-10-01", "date_to": "2026-12-31", "is_current": true, "no_discount": true, // no discount on this price "tiers": [ { "from_qty": 1, "to_qty": 99, "qty_label": "1–99", "pln": "10.00", "eur": "2.40", "chf": null, "czk": null }, { "from_qty": 100, "to_qty": null, "qty_label": "100+", "pln": "8.50", "eur": "2.05", "chf": null, "czk": null } ], "updated_at": "2026-10-01T10:15:00+02:00" }
Promotion fields:
| Field | Type | Description |
|---|---|---|
index | string | Product index (URL key) |
active | boolean | Whether the promotion is enabled |
date_from | date | null | Start date; null = effective immediately |
date_to | date | null | End date (inclusive); null = no end date |
is_current | boolean | Whether the promotion is valid today |
no_discount | boolean | Always true — no discount on promotional prices |
tiers[] | array | Price tiers, sorted ascending by from_qty |
tiers[].from_qty / to_qty | integer | null | Quantity range; to_qty: null = “and more” |
tiers[].qty_label | string | Readable range label, e.g. "1–99", "100+" |
tiers[].pln / eur / chf / czk | decimal | null | Unit price in currency; null = no price in this currency |
updated_at | datetime | Last modification date |
404. A single price “from 1 pc” is one tier 1+. Promotions are automatically included in your purchase price — see Your prices.Promotional price for quantity
Instead of looking up the tier yourself, you can ask for the unit price for a specific quantity and currency.
GET /api/promo-prices/ABC-123/price/?qty=150¤cy=PLN // Response: { "index": "ABC-123", "qty": 150, "currency": "PLN", "unit_price": "8.50", "total": "1275.00", "tier": "100+", "no_discount": true }
404 — the regular product price (with customer discount) applies.Profile & Permissions
Before placing your first order, check your profile. Key fields are set by your account manager — without them the order will be blocked.
{ "username": "company_abc", "email": "office@company.com", "can_order": true, // false = POST /api/orders/ will return 403 "discount_percent": 10.00, // % discount on products with discount_prices=true "currency": "PLN", // currency used in orders "payment_method": "banktransfer", // payment method used in orders "language": "pl" }
| Field | Description | Set By |
|---|---|---|
can_order | When false — the API rejects POST /api/orders/ with a 403 error. | Account manager |
discount_percent | Percentage discount applied automatically to products with discount_prices=true. Does not apply to individual and promotional prices. 0 = no discount. | Account manager |
currency | Currency used for all orders on this account. Determines which price column is used (pln / eur / chf / czk). | Account manager |
payment_method | Payment method for orders. Defaults to banktransfer. | Account manager |
Create an Order
Just provide a list of products. Currency and payment method are automatically retrieved from the logged-in user's profile. The order is passed on for processing immediately.
GET /api/me/. The following must be set:• can_order =
true — permission to place orders• currency and payment_method — retrieved from the profile, no need to provide them in the order
• discount_percent — discount on products with
discount_prices=true; final prices are calculated automatically
/api/client-products/ or /api/my-prices/ (for the same quantity). The applied price is returned in items[].unit_price.POST /api/orders/ Authorization: Bearer <access_token> Content-Type: application/json { // Your own order number (optional) "client_reference": "MOJ-NR-2024/001", "notes": "Please issue a VAT invoice", // optional // List of products — at least one item "items": [ { "product_index": "ABC-123", "quantity": 50 }, { "product_index": "XYZ-007", "quantity": 10 } ] } // Currency and payment method come from the user's profile — no need to provide them.
{ "id": 42, "client_reference": "MOJ-NR-2024/001", // ... orderer and address fields ... "items": [ { "id": 99, "product_index": "ABC-123", "quantity": 50, "unit_price": "8.90", "price_source": "discount", "marking_place_code": "", "marking_option_code": "", "design_files": [] }, { "id": 100, "product_index": "XYZ-007", "quantity": 10, "unit_price": "4.20", "price_source": "individual", "marking_place_code": "", "marking_option_code": "", "design_files": [] } ], "attachments": [], "created_at": "2026-06-25T10:35:00.123Z", // Processing status (filled in automatically) "status": "processing", // new | processing | confirmed | rejected "erp_order_number": "A/2026/0000042", // order number in our system "erp_error": "" // empty = success }
{ "items": [ { "product_index": ["Product with index 'XYZ' does not exist or is inactive."] } ], "billing_city": ["This field is required."] }
{ "detail": "Authentication credentials were not provided." }
Order Fields
Request fields (input):
| Field | Type | Description | |
|---|---|---|---|
items[] | array | List of order items. At least one required. | required |
items[].product_index | string | Product index. Must exist and be active. | required |
items[].quantity | integer | Quantity ≥ 1. | required |
items[].marking_place_code | string | Marking place code, e.g. M02. Must exist for the product. | optional |
items[].marking_option_code | string | Marking technique code, e.g. C1, S1. | optional |
items[].marking_colors | integer | Number of colors. Required when max_colors > 0. | conditional |
items[].marking_info | string | Marking description / instructions. | optional |
client_reference | string | Your own order number — it will appear on documents. | optional |
notes | string | Order notes. | optional |
Fields retrieved automatically from the user's profile:
| Field | Description |
|---|---|
currency | Order currency: PLN / EUR / CHF / CZK. Set by your account manager in the profile. |
payment_method | Payment method: banktransfer / prepayment / cod. Defaults to banktransfer. |
discount_percent | Profile discount applied automatically to products with discount_prices=true. Does not apply to individual and promotional prices. |
Response fields (read-only):
| Field | Description |
|---|---|
status | new — received | processing — in progress | confirmed — confirmed | rejected — rejected |
erp_order_number | Order number assigned in our system, e.g. A/2026/0000042. |
erp_error | Error message when accepting the order. Empty = success. |
items[].unit_price | Net unit price applied in the order (the lowest applicable). |
items[].price_source | Price source: base / discount / individual / promo. |
attachments[] | General order attachments. |
GET /api/stock-info/. Profile and permissions: GET /api/me/.Order with Marking
Each order item can include marking. You provide the place and technique code from the product data returned by the API. The system verifies that such marking exists for the product before accepting the order.
GET /api/marking-data/?product={id} or from the nested marking_data[] in the product response. Look for the code (place) and option_code (technique) fields.Item fields with marking:
| Field | Type | Description | |
|---|---|---|---|
marking_place_code | string | Marking place code, e.g. M02. Must exist for the product. | optional |
marking_option_code | string | Marking technique code, e.g. C1, S1, F1. Must exist at the given place. | optional |
marking_colors | integer | Number of colors. Required when the technique has max_colors > 0. | conditional |
marking_info | string | Marking description / instructions for the designer. | optional |
design_files[] | array | List of design files for this item (read-only). Files are uploaded via a separate request. | read-only |
POST /api/orders/ Authorization: Bearer <token> Content-Type: application/json { // ... orderer and address fields ... "items": [ { "product_index": "20820-14", "quantity": 100, "marking_place_code": "M02", // Back "marking_option_code":"C1", // DTF 60x50mm "marking_info": "Company logo, design attached" }, { "product_index": "20820-14", "quantity": 50, "marking_place_code": "M02", "marking_option_code":"S1", // Screen printing 300x250mm "marking_colors": 2, // max_colors=3 "marking_info": "2-color PMS logo" } ] }
Uploading a design file for an item:
# Step 1 — place the order, note the order id and item id # In the response items[0].id = 99 (item id) # Step 2 — send the design file curl -X POST https://developers.bluecollection.eu/api/orders/42/items/99/design \ -H "Authorization: Bearer $TOKEN" \ -F "file=@/path/to/logo.pdf" // Response 201: { "id": 7, "file": "/media/order_item_designs/42/logo.pdf", "uploaded_at": "2026-06-25T11:30:00.000Z" }
Attachments
You can attach files (PDF, PNG, XLSX, etc.) to every order. Attachments are passed on for processing together with the order.
POST /api/orders/42/attachments/ Authorization: Bearer <token> Content-Type: multipart/form-data // Form field: file // cURL: curl -X POST https://developers.bluecollection.eu/api/orders/42/attachments/ \ -H "Authorization: Bearer $TOKEN" \ -F "file=@/path/to/template.pdf" // Response 201: { "id": 1, "file": "/media/order_attachments/2026/06/wzor.pdf", "uploaded_at": "2026-06-25T11:00:00.000Z" }
Order Status
Every order is automatically passed on for processing right after it's created. The status field shows what stage the order is at.
Order received, waiting to be passed on for processing.
The order is being processed. The erp_order_number field contains its number.
Order confirmed.
Order rejected — see erp_error or contact your account manager.
Order List
Fetch and search placed orders. The status filter lets you track their progress.
GET /api/orders/?status=new Authorization: Bearer <token>
Response Codes
GET request completed successfully.
Resource (order) created successfully.
Validation error — check the response body.
Missing or invalid JWT token.
No permission to access the resource.
Resource does not exist (e.g. incorrect index).
Method not available for the endpoint (e.g. writing to a read-only endpoint).
Rate limit exceeded: 600 requests/min when logged in, 60/min anonymous. Wait and retry.
Code Examples
# 1. Get token TOKEN=$(curl -s -X POST https://developers.bluecollection.eu/api/token/ \ -H "Content-Type: application/json" \ -d '{"username":"username","password":"password"}' \ | python3 -c "import sys,json; print(json.load(sys.stdin)['access'])") # 2. List of products (paginated) curl https://developers.bluecollection.eu/api/products/ \ -H "Authorization: Bearer $TOKEN" # 3. Product details by index curl https://developers.bluecollection.eu/api/products-index/ABC-123/ \ -H "Authorization: Bearer $TOKEN" # 4. Stock levels curl https://developers.bluecollection.eu/api/stock-info/ \ -H "Authorization: Bearer $TOKEN" # 5. Product with your prices (100 pcs) curl "https://developers.bluecollection.eu/api/client-products/ABC-123/?qty=100" \ -H "Authorization: Bearer $TOKEN"
import requests BASE = "https://developers.bluecollection.eu" # 1. Token token = requests.post(f"{BASE}/api/token/", json={ "username": "username", "password": "password" }).json()["access"] headers = {"Authorization": f"Bearer {token}"} # 2. List of products (first page) products = requests.get(f"{BASE}/api/products/", headers=headers).json() print(f"Total products: {products['count']}") # 3. Product details by index product = requests.get(f"{BASE}/api/products-index/ABC-123/", headers=headers).json() print(f"Stock: {product['quantity']} pcs.") # 4. Product marking marking = requests.get(f"{BASE}/api/marking-data/?product={product['id']}", headers=headers).json() print(marking) # 5. Product with your prices (100 pcs) priced = requests.get(f"{BASE}/api/client-products/ABC-123/", params={"qty": 100}, headers=headers).json() print(f"Catalogue price: {priced['catalogue_price']}, Your price: {priced['purchase_price']} {priced['currency']}")
const BASE = "https://developers.bluecollection.eu"; const post = (url, body, tok) => fetch(BASE + url, { method: "POST", headers: { "Content-Type": "application/json", ...(tok && { "Authorization": `Bearer ${tok}` }) }, body: JSON.stringify(body) }).then(r => r.json()); // 1. Token const { access } = await post("/api/token/", { username: "username", password: "password" }); // 2. List of products const get = (url) => fetch(BASE + url, { headers: { "Authorization": `Bearer ${access}` } }).then(r => r.json()); const products = await get("/api/products/"); console.log(`Total products: ${products.count}`); // 3. Product details by index const product = await get("/api/products-index/ABC-123/"); console.log(`Stock: ${product.quantity} pcs.`); // 4. Product marking const marking = await get(`/api/marking-data/?product=${product.id}`); console.log(marking); // 5. Product with your prices (100 pcs) const priced = await get("/api/client-products/ABC-123/?qty=100"); console.log(`Catalogue price: ${priced.catalogue_price}, Your price: ${priced.purchase_price} ${priced.currency}`);
<?php $base = "https://developers.bluecollection.eu"; function apiPost($url, $data, $token = null) { $ch = curl_init($url); $headers = ["Content-Type: application/json"]; if ($token) $headers[] = "Authorization: Bearer $token"; curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => json_encode($data)]); return json_decode(curl_exec($ch)); } // 1. Token $auth = apiPost("$base/api/token/", ["username"=>"username","password"=>"password"]); $token = $auth->access; // 2. List of products $ch = curl_init("$base/api/products/"); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"]]); $products = json_decode(curl_exec($ch)); echo "Total products: {$products->count}\n"; // 3. Product details by index $ch = curl_init("$base/api/products-index/ABC-123/"); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"]]); $product = json_decode(curl_exec($ch)); echo "Stock: {$product->quantity} pcs.\n"; print_r($product); // 5. Product with your prices (100 pcs) $ch = curl_init("$base/api/client-products/ABC-123/?qty=100"); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"]]); $priced = json_decode(curl_exec($ch)); echo "Your price: {$priced->purchase_price} {$priced->currency}\n";
developers.bluecollection.eu. Obtain login credentials from your account manager.