FURNIDESK / DEVELOPER PORTAL
Furnidesk API · بوابة المطورين
زامن المنتجات والأقمشة والصور والمخزون بأمان مع موقعك أو تطبيقك.
https://work.furnidesk.com/api/v1
الاتصال الأول
يفعّل مسؤول المنصة API للشركة. ينشئ المالك أو المدير اتصالاً لكل نظام مصدر ومفتاحاً مؤقتاً من لوحة التكاملات. يظهر السر مرة واحدة؛ احفظه في متغير بيئة على الخادم واختبر GET /me. لا ينشأ عضو فريق؛ تُسجّل العمليات باسم الاتصال.
curl "https://work.furnidesk.com/api/v1/me" \
-H "Authorization: Bearer $FURNIDESK_API_KEY"المصادقة والمفاتيح
يلزم Authorization: Bearer لكل طلب. جلسة اللوحة لا تمنح صلاحية API. الصلاحيات الفعلية تقاطع صلاحيات الشركة والمفتاح؛ قراءة الكتالوج لا تشمل السعر أو التكلفة. مدة المفتاح من1 إلى365 يوماً ويمكن إلغاؤه. لا تضعه في شيفرة المتصفح. التعطيل أو انتهاء الاشتراك يوقف الوصول. النطاق المخصص يقبل مفتاح شركته فقط.
| Scope | الصلاحيات والسلوك |
|---|---|
catalog:read | قراءة الكتالوج |
catalog:write | كتابة الكتالوج |
prices:read | قراءة الأسعار |
prices:write | كتابة الأسعار |
costs:read | قراءة التكاليف |
costs:write | كتابة التكاليف |
media:read | قراءة الصور |
media:write | رفع الصور |
stock:read | قراءة المخزون |
stock:write | كتابة المخزون الفعلي |
إنشاء المنتجات وتحديثها
POST /products/upsert يحدّث external_id أو ينشئ منتجاً. الاسم والفئة مطلوبان للإنشاء. single منتج مباشر وbundle مجموعة منتجات مباشرة بكميات. عدة فئات ومجموعة وعلامة واحدة. أرسل expected_revision من revision في GET. الحقول المحذوفة من الطلب تُحفظ والقوائم المرسلة تستبدل القوائم كاملة. استخدم active:false للمنتج الناقص. DELETE يؤرشف دون تغيير الوثائق السابقة. v1 لا يحدّث الموردين.
{
"external_id": "sofa-100",
"expected_revision": 0,
"mode": "combined",
"name": "Solo Sofa",
"kind": "single",
"active": false,
"category_ids": [
{
"external_id": "sofas"
}
],
"price": "1000.00",
"currency": "EUR",
"unit": "piece",
"details": {
"width": "220.00",
"height": "85.00",
"depth": "95.00"
}
}المعرفات والعلاقات واللغات
external_id فريد ضمن الشركة والاتصال والمورد. target_id يربط سجلاً موجوداً لأول مرة. المراجع UUID أو كائن external_id. الفئات تدعم الأب دون دورات. أسماء العلامات والمجموعات مشتركة؛ أسماء الفئات والأوصاف قابلة للترجمة. name/description باللغة الأساسية وtranslations باللغات النشطة الأخرى، والناقص يعود إلى الأساسية.
{
"external_id": "sofas",
"name": "Koltuk",
"translations": {
"en": {
"name": "Sofas"
}
}
}الأقمشة وحقول الاختيار
استورد الأنواع ثم المجموعات ثم الخيارات. الأقمشة تستخدم pricing_scope:group وقاعدة none/percent/fixed في الكارتة وnone في العينة. الأنواع الأخرى يمكنها استخدام option. fields يحدد مناطق مستقلة، اختيار واحد من الكارتات المسموحة لكل منطقة. القماش الأساسي والأذرع منفصلان.30% × coefficient 1 =30% و×0.3 =9%. المعامل الصريح يتطلب prices:write.
{
"external_id": "sofa-100",
"expected_revision": 3,
"fields": [
{
"external_id": "main-fabric",
"name": "Ana kumaş",
"type_id": {
"external_id": "fabric"
},
"group_ids": [
{
"external_id": "fabric-a"
},
{
"external_id": "fabric-b"
}
],
"coefficient": "1"
},
{
"external_id": "arm-fabric",
"name": "Kollar",
"type_id": {
"external_id": "fabric"
},
"group_ids": [
{
"external_id": "fabric-a"
}
],
"coefficient": "0.3"
}
]
}الأسعار والتكاليف والضرائب
catalog يغيّر المعلومات وprices الأسعار القائمة فقط وcombined كليهما بصلاحيات منفصلة. التكاليف تتطلب costs:read/write. المبالغ نصوص عشرية بعملة الشركة دون تحويل. الضريبة يجب أن تكون في إعدادات الشركة. tax_model هو inclusive/exclusive/none/unconfigured. العروض والمبيعات السابقة تحتفظ بأسعارها.
{
"external_id": "sofa-100",
"expected_revision": 4,
"mode": "prices",
"currency": "EUR",
"price": "1150.00",
"market_price": "1300.00"
}مخزون المنتجات والأقمشة
يلزم اشتراك مخزون نشط ووحدة مفعّلة وصلاحيات stock. مواقع الكتابة تُحدد للاتصال. source_kind:product/option للمنتجات أو الأقمشة. /inventory/{id} يستخدم UUID بطاقة المخزون؛ balances يحتوي المراجعات. set/in/out يغيّر الكمية الفعلية ويحفظ المحجوز والقادم ويمنع الفعلي أقل من المحجوز أو الصفر. الرصيد الجديد revision 0 وevent_id فريد والقطع أعداد صحيحة.
{
"event_id": "11111111-1111-4111-8111-111111111111",
"source_kind": "product",
"source_id": {
"external_id": "sofa-100"
},
"location_id": "22222222-2222-4222-8222-222222222222",
"action": "set",
"quantity": "5",
"revision": 0,
"note": "Website initial physical balance"
}استيراد الصور
POST /media يستقبل صورة multipart مع external_id وname. JPG/PNG/WebP حتى10 MB و40 MP؛ الكتالوج1500 px والخيار720 px دون تكبير. الشفافية واتجاه JPEG محفوظان والناتج WebP. بعد202 انتظر completed في /jobs/{id} ثم اربط UUID/external_id. معالجة متسلسلة في مجلد الشركة. المحتوى أو الاسم أو الغرض الجديد يحتاج external_id جديداً. لا تنزيل URL أو شعارات شركات في v1.
curl -X POST "https://work.furnidesk.com/api/v1/media" \
-H "Authorization: Bearer $FURNIDESK_API_KEY" \
-H "Idempotency-Key: image-sofa-100-v1" \
-F 'external_id=sofa-photo-100-v1' -F 'name=Solo Sofa Beige' \
-F 'purpose=catalog' -F '[email protected]'الاستيراد الجماعي والمزامنة
/imports يقبل1–50 سجلاً. dry_run:true يتحقق دون حفظ، وإلا202 وتقرير created/updated/unchanged/failed. تُحل العلاقات بعدة مرورّات وكل سجل ذري. قوائم GET مع limit 1–100 وnext_cursor. احفظ change_cursor واسأل /changes؛ حرّك next_cursor حتى للصفحة المرشحة الفارغة. origin_connection_id يمنع التكرار الدائري. التقارير خاصة بالاتصال.
{
"dry_run": true,
"records": [
{
"resource": "products",
"body": {
"external_id": "new-chair",
"name": "Chair",
"active": false,
"category_ids": [
{
"external_id": "new-chairs"
}
]
}
},
{
"resource": "categories",
"body": {
"external_id": "new-chairs",
"name": "Chairs"
}
}
]
}الأخطاء والحدود وإعادة المحاولة
POST وDELETE يتطلبان Idempotency-Key بطول8–120. أعد نفس المفتاح والمحتوى عند الخطأ المؤقت.409 يتطلب إعادة القراءة وحل التعارض.401 وصول و403 صلاحيات و404 مفقود و422 تحقق و429 حد و503 انشغال.120 طلباً للمفتاح بالدقيقة وحد الشركة خمسة أضعاف؛ JSON 1 MB و20 مهمة مفتوحة و100 MB صور لكل اتصال. خطأ صلاحية التخزين يوقف الصور حتى إصلاح المشغّل. /jobs/{id}/retry يعيد المهمة الفاشلة أو الموقوفة بمفتاح نشط للاتصال نفسه.
أرسل ترويسة User-Agent تحتوي على اسم التطبيق وإصداره، مثل FurnideskConnector/1.0. قد تصدر استجابة 403 غير JSON أو استجابة HTML من طبقة حماية الشبكة قبل التحقق في API. تحقق من Content-Type وتواصل مع الدعم.
{
"error": {
"code": "revision_conflict",
"message": "Read the current revision before updating.",
"details": {
"current_revision": 5
}
},
"request_id": "33333333-3333-4333-8333-333333333333"
}الإصدارات والخطة المستقبلية
v1 يشمل الكتالوج والخيارات والصور والأسعار والتكاليف والمخزون الاختياري. الطلبات والعملاء والحسابات مراحل لاحقة دون نقاط اتصال نشطة. Webhooks والموصلات جاهزة الاستخدام مخطط لها. الإضافات المتوافقة توسّع v1 دون منح صلاحيات جديدة تلقائياً. التغيير غير المتوافق يصدر بإصدار رئيسي جديد. البوابة ثابتة ولا تستعلم قاعدة البيانات.
مرجع نقاط الاتصال
أسماء الحقول التقنية ثابتة في جميع اللغات. يشرح ملف OpenAPI الحقول والقيود والمرشحات والأمثلة والأخطاء.
| HTTP | Endpoint | الصلاحيات والسلوك |
|---|---|---|
| GET | /api/v1/products | catalog:read; prices mode requires prices:read. prices:read and costs:read independently expose pricing and cost fields. |
| GET | /api/v1/products/{id} | catalog:read. Pricing and costs are included only with their separate read scopes. For price-only keys, use the list endpoint with mode=prices. |
| DELETE | /api/v1/products/{id} | catalog:write. Soft archive; existing quotations and sales remain unchanged. |
| POST | /api/v1/products/upsert | catalog:write and/or prices:write; costs:write separately. external_id is scoped to the current connection. |
| GET | /api/v1/categories | catalog:read; prices mode requires prices:read. prices:read and costs:read independently expose pricing and cost fields. |
| GET | /api/v1/categories/{id} | catalog:read. Pricing and costs are included only with their separate read scopes. For price-only keys, use the list endpoint with mode=prices. |
| POST | /api/v1/categories/upsert | catalog:write and/or prices:write; costs:write separately. external_id is scoped to the current connection. |
| GET | /api/v1/collections | catalog:read; prices mode requires prices:read. prices:read and costs:read independently expose pricing and cost fields. |
| GET | /api/v1/collections/{id} | catalog:read. Pricing and costs are included only with their separate read scopes. For price-only keys, use the list endpoint with mode=prices. |
| POST | /api/v1/collections/upsert | catalog:write and/or prices:write; costs:write separately. external_id is scoped to the current connection. |
| GET | /api/v1/brands | catalog:read; prices mode requires prices:read. prices:read and costs:read independently expose pricing and cost fields. |
| GET | /api/v1/brands/{id} | catalog:read. Pricing and costs are included only with their separate read scopes. For price-only keys, use the list endpoint with mode=prices. |
| POST | /api/v1/brands/upsert | catalog:write and/or prices:write; costs:write separately. external_id is scoped to the current connection. |
| GET | /api/v1/customization-types | catalog:read; prices mode requires prices:read. prices:read and costs:read independently expose pricing and cost fields. |
| GET | /api/v1/customization-types/{id} | catalog:read. Pricing and costs are included only with their separate read scopes. For price-only keys, use the list endpoint with mode=prices. |
| POST | /api/v1/customization-types/upsert | catalog:write and/or prices:write; costs:write separately. external_id is scoped to the current connection. |
| GET | /api/v1/customization-groups | catalog:read; prices mode requires prices:read. prices:read and costs:read independently expose pricing and cost fields. |
| GET | /api/v1/customization-groups/{id} | catalog:read. Pricing and costs are included only with their separate read scopes. For price-only keys, use the list endpoint with mode=prices. |
| POST | /api/v1/customization-groups/upsert | catalog:write and/or prices:write; costs:write separately. external_id is scoped to the current connection. |
| GET | /api/v1/customization-options | catalog:read; prices mode requires prices:read. prices:read and costs:read independently expose pricing and cost fields. |
| GET | /api/v1/customization-options/{id} | catalog:read. Pricing and costs are included only with their separate read scopes. For price-only keys, use the list endpoint with mode=prices. |
| POST | /api/v1/customization-options/upsert | catalog:write and/or prices:write; costs:write separately. external_id is scoped to the current connection. |
| GET | /api/v1/media | media:read |
| POST | /api/v1/media | media:write. One file per request; serial worker. JPG/PNG/WebP <=10 MB /40 MP; catalog max1500px, option max720px, no upscaling. Immutable image versions: changing content/name/purpose requires a new external_id. |
| GET | /api/v1/media/{id} | media:read |
| GET | /api/v1/me | Any valid key. |
| GET | /api/v1/changes | Resource read scope required. origin_connection_id supports loop prevention. Always persist next_cursor, even for an empty filtered page. |
| POST | /api/v1/imports | Write scopes for every record. |
| GET | /api/v1/imports | Any valid key; jobs from another connection remain hidden. |
| GET | /api/v1/jobs | Any valid key; jobs from another connection remain hidden. |
| GET | /api/v1/jobs/{id} | Any valid key on the same connection. Per-record failures appear in report counts. |
| GET | /api/v1/imports/{id} | Any valid key on the same connection. Per-record failures appear in report counts. |
| POST | /api/v1/jobs/{id}/retry | Required write scopes; a replacement active key must belong to the same connection. Storage authorization failures also require an operator to resume storage after fixing credentials. |
| POST | /api/v1/imports/{id}/retry | Required write scopes; a replacement active key must belong to the same connection. Storage authorization failures also require an operator to resume storage after fixing credentials. |
| GET | /api/v1/inventory | stock:read; stock package and module must be active. Detail balances contain revisions. Pagination cursor is inventory item UUID. |
| POST | /api/v1/inventory | stock:write; company must have active stock package and enabled module. Location must be assigned to connection. |
| GET | /api/v1/inventory/{id} | stock:read; id is inventory item UUID, not product UUID. |
نماذج البيانات
أسماء الحقول التقنية ثابتة في جميع اللغات. يشرح ملف OpenAPI الحقول والقيود والمرشحات والأمثلة والأخطاء.
Reference
{
"oneOf": [
{
"type": "string",
"format": "uuid"
},
{
"type": "object",
"properties": {
"external_id": {
"type": "string",
"minLength": 1,
"maxLength": 200
}
},
"required": [
"external_id"
],
"additionalProperties": false,
"description": ""
}
],
"description": "Furnidesk UUID or external ID in the current company + connection + resource namespace."
}SelectionField
{
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"external_id": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"name": {
"type": "string"
},
"translations": {
"type": "object",
"description": "Enabled language codes only; main name/description fields use the company base language. Category names are translated; collection/brand names are shared. Omitted fields are preserved.",
"additionalProperties": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
}
},
"additionalProperties": false
}
},
"type_id": {
"$ref": "#/components/schemas/Reference"
},
"group_ids": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Reference"
},
"maxItems": 100
},
"coefficient": {
"type": "string",
"pattern": "^\\d+(\\.\\d{1,4})?$",
"description": "0 < coefficient <= 100. Explicit coefficients require prices:write. 1 × 30% = 30%; 0.3 × 30% = 9%."
}
},
"required": [
"name",
"type_id"
],
"additionalProperties": false,
"description": "Supply id or external_id. Each field permits one option across its groups; main fabric and arms are separate fields."
}Error
{
"type": "object",
"required": [
"error",
"request_id"
],
"properties": {
"request_id": {
"type": "string",
"format": "uuid"
},
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"details": {
"type": [
"object",
"array"
]
},
"retry_after": {
"type": "integer"
}
}
}
}
}Envelope
{
"type": "object",
"description": "Record fields are filtered by read scopes. Decimal values are strings. All responses include request_id; successful writes can include replayed:true.",
"properties": {
"request_id": {
"type": "string",
"format": "uuid"
},
"outcome": {
"enum": [
"created",
"updated",
"unchanged"
]
},
"record": {
"type": "object"
},
"records": {
"type": "array",
"items": {
"type": "object"
}
},
"job": {
"type": "object"
},
"principal": {
"type": "object"
},
"report": {
"type": "object"
},
"next_cursor": {
"type": [
"string",
"null"
]
},
"has_more": {
"type": "boolean"
},
"replayed": {
"type": "boolean"
}
}
}products-upsert
{
"type": "object",
"properties": {
"external_id": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"target_id": {
"type": "string",
"format": "uuid"
},
"expected_revision": {
"type": "integer",
"minimum": 0,
"description": "Required for updates; read record.revision first. Use 0 or omit for a new record."
},
"mode": {
"enum": [
"catalog",
"prices",
"combined"
],
"default": "catalog"
},
"currency": {
"type": "string",
"description": "Must equal company currency when provided."
},
"name": {
"type": "string",
"maxLength": 200
},
"sku": {
"type": "string",
"maxLength": 100
},
"kind": {
"enum": [
"single",
"bundle"
]
},
"active": {
"type": "boolean",
"default": false
},
"category_ids": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Reference"
},
"minItems": 1,
"maxItems": 50
},
"collection_id": {
"oneOf": [
{
"$ref": "#/components/schemas/Reference"
},
{
"type": "null"
}
]
},
"brand_id": {
"oneOf": [
{
"$ref": "#/components/schemas/Reference"
},
{
"type": "null"
}
]
},
"translations": {
"type": "object",
"description": "Enabled language codes only; main name/description fields use the company base language. Category names are translated; collection/brand names are shared. Omitted fields are preserved.",
"additionalProperties": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
}
},
"additionalProperties": false
}
},
"details": {
"type": "object",
"properties": {
"description": {
"type": "string",
"maxLength": 4000
},
"width": {
"type": [
"string",
"null"
],
"description": "Non-negative decimal; cm for width/height/depth, kg for weight, m³ for packaged volume."
},
"height": {
"type": [
"string",
"null"
],
"description": "Non-negative decimal; cm for width/height/depth, kg for weight, m³ for packaged volume."
},
"depth": {
"type": [
"string",
"null"
],
"description": "Non-negative decimal; cm for width/height/depth, kg for weight, m³ for packaged volume."
},
"weight": {
"type": [
"string",
"null"
],
"description": "Non-negative decimal; cm for width/height/depth, kg for weight, m³ for packaged volume."
},
"volume": {
"type": [
"string",
"null"
],
"description": "Non-negative decimal; cm for width/height/depth, kg for weight, m³ for packaged volume."
}
},
"required": [],
"additionalProperties": false,
"description": ""
},
"unit": {
"enum": [
"piece",
"metre",
"sqm",
"cbm"
]
},
"components": {
"type": "array",
"items": {
"type": "object",
"properties": {
"product_id": {
"$ref": "#/components/schemas/Reference"
},
"quantity": {
"type": "integer",
"minimum": 1,
"maximum": 10000
}
},
"required": [
"product_id",
"quantity"
],
"additionalProperties": false,
"description": ""
},
"maxItems": 100
},
"media_ids": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Reference"
},
"maxItems": 50
},
"cover_id": {
"oneOf": [
{
"$ref": "#/components/schemas/Reference"
},
{
"type": "null"
}
]
},
"fields": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SelectionField"
},
"maxItems": 100
},
"price": {
"type": "string",
"pattern": "^\\d{1,16}(\\.\\d{1,2})?$",
"description": "Non-negative decimal string. Money uses the company currency; no currency conversion."
},
"market_price": {
"oneOf": [
{
"type": "string",
"pattern": "^\\d{1,16}(\\.\\d{1,2})?$",
"description": "Non-negative decimal string. Money uses the company currency; no currency conversion."
},
{
"type": "null"
}
]
},
"cost": {
"oneOf": [
{
"type": "string",
"pattern": "^\\d{1,16}(\\.\\d{1,2})?$",
"description": "Non-negative decimal string. Money uses the company currency; no currency conversion."
},
{
"type": "null"
}
]
},
"tax_rate": {
"oneOf": [
{
"type": "string",
"pattern": "^\\d{1,16}(\\.\\d{1,2})?$",
"description": "Non-negative decimal string. Money uses the company currency; no currency conversion."
},
{
"type": "null"
}
]
}
},
"required": [
"external_id"
],
"additionalProperties": false,
"description": "Partial update. New records also require name and relevant category/type/group references. catalog mode excludes price/cost; prices mode changes only price fields on existing records; combined allows both with separate scopes."
}categories-upsert
{
"type": "object",
"properties": {
"external_id": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"target_id": {
"type": "string",
"format": "uuid"
},
"expected_revision": {
"type": "integer",
"minimum": 0,
"description": "Required for updates; read record.revision first. Use 0 or omit for a new record."
},
"mode": {
"enum": [
"catalog",
"prices",
"combined"
],
"default": "catalog"
},
"currency": {
"type": "string",
"description": "Must equal company currency when provided."
},
"name": {
"type": "string"
},
"parent_id": {
"oneOf": [
{
"$ref": "#/components/schemas/Reference"
},
{
"type": "null"
}
]
},
"translations": {
"type": "object",
"description": "Enabled language codes only; main name/description fields use the company base language. Category names are translated; collection/brand names are shared. Omitted fields are preserved.",
"additionalProperties": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
}
},
"additionalProperties": false
}
},
"description": {
"type": "string"
},
"image_id": {
"oneOf": [
{
"$ref": "#/components/schemas/Reference"
},
{
"type": "null"
}
]
}
},
"required": [
"external_id"
],
"additionalProperties": false,
"description": "Partial update. New records also require name and relevant category/type/group references. catalog mode excludes price/cost; prices mode changes only price fields on existing records; combined allows both with separate scopes."
}collections-upsert
{
"type": "object",
"properties": {
"external_id": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"target_id": {
"type": "string",
"format": "uuid"
},
"expected_revision": {
"type": "integer",
"minimum": 0,
"description": "Required for updates; read record.revision first. Use 0 or omit for a new record."
},
"mode": {
"enum": [
"catalog",
"prices",
"combined"
],
"default": "catalog"
},
"currency": {
"type": "string",
"description": "Must equal company currency when provided."
},
"name": {
"type": "string"
},
"translations": {
"type": "object",
"description": "Enabled language codes only; main name/description fields use the company base language. Category names are translated; collection/brand names are shared. Omitted fields are preserved.",
"additionalProperties": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
}
},
"additionalProperties": false
}
},
"description": {
"type": "string"
},
"image_id": {
"oneOf": [
{
"$ref": "#/components/schemas/Reference"
},
{
"type": "null"
}
]
}
},
"required": [
"external_id"
],
"additionalProperties": false,
"description": "Partial update. New records also require name and relevant category/type/group references. catalog mode excludes price/cost; prices mode changes only price fields on existing records; combined allows both with separate scopes."
}brands-upsert
{
"type": "object",
"properties": {
"external_id": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"target_id": {
"type": "string",
"format": "uuid"
},
"expected_revision": {
"type": "integer",
"minimum": 0,
"description": "Required for updates; read record.revision first. Use 0 or omit for a new record."
},
"mode": {
"enum": [
"catalog",
"prices",
"combined"
],
"default": "catalog"
},
"currency": {
"type": "string",
"description": "Must equal company currency when provided."
},
"name": {
"type": "string"
},
"translations": {
"type": "object",
"description": "Enabled language codes only; main name/description fields use the company base language. Category names are translated; collection/brand names are shared. Omitted fields are preserved.",
"additionalProperties": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
}
},
"additionalProperties": false
}
},
"description": {
"type": "string"
},
"image_id": {
"oneOf": [
{
"$ref": "#/components/schemas/Reference"
},
{
"type": "null"
}
]
}
},
"required": [
"external_id"
],
"additionalProperties": false,
"description": "Partial update. New records also require name and relevant category/type/group references. catalog mode excludes price/cost; prices mode changes only price fields on existing records; combined allows both with separate scopes."
}customization-types-upsert
{
"type": "object",
"properties": {
"external_id": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"target_id": {
"type": "string",
"format": "uuid"
},
"expected_revision": {
"type": "integer",
"minimum": 0,
"description": "Required for updates; read record.revision first. Use 0 or omit for a new record."
},
"mode": {
"enum": [
"catalog",
"prices",
"combined"
],
"default": "catalog"
},
"currency": {
"type": "string",
"description": "Must equal company currency when provided."
},
"name": {
"type": "string"
},
"translations": {
"type": "object",
"description": "Enabled language codes only; main name/description fields use the company base language. Category names are translated; collection/brand names are shared. Omitted fields are preserved.",
"additionalProperties": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
}
},
"additionalProperties": false
}
},
"pricing_scope": {
"enum": [
"group",
"option"
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"external_id"
],
"additionalProperties": false,
"description": "Partial update. New records also require name and relevant category/type/group references. catalog mode excludes price/cost; prices mode changes only price fields on existing records; combined allows both with separate scopes."
}customization-groups-upsert
{
"type": "object",
"properties": {
"external_id": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"target_id": {
"type": "string",
"format": "uuid"
},
"expected_revision": {
"type": "integer",
"minimum": 0,
"description": "Required for updates; read record.revision first. Use 0 or omit for a new record."
},
"mode": {
"enum": [
"catalog",
"prices",
"combined"
],
"default": "catalog"
},
"currency": {
"type": "string",
"description": "Must equal company currency when provided."
},
"name": {
"type": "string"
},
"translations": {
"type": "object",
"description": "Enabled language codes only; main name/description fields use the company base language. Category names are translated; collection/brand names are shared. Omitted fields are preserved.",
"additionalProperties": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
}
},
"additionalProperties": false
}
},
"type_id": {
"$ref": "#/components/schemas/Reference"
},
"active": {
"type": "boolean"
},
"photo_enabled": {
"type": "boolean"
},
"pricing_mode": {
"enum": [
"none",
"percent",
"fixed"
]
},
"pricing_value": {
"type": "string",
"pattern": "^\\d{1,16}(\\.\\d{1,2})?$",
"description": "Non-negative decimal string. Money uses the company currency; no currency conversion."
}
},
"required": [
"external_id"
],
"additionalProperties": false,
"description": "Partial update. New records also require name and relevant category/type/group references. catalog mode excludes price/cost; prices mode changes only price fields on existing records; combined allows both with separate scopes."
}customization-options-upsert
{
"type": "object",
"properties": {
"external_id": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"target_id": {
"type": "string",
"format": "uuid"
},
"expected_revision": {
"type": "integer",
"minimum": 0,
"description": "Required for updates; read record.revision first. Use 0 or omit for a new record."
},
"mode": {
"enum": [
"catalog",
"prices",
"combined"
],
"default": "catalog"
},
"currency": {
"type": "string",
"description": "Must equal company currency when provided."
},
"name": {
"type": "string"
},
"translations": {
"type": "object",
"description": "Enabled language codes only; main name/description fields use the company base language. Category names are translated; collection/brand names are shared. Omitted fields are preserved.",
"additionalProperties": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
}
},
"additionalProperties": false
}
},
"group_id": {
"$ref": "#/components/schemas/Reference"
},
"code": {
"type": "string"
},
"active": {
"type": "boolean"
},
"media_id": {
"oneOf": [
{
"$ref": "#/components/schemas/Reference"
},
{
"type": "null"
}
]
},
"pricing_mode": {
"enum": [
"none",
"percent",
"fixed"
]
},
"pricing_value": {
"type": "string",
"pattern": "^\\d{1,16}(\\.\\d{1,2})?$",
"description": "Non-negative decimal string. Money uses the company currency; no currency conversion."
}
},
"required": [
"external_id"
],
"additionalProperties": false,
"description": "Partial update. New records also require name and relevant category/type/group references. catalog mode excludes price/cost; prices mode changes only price fields on existing records; combined allows both with separate scopes."
}StockWrite
{
"type": "object",
"properties": {
"event_id": {
"type": "string",
"format": "uuid"
},
"source_kind": {
"enum": [
"product",
"option"
]
},
"source_id": {
"$ref": "#/components/schemas/Reference"
},
"location_id": {
"type": "string",
"format": "uuid"
},
"action": {
"enum": [
"set",
"in",
"out"
]
},
"quantity": {
"type": "string",
"pattern": "^\\d{1,9}(\\.\\d{1,3})?$"
},
"revision": {
"type": "integer",
"minimum": 0
},
"note": {
"type": "string",
"minLength": 1,
"maxLength": 1000
}
},
"required": [
"event_id",
"source_kind",
"source_id",
"location_id",
"action",
"quantity",
"revision",
"note"
],
"additionalProperties": false,
"description": "Updates physical stock only in an assigned location. Preserves reserved and incoming stock. Cannot reduce physical below reserved or zero. Pieces must be whole numbers. A new balance uses revision 0. Read inventory/{item_id} balances for subsequent revisions."
}Import
{
"type": "object",
"properties": {
"dry_run": {
"type": "boolean",
"default": false
},
"records": {
"type": "array",
"minItems": 1,
"maxItems": 50,
"items": {
"oneOf": [
{
"type": "object",
"properties": {
"resource": {
"const": "products"
},
"body": {
"$ref": "#/components/schemas/products-upsert"
}
},
"required": [
"resource",
"body"
],
"additionalProperties": false,
"description": ""
},
{
"type": "object",
"properties": {
"resource": {
"const": "categories"
},
"body": {
"$ref": "#/components/schemas/categories-upsert"
}
},
"required": [
"resource",
"body"
],
"additionalProperties": false,
"description": ""
},
{
"type": "object",
"properties": {
"resource": {
"const": "collections"
},
"body": {
"$ref": "#/components/schemas/collections-upsert"
}
},
"required": [
"resource",
"body"
],
"additionalProperties": false,
"description": ""
},
{
"type": "object",
"properties": {
"resource": {
"const": "brands"
},
"body": {
"$ref": "#/components/schemas/brands-upsert"
}
},
"required": [
"resource",
"body"
],
"additionalProperties": false,
"description": ""
},
{
"type": "object",
"properties": {
"resource": {
"const": "customization-types"
},
"body": {
"$ref": "#/components/schemas/customization-types-upsert"
}
},
"required": [
"resource",
"body"
],
"additionalProperties": false,
"description": ""
},
{
"type": "object",
"properties": {
"resource": {
"const": "customization-groups"
},
"body": {
"$ref": "#/components/schemas/customization-groups-upsert"
}
},
"required": [
"resource",
"body"
],
"additionalProperties": false,
"description": ""
},
{
"type": "object",
"properties": {
"resource": {
"const": "customization-options"
},
"body": {
"$ref": "#/components/schemas/customization-options-upsert"
}
},
"required": [
"resource",
"body"
],
"additionalProperties": false,
"description": ""
}
]
}
}
},
"required": [
"records"
],
"additionalProperties": false,
"description": "Dry run returns a synchronous report and rolls back all changes. Otherwise 202 creates a job. Records resolve dependencies in multiple passes; each record is atomic; failures do not cancel other records."
}