KOT API - Kot w Butach Dropshipping API (1.0.0-draft.5)

Download OpenAPI specification:Download

Product feed and dropshipping orders for Kot w Butach partners.

DRAFT - subject to change before v1.0.0 is released.

KOT API lets dropshipping partners:

  1. read our product feed (prices, stock, product and parcel dimensions, images),
  2. place orders that we ship directly to your customers as single parcels,
  3. supply your own courier label (PDF) - we print it and pack the parcel with it,
  4. track order status until the parcel is packed and ready for the carrier.

Typical flow

  1. GET /products - import products into your shop (refresh at most every 15 minutes).
  2. Your customer orders in your shop.
  3. Create the courier label on your carrier account using package from the feed.
  4. POST /orders with the recipient, items and the label (or send the label later with PUT /orders/{order_id}/label).
  5. Poll GET /orders?updated_since=... (or receive webhooks, planned) until the status is shipped.
  6. Book the carrier pickup for the parcel (see Carrier pickup).

Authentication

Every request needs an API key in the Authorization header:

Authorization: Bearer kot_live_xxxxxxxxxxxxxxxxxxxxxxxx
  • kot_live_... keys work on real orders.
  • kot_test_... keys (sandbox) use the same URL and the same validation, but orders are never processed or packed. Sandbox orders move through statuses automatically so you can test your integration.
  • The product feed can also be read with a read-only feed token in the URL (?feed_token=...) for tools that cannot send headers. A feed token cannot place orders.

Keys are issued by Kot w Butach. Keep them secret; ask us to rotate a key if it leaks.

SKU

sku is our stable product code: uppercase letters A-Z, digits and hyphens only (e.g. KWB-CAT-GRASS), safe to use in URLs and CSV files as is. A SKU never changes once published in the feed.

Some products come in variants (e.g. fabric patterns of a cat lounger). Every variant has its own sku (e.g. KWB-LOUNGER-STASIU-V12) - order the variant SKU. The product SKU of such a product (KWB-LOUNGER-STASIU) groups its variants and cannot be ordered itself (variant_required).

Idempotency

external_id (your order number) is unique per partner. Sending the same order again returns the existing order (200) instead of creating a duplicate. Sending a different payload with an external_id that already exists returns 409 duplicate_external_id.

Order statuses

Status Meaning
received Accepted by the API, being processed (usually seconds to minutes).
confirmed Stock reserved, warehouse document created. Parcel will be packed once a label is present.
shipped Packed with your label and ready for carrier pickup. Final status.
rejected Order could not be processed - see rejection.code. Final status.
cancelled Cancelled by you before packing. Final status.

received -> confirmed -> shipped; received -> rejected; received | confirmed -> cancelled.

Dispatch cutoff

Orders received with a label by 13:00 Polish time (Europe/Warsaw) on a business day are packed the same day. Later orders, and orders whose label arrives after 13:00, are packed on the next business day. Business days = Monday to Friday, except public holidays in Poland.

Carrier pickup

You book the carrier pickup for every parcel on your carrier account, once the order status is shipped (packed and ready). The pickup address is returned by GET /me (pickup_address).

Destination countries

Any country. You buy the courier label on your own carrier account, so the destination and the shipping cost are yours to decide.

Courier labels

  • Format: PDF, one page, 100 x 150 mm (4 x 6 in), max 2 MB, base64 encoded in JSON.
  • One parcel per order (single unit or several items packed together in one parcel).
  • Use package from the feed (the parcel of one unit), not dimensions (the product itself). Some products ship flat-packed in a box smaller than the assembled product. For orders with more than one item, sum the weights and contact us for combined packing rules.
  • An order without a label stays in confirmed and is not packed until the label arrives.

Field length limits

Address fields are stored in our ERP with fixed lengths (see schemas). Values that are too long are rejected with 422 validation_error (we never cut your customer's address silently).

Rate limits

60 requests per minute per API key. Above the limit the API returns 429 with a Retry-After header. Every response carries X-Request-Id - quote it when contacting us.

Money

Amounts are decimal strings with two decimal places ("10.08"), prices are net (VAT not included) in the currency agreed with you (price.currency).

Versioning

The version is part of the URL (/v1). New optional fields may be added to responses at any time - ignore unknown fields. Breaking changes only in a new version (/v2).

Account

Check your API key.

Check the API key

Returns the partner and key type. Use it to test your connection.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "partner_id": 3,
  • "partner_name": "Example Pets GmbH",
  • "mode": "test",
  • "currency": "EUR",
  • "pickup_address": {
    }
}

Products

Product feed.

Product feed (JSON)

All products available to your account. The catalogue is small, so the whole feed is returned in one response. Use updated_since to fetch only changed products.

Authorizations:
bearerAuthfeedToken
query Parameters
updated_since
string <date-time>

Only items changed at or after this moment (ISO 8601, e.g. 2026-11-02T08:00:00Z).

Responses

Response samples

Content type
application/json
{
  • "generated_at": "2026-11-02T08:00:00Z",
  • "data": [
    ]
}

Product feed (CSV)

The same feed as CSV (UTF-8, comma separated, header row). One row per orderable item - products with variants have one row per variant (variant sku, product SKU in parent_sku). Columns: sku, parent_sku, variant_name, ean, name, description, price_net, currency, availability, quantity, weight_g, length_cm, width_cm, height_cm, package_weight_g, package_length_cm, package_width_cm, package_height_cm, country_of_origin, cn_code, image_1, image_2, image_3, updated_at. availability = in_stock / low_stock / out_of_stock / discontinued; quantity is always empty (exact quantities are not reported).

Authorizations:
bearerAuthfeedToken
query Parameters
updated_since
string <date-time>

Only items changed at or after this moment (ISO 8601, e.g. 2026-11-02T08:00:00Z).

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

Product feed (XML)

The same feed as XML (UTF-8). One <product> element per orderable item, with child elements named like the CSV columns (<sku>, <parent_sku>, ..., <updated_at>); empty values are empty elements.

<?xml version="1.0" encoding="UTF-8"?>
<products generated_at="2026-11-02T08:00:00Z">
  <product>
    <sku>KWB-CAT-GRASS</sku>
    <parent_sku/>
    <name>Organic Cat Grass</name>
    <price_net>3.00</price_net>
    <currency>EUR</currency>
    ...
  </product>
</products>
Authorizations:
bearerAuthfeedToken
query Parameters
updated_since
string <date-time>

Only items changed at or after this moment (ISO 8601, e.g. 2026-11-02T08:00:00Z).

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

One product

Authorizations:
bearerAuth
path Parameters
sku
required
string

Product SKU or variant SKU (a variant SKU returns its product with all variants).

Responses

Response samples

Content type
application/json
{
  • "sku": "string",
  • "ean": "string",
  • "name": "string",
  • "description": "string",
  • "images": [],
  • "price": {
    },
  • "availability": {
    },
  • "dimensions": {
    },
  • "package": {
    },
  • "country_of_origin": "string",
  • "cn_code": "string",
  • "variants": [
    ],
  • "updated_at": "2019-08-24T14:15:22Z"
}

Orders

Dropshipping orders.

Create an order

Creates a dropshipping order. The response is returned right after validation with status received; stock reservation happens asynchronously (poll the order or wait for the webhook).

Authorizations:
bearerAuth
Request Body schema: application/json
required
external_id
required
string <= 64 characters

Your unique order id (e.g. Shopify order id). Idempotency key.

reference
string or null <= 64 characters

Human readable order number (e.g. Shopify #1042).

required
object (Recipient)
required
Array of objects (OrderItemCreate) [ 1 .. 20 ] items
Shipping (object) or null

Courier label. May be omitted and sent later with PUT /orders/{order_id}/label.

note
string or null <= 250 characters

Note for our warehouse (not printed for the recipient).

Responses

Request samples

Content type
application/json
{
  • "external_id": "5873219443001",
  • "reference": "#1042",
  • "recipient": {
    },
  • "items": [
    ],
  • "shipping": {},
  • "note": null
}

Response samples

Content type
application/json
{
  • "id": "ord_8K2M4Q",
  • "external_id": "string",
  • "reference": "string",
  • "status": "received",
  • "mode": "live",
  • "recipient": {
    },
  • "items": [
    ],
  • "total": {
    },
  • "label_received": true,
  • "carrier": "string",
  • "tracking_number": "string",
  • "tracking_url": "string",
  • "rejection": {
    },
  • "note": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "confirmed_at": "2019-08-24T14:15:22Z",
  • "shipped_at": "2019-08-24T14:15:22Z",
  • "cancelled_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

List orders

Your orders, newest change first. For synchronisation poll with updated_since set to the updated_at of the last order you processed.

Authorizations:
bearerAuth
query Parameters
updated_since
string <date-time>

Only items changed at or after this moment (ISO 8601, e.g. 2026-11-02T08:00:00Z).

status
string (OrderStatus)
Enum: "received" "confirmed" "shipped" "rejected" "cancelled"
external_id
string

Find an order by your order number.

limit
integer [ 1 .. 100 ]
Default: 50
cursor
string

Value of next_cursor from the previous page.

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "next_cursor": "string"
}

Get an order

Authorizations:
bearerAuth
path Parameters
order_id
required
string
Example: ord_8K2M4Q

Order id returned by POST /orders (id).

Responses

Response samples

Content type
application/json
{
  • "id": "ord_8K2M4Q",
  • "external_id": "5873219443001",
  • "reference": "#1042",
  • "status": "shipped",
  • "mode": "live",
  • "recipient": {
    },
  • "items": [
    ],
  • "total": {
    },
  • "label_received": true,
  • "carrier": "DHL",
  • "tracking_number": "00340434161234567890",
  • "rejection": null,
  • "note": null,
  • "created_at": "2026-11-02T09:15:00Z",
  • "confirmed_at": "2026-11-02T09:16:10Z",
  • "shipped_at": "2026-11-02T11:40:00Z",
  • "cancelled_at": null,
  • "updated_at": "2026-11-02T11:40:00Z"
}

Add or replace the courier label

Allowed while the order is received or confirmed. After shipped returns 409 order_locked.

Authorizations:
bearerAuth
path Parameters
order_id
required
string
Example: ord_8K2M4Q

Order id returned by POST /orders (id).

Request Body schema: application/json
required
carrier
required
string <= 30 characters

Carrier name as printed on the label, e.g. DHL, UPS, DPD, GLS.

tracking_number
required
string <= 64 characters
tracking_url
string or null <uri> <= 500 characters
label_pdf_base64
required
string <base64> <application/pdf>

One-page PDF, 100 x 150 mm, max 2 MB before encoding.

Responses

Request samples

Content type
application/json
{
  • "carrier": "string",
  • "tracking_number": "string",
  • "tracking_url": "http://example.com",
  • "label_pdf_base64": "string"
}

Response samples

Content type
application/json
{
  • "id": "ord_8K2M4Q",
  • "external_id": "string",
  • "reference": "string",
  • "status": "received",
  • "mode": "live",
  • "recipient": {
    },
  • "items": [
    ],
  • "total": {
    },
  • "label_received": true,
  • "carrier": "string",
  • "tracking_number": "string",
  • "tracking_url": "string",
  • "rejection": {
    },
  • "note": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "confirmed_at": "2019-08-24T14:15:22Z",
  • "shipped_at": "2019-08-24T14:15:22Z",
  • "cancelled_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Cancel an order

Allowed while the order is received or confirmed. After shipped returns 409 order_locked.

Authorizations:
bearerAuth
path Parameters
order_id
required
string
Example: ord_8K2M4Q

Order id returned by POST /orders (id).

Request Body schema: application/json
optional
reason
string <= 250 characters

Responses

Request samples

Content type
application/json
{
  • "reason": "string"
}

Response samples

Content type
application/json
{
  • "id": "ord_8K2M4Q",
  • "external_id": "string",
  • "reference": "string",
  • "status": "received",
  • "mode": "live",
  • "recipient": {
    },
  • "items": [
    ],
  • "total": {
    },
  • "label_received": true,
  • "carrier": "string",
  • "tracking_number": "string",
  • "tracking_url": "string",
  • "rejection": {
    },
  • "note": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "confirmed_at": "2019-08-24T14:15:22Z",
  • "shipped_at": "2019-08-24T14:15:22Z",
  • "cancelled_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Order status changed (PLANNED - not available in v1.0) Webhook

Sent to the URL registered for your account whenever an order changes status. Body = the full order. Signature: header X-Kot-Signature: sha256=<hex> = HMAC-SHA256 of the raw body with your webhook secret. Respond 2xx within 10 s; failed deliveries are retried with backoff for 24 h. Polling GET /orders always works as a fallback.

Authorizations:
bearerAuth
Request Body schema: application/json
id
required
string
external_id
required
string
reference
string or null
status
required
string (OrderStatus)
Enum: "received" "confirmed" "shipped" "rejected" "cancelled"
mode
required
string
Enum: "live" "test"
required
object (Recipient)
required
Array of objects (OrderItem)
required
object (Money)
label_received
required
boolean

false = we are waiting for your label; the order will not be packed.

carrier
string or null
tracking_number
string or null
tracking_url
string or null
Rejection (object) or null
note
string or null
created_at
required
string <date-time>
confirmed_at
string or null <date-time>
shipped_at
string or null <date-time>

When the parcel was packed with your label.

cancelled_at
string or null <date-time>
updated_at
required
string <date-time>

Responses

Request samples

Content type
application/json
{
  • "id": "ord_8K2M4Q",
  • "external_id": "string",
  • "reference": "string",
  • "status": "received",
  • "mode": "live",
  • "recipient": {
    },
  • "items": [
    ],
  • "total": {
    },
  • "label_received": true,
  • "carrier": "string",
  • "tracking_number": "string",
  • "tracking_url": "string",
  • "rejection": {
    },
  • "note": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "confirmed_at": "2019-08-24T14:15:22Z",
  • "shipped_at": "2019-08-24T14:15:22Z",
  • "cancelled_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}