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
| Umgebung | Basis-URL |
|---|---|
| Produktion | https://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_TOKENwird als alternativer Header akzeptiert.
3. Voraussetzungen
Eine Bestellung wird nur angenommen, wenn alle folgenden Bedingungen erfüllt sind:
- Ihr Konto ist freigeschaltet und im Kanal dropshipping.
- Ihr Dropshipping-Abonnement ist aktiv — andernfalls liefert jede Anfrage
403 subscription_required. - Ihr Wallet verfügt über ausreichendes Guthaben für die Bestellung — andernfalls wird die Bestellung angelegt und zurückgehalten (
402, siehe Abschnitt 6). - Jede
eanin 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
| Feld | Typ | Pflicht | Hinweise |
|---|---|---|---|
externalRef | string | ja | Ihre eigene Bestellnummer. Muss innerhalb Ihres Kontos eindeutig sein. Dies ist der Idempotenzschlüssel — siehe Abschnitt 5. |
payment | string | nein | Nur "WALLET" wird unterstützt. Standardwert ist "WALLET". |
lines[].ean | string | ja | EAN, wie in Ihrem Katalog-Feed veröffentlicht. |
lines[].quantity | integer | ja | Muss ≥ 1 sein. |
shipping.firstName | string | empfohlen | |
shipping.lastName | string | empfohlen | |
shipping.address1 | string | ja | |
shipping.address2 | string | nein | |
shipping.postcode | string | ja | |
shipping.city | string | ja | |
shipping.country | string | ja | ISO 3166-1 alpha-2, z. B. NL, BE, DE. Muss ein Land sein, in das wir liefern. |
shipping.company | string | nein | |
shipping.state | string | nein | |
shipping.phone | string | nein | Empfohlen — manche Zusteller setzen sie voraus. |
shipping.email | string | nein | Wird, 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": []
}
| Feld | Bedeutung |
|---|---|
id | Cosmetic Wholesale Bestell-ID (UUID). Speichern Sie diese. |
externalRef | Echo Ihrer Bestellnummer. |
omsReference | Interne Lagerreferenz, ds:{accountId}:{externalRef}. |
status | Siehe Abschnitt 7. |
walletDebited | true, sobald die Bestellung aus Ihrem Wallet bezahlt wurde. |
omsPushStatus | Fortschritt der Übergabe an unser Lager: pending, pushed, retry, failed, skipped oder null. |
omsOrderItemIds | Lager-Positions-IDs, werden nach Abschluss der Übergabe gefüllt. In der ersten Response leer — fragen Sie per GET nach. |
tracking | Leer, bis das Paket versendet wird. Siehe Abschnitt 8. |
HTTP-Statuscodes
| Code | Bedeutung | Was Ihr System tun sollte |
|---|---|---|
201 | Bestellung angelegt und aus dem Wallet bezahlt. | Als gesendet markieren. |
200 | Diese externalRef existiert bereits. Der Body enthält den aktuellen Stand dieser Bestellung. | Nichts — dies ist ein sicherer erneuter Versuch. |
402 | Bestellung angelegt, aber zurückgehalten: Das Wallet-Guthaben reicht nicht aus. | Nicht stornieren. Siehe Abschnitt 6. |
400 | Validierungsfehler. | Payload korrigieren; nicht unverändert erneut senden. |
401 | Token fehlt, ist ungültig oder widerrufen. | Zugangsdaten prüfen. |
403 | Kein aktives Dropshipping-Abonnement. | Kontaktieren Sie uns. |
404 | (nur GET) Bestellung in Ihrem Konto nicht gefunden. | |
409 | Unzureichender 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
200mit 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
externalReferneut per POST, oder warten Sie einfach ab: Zurückgehaltene Bestellungen werden automatisch erneut versucht, sobald das Guthaben ausreicht. - Fragen Sie per
GETnach, biswalletDebitedden Werttruehat undstatusnicht mehrHOLDist.
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
status | Bedeutung |
|---|---|
HOLD | Wegen unzureichenden Wallet-Guthabens zurückgehalten (siehe Abschnitt 6). |
PENDING_PAYMENT | Wallet-Zahlung läuft. |
ON_HOLD | Wartet auf Versand-/Umsatzsteuerverarbeitung. |
PAID | Aus dem Wallet bezahlt. |
PROCESSING | Wird im Lager vorbereitet. |
SHIPPED | An den Zusteller übergeben — tracking ist gefüllt. |
COMPLETED | Zugestellt / abgeschlossen. |
CANCELLED, REFUNDED | Endzustä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.
code | HTTP | Ursache |
|---|---|---|
external_ref_required | 400 | externalRef fehlt oder ist leer. |
lines_required | 400 | lines fehlt oder ist leer. |
ean_required | 400 | Eine Position hat keine ean. |
invalid_qty | 400 | quantity ist keine positive Ganzzahl. |
unknown_ean | 400 | Die EAN ist nicht in Ihrem Katalog enthalten. |
forbidden_ean | 400 | Die EAN kann über diesen Endpunkt nicht bestellt werden. |
shipping_required | 400 | Objekt shipping fehlt. |
shipping_incomplete | 400 | address1, city, postcode oder country fehlt. |
shipping_country_required | 400 | Land leer oder nicht in unseren Versandzonen. |
payment_wallet_only | 400 | payment wurde auf einen anderen Wert als WALLET gesetzt. |
invalid_json | 400 | Body ist kein gültiges JSON. |
| — | 401 | Token fehlt, ist ungültig oder widerrufen. |
subscription_required | 403 | Kein aktives Dropshipping-Abonnement. |
not_found | 404 | Keine solche Bestellung in Ihrem Konto. |
insufficient_stock | 409 | Nicht 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
| Alt | Neu |
|---|---|
POST /restApi/v1/dropShippingOrder | POST /api/v1/dropshipping/orders |
GET /restApi/v1/dropShippingOrderItem/{id} | GET /api/v1/dropshipping/orders/{id} (auf Bestellebene, nicht auf Positionsebene) |
HTTP Basic username / password | Authorization: Bearer <account token> |
Request-Felder
| Alt | Neu |
|---|---|
order_number | externalRef |
line_items[] | lines[] |
line_items[].ean | lines[].ean |
line_items[].quantity | lines[].quantity |
line_items[].name | entfernt — stammt aus unserem Katalog |
line_items[].image_src | entfernt — stammt aus unserem Katalog |
shipping.first_name / last_name | shipping.firstName / lastName (snake_case weiterhin akzeptiert) |
shipping.address_1 / address_2 | shipping.address1 / address2 (snake_case weiterhin akzeptiert) |
shipping.country, city, postcode, company, phone | unverändert |
| — | payment: "WALLET" |
Response-Felder
| Alt | Neu |
|---|---|
orderCreated: 1 | HTTP 201 |
orderCreated: 0 (existiert bereits) | HTTP 200 |
productCreated / productUpdated / orderUpdated | entfernt |
omsOrderItemIds: "8059,8060" (kommaseparierte Zeichenkette) | omsOrderItemIds: ["8059","8060"] (Array aus Zeichenketten) |
statusCode / message im Body | HTTP-Statuscode; Fehler enthalten error + code |
data.status, data.trackingNumber, data.trackingDate | status, tracking[].trackingNumber, tracking[].dateShipped |
data.serializedData, shippingProviderName | tracking[].provider |
| — | walletDebited, omsPushStatus, 402 HOLD |
Verhaltensänderungen, die Sie in Ihrem Code prüfen sollten
402ist kein Fehlschlag. Die alte API hatte keine Wallet-Ebene. Behandeln Sie402als "zurückgehalten, erneut mit derselben Referenz versuchen" — niemals als "Bestellung stornieren".- Wiederholungen sind sicher.
externalRefdedupliziert; die alte Prüfung auforderCreated: 0wird durch die Unterscheidung zwischen200und201ersetzt. - Die Sendungsverfolgung ist zur Bestellung gewandert. Ein
GETpro Bestellung ersetzt einGETpro Position, und das Ergebnis ist ein Array. - Die Produktdaten gehören uns. Senden Sie keine Titel und Bild-URLs mehr.