دليل تنفيذ مكتوب وفق سلوك الـ API الموثَّق؛ ولا يذكر اسم أي عميل. نُشر في .
المشكلة: يبدأ البيع قبل وصول الصور
يُنشر كتالوج المزاد فور وصول السيارات المعروضة للبيع: رقم VIN ورقم القطعة وتاريخ البيع لكل سيارة. أما الصور فتأتي عندما تصل السيارة إلى الساحة ويصل المصوّر إليها، وهو ما يحدث لجزء من المزاد بعد بدء المزايدة. وحتى ذلك الحين، تظهر كل قطعة من هذه القطع كمربع رمادي بجانب سطر من النص.
يملأ تصيير الكتالوج الإطار بسنة الطراز الصحيحة. لكن المزاد هو أيضًا المكان الذي يجب ألا يُقدَّم فيه التصيير أبدًا على أنه السيارة نفسها: فالمزايدون يسعّرون الحالة، والتصيير لا يُظهر منها شيئًا، لأنه لا يصوّر أي مركبة مادية بعينها. كل ما يلي ينبع من مراعاة هذين الأمرين معًا.
كيف يبدو التكامل
- تصيير لكل قطعة انطلاقًا من رقم VIN الخاص بها، يُعرض فقط إلى أن تصل صور القطعة نفسها.
- أداة اختيار من الكتالوج للقطع المعروضة التي لا يمكن فك ترميز رقم VIN الخاص بها.
- تسمية على الصورة: “صورة من الكتالوج، وليست السيارة المعروضة في هذا المزاد”.
- عنوان URL واحد لكل قطعة في المزاد، يُنشأ مرة واحدة باستخدام
Idempotency-Key، ويبقى صالحًا طوال مدة البيع.
الخطوة 1: من رقم VIN لقطعة المزاد إلى معرّف المركبة
GET /api/v1/vin/{vin} مجاني. عندما تكون المركبة في الكتالوج، تتضمن الاستجابة data.vehicle.id، وهو المعرّف الثابت الذي يُخزَّن مع القطعة؛ أما الفئة والهيكل والمحرك المستخرجة فتبقى في نص القطعة. يمتد الكتالوج من 1990 إلى 2027، لذا يُصيَّر المخزون الأقدم في المزاد كما يُصيَّر الأحدث. يشرح دليل التأمين عملية فك الترميز حقلًا بحقل، وتغطي وثائق VIN أرقام VIN الجزئية والأخطاء.
# the lot's VIN to the vehicle id the lot stores (free); nothing printed means no catalog match
npx @meterapp/car-image vin "$LOT_VIN" --json | jq -r '.data.vehicle.id // empty'الخطوة 2: لا يوجد VIN قابل للفك؟ اختر المركبة من الكتالوج
تصل بعض القطع المعروضة في المزاد دون رقم VIN قابل للفك: مركبة صُنعت لسوق آخر، أو رقم VIN أخطأ المصدر في كتابته، أو ورقة إيداع لا تتضمن سوى سنة وعلامة تجارية وطراز. أضف إلى نموذج الإيداع ثلاث قوائم منسدلة تغذيها GET /api/v1/vehicles، وهي مجانية وعامة: يسرد ?year= العلامات التجارية التي لديها طراز في تلك السنة، ويسرد ?year=&makeId= طرازات تلك العلامة، ولكلٍّ منها معرّف المركبة لسنة الطراز تلك.
# every make with a 2006 model year (free, public)
curl -s "https://carimage.dev/api/v1/vehicles?year=2006"
# {"data":{"year":2006,"makes":[…,{"id":476,"name":"DODGE","slug":"dodge"},…]},…}
# that make's 2006 models, each with the id of its 2006 model year
curl -s "https://carimage.dev/api/v1/vehicles?year=2006&makeId=476"{
"data": {
"year": 2006,
"make_id": 476,
"models": [
{
"name": "Caliber",
"slug": "caliber",
"vehicle_type": "Passenger Car",
"id": "veh_294vq81yq0d3y"
},
{
"name": "Charger",
"slug": "charger",
"vehicle_type": "Passenger Car",
"id": "veh_96j8z47jqfd4r"
},
"…"
]
}
}الكتالوج نفسه متاح كحزمة @meterapp/vehicle-db مفتوحة المصدر، لذا يمكن للقوائم المنسدلة أيضًا أن تعمل دون اتصال على القائمة المطابقة نفسها. وفي كلتا الحالتين، يحمل اللوت النوع نفسه من المعرّفات الذي ينتج عن فك VIN، ولا توجد أي مرحلة لاحقة تكتب اسم السيارة يدويًا.
عندما تصل ورقة إرسالية كاملة دفعة واحدة، يقرأ POST /api/v1/vehicles/check حتى 2,000 إدخال في كل استدعاء، كلٌّ كما هو مكتوب في الورقة ومع رقم القطعة بوصفه ref، ويرد على كل منها: تطابق مع المعرّف الذي يجب تخزينه، أو اقتراحات، أو عدم تطابق، أو صف غير صالح. لا يُنشأ أي شيء ولا تُفرض أي رسوم؛ وتجدون التفاصيل في وثائق vehicles.
الخطوة 3: وضّح أن الصورة المولَّدة صورة مولَّدة
ضع التسمية على الصورة نفسها، حيث تقع عين المزايد، وفي النص البديل، وليس فقط في الوصف أسفلها. أما الحالة والمسافة المقطوعة والأضرار والتجهيزات ولون الطلاء الفعلي للسيارة فمكانها نص القطعة المعروضة، ثم الصور الفوتوغرافية حين تتوفر. وعندما تصل الصور، تُزال الصورة المُنشأة.
<figure class="lot-image">
<img src="https://carimage.dev/api/v1/delivery/eyJhbGciOi…" width="800" height="450"
alt="Catalog render of a 2006 Dodge Charger, not a photo of lot 117">
<span class="lot-image__badge">Catalog render, not this lot</span>
</figure>
<p>Photos of this lot are on their way. Condition, mileage and damage are in the report.</p>الخطوة 4: عنوان URL واحد لكل بند، يُنشأ مرة واحدة
أصدِر عنوان URL الخاص باللوت عند الاستلام باستخدام POST /api/v1/image-urls وIdempotency-Key مكوَّن من البيع واللوت، بالأحرف والأرقام و. و_ و: و-. يُدفع ثمن عنوان URL عند إصداره (1 وحدة رصيد)، لذا فبدون مفتاح ستؤدي إعادة محاولة الاستلام إلى إصداره والدفع مرتين. مع المفتاح:
- يؤدي استخدام المفتاح نفسه مع المحتوى نفسه خلال 24 ساعة إلى إعادة الاستجابة الأولى، بما فيها عنوان URL نفسه، مع
Idempotent-Replayed: trueودون خصم ثانٍ. - المفتاح نفسه مع متن مختلف يُرجع
422، وإعادة المحاولة التي تسبق الطلب الأول تُرجع409معRetry-After. لذا ثبّتوا كل ما في المتن، بما في ذلك مدة الصلاحية، عند إنشاء القطعة المعروضة في المزاد، وخزّنوه معها. - احفظ عنوان URL في سجل القطعة أيضًا: يحمي المفتاح عمليات إعادة المحاولة، وعنوان URL المحفوظ هو ما تقرؤه الصفحات طوال بقية المزاد.
يمكن لكتالوج كامل يُستورد دفعة واحدة إصدار روابط لما يصل إلى 50 من قطع المزاد في كل استدعاء، مع مفتاح لكل دفعة؛ ويعكس كل URL في الاستجابة المركبة والخيارات التي أُصدر من أجلها.
curl --fail-with-body -X POST https://carimage.dev/api/v1/image-urls \
-H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sale-2026-10-14:lot-117" \
-d '{"images":[{"vehicle":"veh_96j8z47jqfd4r","view":"side","width":800,"height":450,"fit":"contain","trim":true,"padding":6,"format":"auto"}],"ttl_seconds":259200}'
# run it again: the same URL comes back with Idempotent-Replayed: true, and nothing more is chargedالخطوة 5: طابقوا مدة صلاحية عنوان URL مع مدة البيع
تصل قيمة ttl_seconds إلى 604800 (7 أيام) كحد أقصى؛ وتُقتطع أي قيمة أطول إلى هذا الحد. بالنسبة إلى مزاد يُغلق خلال الأسبوع، اضبطها على الوقت المتبقي حتى الإغلاق. عندها يتوقف العنصر النائب عن العمل حين تتوقف المزايدة، وبعد ذلك يُرجع عنوان URL الرمز 403.
- بيع محدد المدة أطول من 7 يومًا: أنشئ العنوان مع
renew: true، وttl_secondsعند الحد الأقصى، وrenew_daysبما يغطي مدة البيع. كل أسبوع إضافي تُفتح فيه القطعة يكلّف 1 رصيدًا، والأسابيع الهادئة لا تكلّف شيئًا، ويتوقف التجديد عندrenews_until. - أبقِ
max_usesعند 0، وهي القيمة الافتراضية: العنوان غير المحدود قابل للتخزين المؤقت العام طوال مدة صلاحيته، بينما العنوان المحدود يكونprivate, no-storeويرسل كل عملية تحميل إلى الخادم الأصلي. - قيّدوا مفتاح الإصدار بنطاقات مزادكم من لوحة التحكم: صورة القطعة المنسوخة إلى موقع آخر تُرجع
403.
import { CarImageClient, type ImageParams } from "@meterapp/car-image-sdk";
const client = new CarImageClient({ apiKey: process.env.CAR_IMAGE_API_KEY });
const lotImage = (vehicle: string): ImageParams => ({
vehicle, view: "side", width: 800, height: 450, fit: "contain", trim: true, padding: 6, format: "auto",
});
// How long the lot's URL lives, decided once from the sale's close.
function lifetime(closesAt: Date) {
const seconds = secondsUntil(closesAt);
return seconds <= 604800
? { ttlSeconds: Math.max(seconds, 60) } // closes within the week
: { ttlSeconds: 604800, renew: true, renewDays: Math.min(daysUntil(closesAt), 365) }; // a longer timed sale
}
// At intake, once per lot. The lifetime is stored with the lot, so a retry sends
// the same body and replays the first answer instead of minting a second URL.
lot.imageLifetime ??= lifetime(sale.closesAt);
const { data: [placeholder] } = await client.createImageUrls(lotImage(lot.vehicleId), {
...lot.imageLifetime,
idempotencyKey: `${sale.id}:lot-${lot.number}`,
});
lot.placeholderUrl = placeholder.url; // replaced by the lot's own photos when they arriveخطِّط بحسب حجم المزاد
كل بند مركبة مختلفة ما لم يشترك بندان في الشركة المصنّعة والطراز والسنة، وتضع كل خطة حدًا أقصى لعدد المركبات المختلفة شهريًا: Free 100، Pro 2,500 وBusiness 15,000؛ لا حدّ أقصى في Enterprise. المركبة التي خُدمت هذا الشهر مسموح بها دائمًا، والدفعة التي قد تتجاوز الحد تُرفض بالكامل قبل خصم أي شيء، مع ذكر الخطة والحد في الرد. اختر الخطة بحسب عدد البنود التي تعرضها شهريًا، لا بحسب الأرصدة. وتتناول قصة الأسواق الإلكترونية كيفية تنظيم استيراد كبير ضمن حدود المعدّل وحدود توليد الصور.
الأسئلة الشائعة
- هل يمكن إبقاء الصورة المُولّدة معروضة بعد وصول صور القطعة؟
- استبدلها. الصورة مجرد بديل مؤقت يمثّل سنة الطراز، ولا ينبغي أن يتساءل المزايد أبدًا أي صورة هي السيارة المبيعة. وإذا احتفظت صفحة بها كمرجع، فيجب أن ترافقها التسمية.
- ماذا يحدث لعنوان URL بعد البيع؟
- تنتهي صلاحيته عند انتهاء مدته، أو عندما يبلغ عنوان URL المتجدد نهاية فترة تجديده، وعندها يُرجع الخطأ 403. وبحلول ذلك الوقت يكون للبند المعروض صوره الخاصة أو نتيجته، وينبغي أن يعرض أرشيف المزاد الصور الفوتوغرافية. ويؤدي إلغاء المفتاح الذي أنشأ العناوين إلى إنهائها جميعًا دفعة واحدة.
- أعادت محاولةٌ مكرَّرة الرمز 422. لماذا؟
- عاد مفتاح Idempotency-Key نفسه بمحتوى طلب مختلف. السبب المعتاد هو مدة صلاحية أُعيد حسابها من الساعة لحظة إعادة المحاولة. حدّد مدة الصلاحية عند إنشاء القطعة واحفظها، وبذلك ترسل إعادة المحاولة المحتوى نفسه وتُعيد الاستجابة الأولى.
- هل يمكن أن تُظهر الصورة المُولّدة لون طلاء القطعة؟
- نعم: يقبل color أيًّا من أسماء الألوان المعدّة مسبقًا أو رمزًا سداسيًا عشريًا (hex)، لذا يمكن أن تطابق الصورة الطلاء المذكور في ورقة الإيداع دون أي تكلفة إضافية. لكنها تظل صورة الطراز بذلك اللون، لا السيارة المعروضة في المزاد، لذا تبقى التسمية التوضيحية.
- كم تكلّف عملية بيع تضم 400 قطعة؟
- 400 رصيد: 1 رصيد لكل قطعة عند إنشاء عنوان URL الخاص بها، ولا تُحتسب أي رسوم على إعادة محاولة الإنشاء. كل تحميل خلال مدة صلاحية عنوان URL مجاني. تحدد الخطط حدًا أقصى لعدد المركبات المختلفة شهريًا (Free 100، Pro 2,500 وBusiness 15,000؛ لا حدّ أقصى في Enterprise)، والقطع التي تشترك في الشركة المصنّعة والطراز وسنة الصنع تُحتسب مرة واحدة.