1 month free dropshipping trial — then €39/month · Wholesale min €500
cosmeticwholesale

Cosmetic Wholesale

Partner Order API

Dropshipping-Bestellungen aus Ihrem eigenen System anlegen und auslesen.

Dieses Dokument ersetzt das bisherige Turor-OMS-Paket (POST /restApi/v1/dropShippingOrder, GET /restApi/v1/dropShippingOrderItem/{id}). Diese Endpunkte und ihre HTTP-Basic-Zugangsdaten sind veraltet und dürfen nicht mehr verwendet werden. Die feldweise Zuordnung finden Sie in Anhang A am Ende dieser Seite.


Benötigen Sie diese API?

Die meisten Partner nicht. Wenn Sie WooCommerce oder Shopify einsetzen, verwenden Sie stattdessen unser Plugin — siehe WooCommerce oder Shopify. Entwickeln Sie nur dann gegen diese API, wenn Sie ein individuelles oder eigenes System betreiben.

Der Katalogimport sowie das Aktuellhalten von Beständen und Preisen wird separat in Catalog feed beschrieben.

Eine PDF-Fassung dieser Seite steht Ihren Entwicklern zur Verfügung: Partner Order API v1.0 (PDF).

1. Umgebungen

UmgebungBasis-URL
Produktionhttps://cosmeticwholesale.eu/api/v1
Staging (Integrationstests)https://cosmeticwholesale1.bookgurusapi.com/api/v1

Alle Beispiele in diesem Dokument verwenden die Basis-URL der Produktionsumgebung. Tokens werden nicht zwischen Umgebungen geteilt — fragen Sie Ihren Account Manager nach einem Staging-Token, wenn Sie zuerst testen möchten.


2. Authentifizierung

Jede Anfrage führt Ihr Konto-Token als Bearer-Token mit:

Authorization: Bearer YOUR_ACCOUNT_TOKEN

Es ist dasselbe Token, das Sie für den Produktkatalog-Feed verwenden (GET /api/v1/feeds/catalog.csv). Sie finden es auf der Seite Account Ihres Cosmetic Wholesale Logins, zusammen mit Ihrer Feed-URL.

Hinweise:

  • Das Token identifiziert Ihr Konto. Geben Sie es niemals in clientseitigem Code oder einem öffentlichen Repository preis.
  • Tokens können jederzeit über Ihr Konto widerrufen und neu ausgestellt werden. Ein widerrufenes Token liefert 401.
  • Es werden kein Benutzername und kein Passwort verwendet. Die alten HTTP-Basic-Zugangsdaten sind für die Bestellübermittlung nicht mehr gültig.
  • X-Feed-Token: YOUR_ACCOUNT_TOKEN wird als alternativer Header akzeptiert.

3. Voraussetzungen

Eine Bestellung wird nur angenommen, wenn alle folgenden Bedingungen erfüllt sind:

  1. Ihr Konto ist freigeschaltet und im Kanal dropshipping.
  2. Ihr Dropshipping-Abonnement ist aktiv — andernfalls liefert jede Anfrage 403 subscription_required.
  3. Ihr Wallet verfügt über ausreichendes Guthaben für die Bestellung — andernfalls wird die Bestellung angelegt und zurückgehalten (402, siehe Abschnitt 6).
  4. Jede ean in der Bestellung existiert in Ihrem Katalog-Feed und ist auf Lager.

Produkttitel, Bilder und Preise senden Sie nicht — sie stammen aus unserem Katalog. Sie senden lediglich die EAN und die Menge.


4. Eine Bestellung anlegen

POST /dropshipping/orders

Request-Header

Authorization: Bearer YOUR_ACCOUNT_TOKEN
Content-Type: application/json

Request-Body

{
  "externalRef": "ORD123456",
  "payment": "WALLET",
  "shipping": {
    "firstName": "Name",
    "lastName": "LastName",
    "company": "TestCompany",
    "address1": "Main Street 123",
    "address2": "",
    "postcode": "1072 HM",
    "city": "Amsterdam",
    "country": "NL",
    "phone": "",
    "email": "customer@example.com"
  },
  "lines": [
    { "ean": "6291107455365", "quantity": 2 },
    { "ean": "3607348816552", "quantity": 3 }
  ]
}

Felder

FeldTypPflichtHinweise
externalRefstringjaIhre eigene Bestellnummer. Muss innerhalb Ihres Kontos eindeutig sein. Dies ist der Idempotenzschlüssel — siehe Abschnitt 5.
paymentstringneinNur "WALLET" wird unterstützt. Standardwert ist "WALLET".
lines[].eanstringjaEAN, wie in Ihrem Katalog-Feed veröffentlicht.
lines[].quantityintegerjaMuss ≥ 1 sein.
shipping.firstNamestringempfohlen
shipping.lastNamestringempfohlen
shipping.address1stringja
shipping.address2stringnein
shipping.postcodestringja
shipping.citystringja
shipping.countrystringjaISO 3166-1 alpha-2, z. B. NL, BE, DE. Muss ein Land sein, in das wir liefern.
shipping.companystringnein
shipping.statestringnein
shipping.phonestringneinEmpfohlen — manche Zusteller setzen sie voraus.
shipping.emailstringneinWird, sofern unterstützt, für Benachrichtigungen des Zustellers verwendet.

Aus Kompatibilitätsgründen werden snake_case-Aliase akzeptiert: external_ref, first_name, last_name, address_1, address_2 sowie qty für quantity.

Response

{
  "id": "8f1c0b2e-6a3d-4f21-9b7e-0c5a4d2e1f33",
  "externalRef": "ORD123456",
  "omsReference": "ds:usr_2f9a1c:ORD123456",
  "createdVia": "dropshipping-api",
  "status": "PAID",
  "walletDebited": true,
  "omsPushStatus": "pending",
  "omsOrderItemIds": [],
  "tracking": []
}
FeldBedeutung
idCosmetic Wholesale Bestell-ID (UUID). Speichern Sie diese.
externalRefEcho Ihrer Bestellnummer.
omsReferenceInterne Lagerreferenz, ds:{accountId}:{externalRef}.
statusSiehe Abschnitt 7.
walletDebitedtrue, sobald die Bestellung aus Ihrem Wallet bezahlt wurde.
omsPushStatusFortschritt der Übergabe an unser Lager: pending, pushed, retry, failed, skipped oder null.
omsOrderItemIdsLager-Positions-IDs, werden nach Abschluss der Übergabe gefüllt. In der ersten Response leer — fragen Sie per GET nach.
trackingLeer, bis das Paket versendet wird. Siehe Abschnitt 8.

HTTP-Statuscodes

CodeBedeutungWas Ihr System tun sollte
201Bestellung angelegt und aus dem Wallet bezahlt.Als gesendet markieren.
200Diese externalRef existiert bereits. Der Body enthält den aktuellen Stand dieser Bestellung.Nichts — dies ist ein sicherer erneuter Versuch.
402Bestellung angelegt, aber zurückgehalten: Das Wallet-Guthaben reicht nicht aus.Nicht stornieren. Siehe Abschnitt 6.
400Validierungsfehler.Payload korrigieren; nicht unverändert erneut senden.
401Token fehlt, ist ungültig oder widerrufen.Zugangsdaten prüfen.
403Kein aktives Dropshipping-Abonnement.Kontaktieren Sie uns.
404(nur GET) Bestellung in Ihrem Konto nicht gefunden.
409Unzureichender Bestand für eine oder mehrere Positionen.Später erneut versuchen oder die Menge reduzieren.

5. Idempotenz

externalRef ist der Idempotenzschlüssel. Dieselbe externalRef zweimal zu senden, legt niemals eine zweite Bestellung an und belastet Ihr Wallet niemals doppelt:

  • War der erste Versuch erfolgreich, liefert der zweite 200 mit der bestehenden Bestellung.
  • Wurde der erste Versuch wegen unzureichenden Guthabens zurückgehalten, wiederholt der zweite Versuch die Wallet-Zahlung und liefert 201/200, sobald das Guthaben ausreicht.

Dadurch kann der Endpunkt bei Timeouts und Netzwerkfehlern gefahrlos erneut aufgerufen werden. Wiederholen Sie stets mit derselben externalRef — erzeugen Sie niemals eine neue für dieselbe Kundenbestellung.


6. Unzureichendes Guthaben: der 402-HOLD-Kontrakt

Dieses Verhalten hat in der alten OMS-API keine Entsprechung und ist der häufigste Integrationsfehler.

Wenn Ihr Wallet die Bestellung nicht decken kann:

HTTP/1.1 402 Payment Required
{
  "id": "8f1c0b2e-6a3d-4f21-9b7e-0c5a4d2e1f33",
  "externalRef": "ORD123456",
  "status": "HOLD",
  "reason": "INSUFFICIENT_WALLET",
  "walletDebited": false,
  "omsPushStatus": null,
  "omsOrderItemIds": [],
  "tracking": []
}

Was das bedeutet:

  • Die Bestellung existiert in unserem System und ist reserviert. Sie wird weder abgelehnt noch gelöscht.
  • Es wurde nichts belastet.
  • Es wurde noch nichts an das Lager übermittelt.

Was Ihr System tun muss:

  • Stornieren oder löschen Sie die Bestellung nicht auf Ihrer Seite. Belassen Sie sie in einem offenen bzw. in Bearbeitung befindlichen Status.
  • Laden Sie Ihr Wallet auf.
  • Senden Sie dieselbe externalRef erneut per POST, oder warten Sie einfach ab: Zurückgehaltene Bestellungen werden automatisch erneut versucht, sobald das Guthaben ausreicht.
  • Fragen Sie per GET nach, bis walletDebited den Wert true hat und status nicht mehr HOLD ist.

Eine Stornierung bei 402 ist genau das, was den Abgleich zerstört — die Bestellung bleibt bei uns reserviert, während Ihr System sie für fehlgeschlagen hält.


7. Eine Bestellung auslesen

GET /dropshipping/orders/{id}
GET /dropshipping/orders?externalRef=ORD123456

{id} akzeptiert entweder die Cosmetic Wholesale id (UUID) oder Ihre eigene externalRef. Beide Formen liefern denselben Body wie der Anlage-Aufruf. Bestellungen sind an Ihr Token gebunden — Sie können nur Ihre eigenen Bestellungen auslesen.

GET https://cosmeticwholesale.eu/api/v1/dropshipping/orders?externalRef=ORD123456
Authorization: Bearer YOUR_ACCOUNT_TOKEN

Werte des Bestellstatus

statusBedeutung
HOLDWegen unzureichenden Wallet-Guthabens zurückgehalten (siehe Abschnitt 6).
PENDING_PAYMENTWallet-Zahlung läuft.
ON_HOLDWartet auf Versand-/Umsatzsteuerverarbeitung.
PAIDAus dem Wallet bezahlt.
PROCESSINGWird im Lager vorbereitet.
SHIPPEDAn den Zusteller übergeben — tracking ist gefüllt.
COMPLETEDZugestellt / abgeschlossen.
CANCELLED, REFUNDEDEndzustände.

8. Sendungsverfolgung

Die Sendungsverfolgung wird über denselben GET als Array zurückgegeben (eine Bestellung kann in mehreren Paketen versendet werden):

{
  "id": "8f1c0b2e-6a3d-4f21-9b7e-0c5a4d2e1f33",
  "externalRef": "ORD123456",
  "status": "SHIPPED",
  "walletDebited": true,
  "omsPushStatus": "pushed",
  "omsOrderItemIds": ["418224", "418225", "418226"],
  "tracking": [
    {
      "provider": "postnl-3s",
      "trackingNumber": "3SXXXX00000000",
      "dateShipped": "2026-05-06T17:39:20.000Z"
    }
  ]
}

Fragen Sie höchstens einmal pro Stunde und Bestellung ab. Fragen Sie keine Bestellungen ab, die bereits COMPLETED, CANCELLED oder REFUNDED sind.


9. Fehlercodes

Fehler werden als { "error": "<message>", "code": "<code>" } zurückgegeben.

codeHTTPUrsache
external_ref_required400externalRef fehlt oder ist leer.
lines_required400lines fehlt oder ist leer.
ean_required400Eine Position hat keine ean.
invalid_qty400quantity ist keine positive Ganzzahl.
unknown_ean400Die EAN ist nicht in Ihrem Katalog enthalten.
forbidden_ean400Die EAN kann über diesen Endpunkt nicht bestellt werden.
shipping_required400Objekt shipping fehlt.
shipping_incomplete400address1, city, postcode oder country fehlt.
shipping_country_required400Land leer oder nicht in unseren Versandzonen.
payment_wallet_only400payment wurde auf einen anderen Wert als WALLET gesetzt.
invalid_json400Body ist kein gültiges JSON.
401Token fehlt, ist ungültig oder widerrufen.
subscription_required403Kein aktives Dropshipping-Abonnement.
not_found404Keine solche Bestellung in Ihrem Konto.
insufficient_stock409Nicht genügend Bestand für eine oder mehrere Positionen.

10. Beispiele

cURL — anlegen

curl -X POST https://cosmeticwholesale.eu/api/v1/dropshipping/orders \
  -H "Authorization: Bearer YOUR_ACCOUNT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "externalRef": "ORD123456",
    "shipping": {
      "firstName": "Name",
      "lastName": "LastName",
      "address1": "Main Street 123",
      "postcode": "1072 HM",
      "city": "Amsterdam",
      "country": "NL"
    },
    "lines": [
      { "ean": "6291107455365", "quantity": 2 }
    ]
  }'

cURL — auslesen

curl "https://cosmeticwholesale.eu/api/v1/dropshipping/orders?externalRef=ORD123456" \
  -H "Authorization: Bearer YOUR_ACCOUNT_TOKEN"

PHP — anlegen

<?php

$token = 'YOUR_ACCOUNT_TOKEN';
$url   = 'https://cosmeticwholesale.eu/api/v1/dropshipping/orders';

$payload = [
    'externalRef' => 'ORD123456',
    'shipping' => [
        'firstName' => 'Name',
        'lastName'  => 'LastName',
        'address1'  => 'Main Street 123',
        'postcode'  => '1072 HM',
        'city'      => 'Amsterdam',
        'country'   => 'NL',
    ],
    'lines' => [
        ['ean' => '6291107455365', 'quantity' => 2],
    ],
];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $token,
    'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));

$response = curl_exec($ch);
$status   = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$order = json_decode($response, true);

if ($status === 402) {
    // Held for insufficient wallet balance.
    // Keep your order open and retry with the SAME externalRef after topping up.
} elseif ($status === 201 || $status === 200) {
    // Accepted. Store $order['id'] and poll for tracking.
}
?>

PHP — auslesen

<?php

$token       = 'YOUR_ACCOUNT_TOKEN';
$externalRef = 'ORD123456';
$url = 'https://cosmeticwholesale.eu/api/v1/dropshipping/orders/' . rawurlencode($externalRef);

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer ' . $token]);
$response = curl_exec($ch);
curl_close($ch);

echo $response;
?>

Anhang A — Migration von den Turor-OMS-Endpunkten

Das alte Paket verwies auf https://omsback.turor.org/restApi/v1 mit HTTP-Basic-Zugangsdaten. Diese Zugangsdaten gehörten zu einem gemeinsam genutzten Systemkonto und umgingen die Wallet-, Bestands- und Abonnementebene. Sie werden nicht mehr ausgegeben, und bestehende werden zurückgezogen.

Endpunkte

AltNeu
POST /restApi/v1/dropShippingOrderPOST /api/v1/dropshipping/orders
GET /restApi/v1/dropShippingOrderItem/{id}GET /api/v1/dropshipping/orders/{id} (auf Bestellebene, nicht auf Positionsebene)
HTTP Basic username / passwordAuthorization: Bearer <account token>

Request-Felder

AltNeu
order_numberexternalRef
line_items[]lines[]
line_items[].eanlines[].ean
line_items[].quantitylines[].quantity
line_items[].nameentfernt — stammt aus unserem Katalog
line_items[].image_srcentfernt — stammt aus unserem Katalog
shipping.first_name / last_nameshipping.firstName / lastName (snake_case weiterhin akzeptiert)
shipping.address_1 / address_2shipping.address1 / address2 (snake_case weiterhin akzeptiert)
shipping.country, city, postcode, company, phoneunverändert
payment: "WALLET"

Response-Felder

AltNeu
orderCreated: 1HTTP 201
orderCreated: 0 (existiert bereits)HTTP 200
productCreated / productUpdated / orderUpdatedentfernt
omsOrderItemIds: "8059,8060" (kommaseparierte Zeichenkette)omsOrderItemIds: ["8059","8060"] (Array aus Zeichenketten)
statusCode / message im BodyHTTP-Statuscode; Fehler enthalten error + code
data.status, data.trackingNumber, data.trackingDatestatus, tracking[].trackingNumber, tracking[].dateShipped
data.serializedData, shippingProviderNametracking[].provider
walletDebited, omsPushStatus, 402 HOLD

Verhaltensänderungen, die Sie in Ihrem Code prüfen sollten

  1. 402 ist kein Fehlschlag. Die alte API hatte keine Wallet-Ebene. Behandeln Sie 402 als "zurückgehalten, erneut mit derselben Referenz versuchen" — niemals als "Bestellung stornieren".
  2. Wiederholungen sind sicher. externalRef dedupliziert; die alte Prüfung auf orderCreated: 0 wird durch die Unterscheidung zwischen 200 und 201 ersetzt.
  3. Die Sendungsverfolgung ist zur Bestellung gewandert. Ein GET pro Bestellung ersetzt ein GET pro Position, und das Ergebnis ist ein Array.
  4. Die Produktdaten gehören uns. Senden Sie keine Titel und Bild-URLs mehr.