Поръчан до 12:00 = изпраща се на следващия работен ден*

Dropshipping API

Integrate your store or ERP system with Tormino via our REST API. Place orders automatically, sync stock in real-time, and manage your wallet balance.

Authentication

All API requests require an API key (Bearer token). You receive this after your dropshipping application is approved. Add the key to every request header:

# Header format - Orders & Stock API
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

# Header format - Wallet API (Shopwaive)
X-Shopwaive-Access-Token: YOUR_SHOPWAIVE_API_KEY
X-Shopwaive-Platform: shopify
Content-Type: application/json
Base URL: https://api.tormino.com/dropship/v1
Wallet API: https://app.shopwaive.com/api
↑ Back to top

Placing orders

Submit a new order from your system directly to our warehouse. The order is only created if wallet balance and stock are sufficient.

POST /orders

Request

ParameterTypeRequiredDescription
referencestringYesYour internal order reference
shipping.namestringYesEnd-customer full name
shipping.address1stringYesStreet address
shipping.address2stringNoAdditional address line
shipping.citystringYesCity
shipping.zipstringYesPostal code
shipping.countrystringYesISO 3166-1 alpha-2 (NL, BE, DE, FR)
shipping.phonestringNoEnd-customer phone
shipping.emailstringNoEnd-customer email
items[].eanstringYesProduct EAN code
items[].skustringNoProduct SKU (fallback if EAN missing)
items[].quantityintegerYesQuantity to order
// POST /orders
{
  "reference": "ORD-2024-12345",
  "shipping": {
    "name": "Jan Jansen",
    "address1": "Kerkstraat 1",
    "address2": "",
    "city": "Amsterdam",
    "zip": "1011AB",
    "country": "NL",
    "phone": "+31612345678",
    "email": "jan@example.com"
  },
  "items": [
    { "ean": "8711234567890", "quantity": 2 }
  ]
}

Response 200 OK

{
  "order_id": "TORM-500123",
  "status": "confirmed",
  "wallet_balance_after": 425.50,
  "currency": "EUR",
  "estimated_ship_date": "2026-07-17"
}

Response 402 Payment Required

If wallet balance is insufficient, the order is rejected (HTTP 402). No order is created and no funds are deducted.
{
  "error": "insufficient_wallet_balance",
  "message": "Insufficient balance. Current: EUR 25.00. Required: EUR 74.50.",
  "wallet_balance": 25.00,
  "amount_required": 74.50,
  "top_up_url": "https://tormino.nl/pages/b2b-dashboard#wallet"
}

Response 409 Conflict

{
  "error": "insufficient_stock",
  "message": "Item 8711234567890 has insufficient stock.",
  "item_ean": "8711234567890",
  "requested": 2,
  "available": 1
}
Idempotency: Include an Idempotency-Key header to prevent duplicate orders on retries. Same key = no duplicate order created.
↑ Back to top

Stock synchronization

Check real-time availability of items before listing them in your store. Available as single or batch request.

Single item

GET /stock/{ean}
{
  "ean": "8711234567890",
  "sku": "TORM-12345",
  "available": true,
  "quantity": 47,
  "lead_time_days": 1
}

Batch (max 100 per request)

POST /stock/batch
// Request
{ "eans": ["8711234567890", "8711234567891"] }

// Response
{
  "items": [
    { "ean": "8711234567890", "available": true, "quantity": 47 },
    { "ean": "8711234567891", "available": false, "quantity": 0 }
  ]
}
↑ Back to top

Wallet management

Check your current balance and transaction history, or top up your wallet via iDeal.

Powered by Shopwaive — Wallet endpoints use the Shopwaive REST API with separate authentication.

Balance & transactions

GET /customer/{email}?activity=true
# Headers
X-Shopwaive-Access-Token: YOUR_SHOPWAIVE_API_KEY
X-Shopwaive-Platform: shopify
Content-Type: application/json

# Response
{
  "balance": 425.50,
  "activity": [
    {
      "id": "action_739281",
      "type": "deposit",
      "amount": 500.00,
      "note": "Top-up via iDeal",
      "date": "2026-07-15T14:00:00Z",
      "status": "active"
    },
    {
      "id": "action_739282",
      "type": "withdrawal",
      "amount": -74.50,
      "note": "Order TORM-500123",
      "date": "2026-07-16T10:30:00Z",
      "status": "active"
    }
  ]
}
Parameters: ?activity=true for transaction history, ?orders=true for linked Shopify orders.

Top up

Minimum top-up is EUR 100. Balance does not expire.
GET /products/wallet-opwaarderen
Top-ups go through the normal Shopify checkout. Purchase the wallet top-up product, choose the amount and pay with iDeal. After payment, the balance is automatically updated via Shopwaive + Shopify Flow.
// Programmatore top-up (via Shopwaive API)
PUT /customer
X-Shopwaive-Access-Token: YOUR_SHOPWAIVE_API_KEY
X-Shopwaive-Platform: shopify

{ "email": "partner@example.com", "amount": 500.00 }
↑ Back to top

Order overview

View your recent orders with fulfillment status and tracking number.

GET /orders?status=confirmed&limit=20
{
  "orders": [
    {
      "order_id": "TORM-500123",
      "reference": "ORD-2024-12345",
      "status": "fulfilled",
      "tracking_number": "3S1234567890",
      "tracking_carrier": "PostNL",
      "total": 74.50,
      "created_at": "2026-07-16T10:30:00Z",
      "shipped_at": "2026-07-17T09:15:00Z"
    }
  ]
}
Query parameters: status (confirmed, shipped, cancelled), limit (max 50), offset for pagination.
↑ Back to top

Webhooks

We send notifications to your configured webhook URL on order status changes. All webhooks are signed with HMAC-SHA256.

EventTriggerPayload
order.confirmedOrder created + wallet chargedorder_id, amount, wallet_balance_after
order.shippedFulfillment created in Shopifyorder_id, tracking_number, tracking_carrier
order.cancelledOrder cancelledorder_id, reason, refund_amount
wallet.low_balanceBalance drops below thresholdbalance, threshold, top_up_url

Webhook verification

// Verify HMAC-SHA256 signature
X-Tormino-Signature: sha256=hex_signature

// Pseudocode
expected = HMAC_SHA256(webhook_secret, raw_body)
if expected == received_signature:
    process_webhook(payload)
else:
    reject()
↑ Back to top

Error handling

The API uses standard HTTP status codes. Error responses contain a machine-readable error code and a description.

CodeErrorDescription
401unauthorizedMissing or invalid API key
402insufficient_wallet_balanceWallet balance too low for order
404product_not_foundEAN/SKU not found in catalog
409insufficient_stockRequested quantity exceeds available stock
422validation_errorMissing or invalid request fields
429rate_limit_exceededToo many requests (max 100/min)
500internal_errorServer error - retry with backoff
↑ Back to top

Code examples

Ready-to-use examples for placing an order in the most common programming languages.

$payload = [
    'reference' => 'ORD-2024-12345',
    'shipping' => [
        'name' => 'Jan Jansen',
        'address1' => 'Kerkstraat 1',
        'city' => 'Amsterdam',
        'zip' => '1011AB',
        'country' => 'NL',
    ],
    'items' => [
        ['ean' => '8711234567890', 'quantity' => 2],
    ],
];

$ch = curl_init('https://api.tormino.com/dropship/v1/orders');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_API_KEY',
    'Content-Type: application/json',
    'Idempotency-Key: ' . uniqid(),
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($httpCode === 200) {
    $result = json_decode($response, true);
    echo "Order confirmed: " . $result['order_id'];
} elseif ($httpCode === 402) {
    echo "Insufficient wallet balance";
}
curl_close($ch);
import requests
import uuid

payload = {
    "reference": "ORD-2024-12345",
    "shipping": {
        "name": "Jan Jansen",
        "address1": "Kerkstraat 1",
        "city": "Amsterdam",
        "zip": "1011AB",
        "country": "NL",
    },
    "items": [
        {"ean": "8711234567890", "quantity": 2},
    ],
}

headers = {
    "Authorization": "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
    "Idempotency-Key": str(uuid.uuid4()),
}

response = requests.post(
    "https://api.tormino.com/dropship/v1/orders",
    json=payload,
    headers=headers,
)

if response.status_code == 200:
    data = response.json()
    print(f"Order confirmed: {data['order_id']}")
elif response.status_code == 402:
    print("Insufficient wallet balance")
const response = await fetch('https://api.tormino.com/dropship/v1/orders', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    reference: 'ORD-2024-12345',
    shipping: {
      name: 'Jan Jansen',
      address1: 'Kerkstraat 1',
      city: 'Amsterdam',
      zip: '1011AB',
      country: 'NL',
    },
    items: [
      { ean: '8711234567890', quantity: 2 },
    ],
  }),
});

const data = await response.json();
if (response.ok) {
  console.log(`Order confirmed: ${data.order_id}`);
} else if (response.status === 402) {
  console.log('Insufficient wallet balance');
}
↑ Back to top

Rate limits & best practices

Maximum 100 requests per minute per API key. Use batch endpoints for stock checks to save requests.

LimitValue
Requests per minute100 per API key
Requests per hour1000 per API key
Batch stock checkMax 100 EANs per request
Orders per request1 (one order per API call)
Rate limit headersX-RateLimit-Remaining, X-RateLimit-Reset
Best practice: Sync stock via batch endpoint every 15-30 minutes. Use webhooks for order status updates instead of polling. Always include Idempotency-Key on order requests to safely retry on network errors.