คู่มือการสร้างที่เขียนจากพฤติกรรมของ API ตามเอกสาร โดยไม่ได้ระบุชื่อลูกค้ารายใด เผยแพร่เมื่อ
ปัญหา: การเช่ารถคือการเช่าตามประเภทรถ ไม่ใช่รถคันใดคันหนึ่ง
ลูกค้าเช่ารถจะจองตามประเภทรถ เช่น รถคอมแพกต์ SUV ขนาดกลาง หรือรถมินิแวน แล้วขับออกไปด้วยรถคันใดก็ได้ในประเภทนั้นที่มีอยู่ในลานจอด หน้าการจองยังคงต้องมีภาพสำหรับทุกประเภท และธรรมเนียมคือใช้รุ่นตัวอย่างหนึ่งรุ่นพร้อมคำว่า "หรือเทียบเท่า"
รูปภาพเหล่านั้นมักมาจากที่ใดก็ตามที่หาได้: ภาพประชาสัมพันธ์จากที่หนึ่ง ภาพจากดีลเลอร์จากอีกที่หนึ่ง มุมกล้องต่างกัน ฉากหลังต่างกัน และรถที่กินพื้นที่การ์ดไม่เท่ากัน กริดที่ประกอบขึ้นแบบนั้นจะดูปะติดปะต่อ สิ่งที่หน้าแสดงรถในฟลีตต้องการคือสิ่งตรงกันข้าม: ทุกคลาสถูกนำเสนอในแบบเดียวกัน เพื่อให้สิ่งเดียวที่เปลี่ยนไปในแต่ละการ์ดคือตัวรถ
การผสานรวมมีลักษณะอย่างไร
- รุ่นตัวแทนหนึ่งรุ่นต่อกลุ่มรถ เก็บไว้เป็น vehicle id ในแค็ตตาล็อก โดยใส่ "หรือเทียบเท่า" ไว้ในป้ายกำกับ และไม่ใส่ไว้ในภาพเด็ดขาด
- รูปแบบคำขอเดียวสำหรับทุกการ์ด: ใช้มุมมอง กรอบ การปรับพอดี การครอป ระยะขอบ สี ฉากหลัง และรูปแบบไฟล์เดียวกัน
- ไม่เกินสองภาพต่อประเภทรถ: การ์ดในกริดและภาพหลักบนหน้าประเภทรถ
- แคชไว้ เพราะกริดเหมือนกันสำหรับผู้เข้าชมทุกคน: ไฟล์บน CDN ของคุณ หรือ signed URL ที่สร้างครั้งเดียวแล้วนำกลับมาใช้ซ้ำ
ขั้นตอนที่ 1: เลือกรุ่นตัวแทนของแต่ละคลาสด้วย id
เลือกรุ่นที่ใช้เป็นตัวแทนในการให้เช่าของรถแต่ละกลุ่ม แล้วค้นหารุ่นปีปัจจุบันของรุ่นนั้น GET /api/v1/vehicles?make=&model= ใช้งานได้ฟรีและเป็นสาธารณะ โดยจะแสดงทุกปีของรุ่นนั้น พร้อม vehicle id ที่คงที่ซึ่งคุณจะจัดเก็บไว้ ส่วน POST /api/v1/images/resolve ทำงานเดียวกันจากวลี เช่น "2026 toyota rav4"
# one model's years, each with its vehicle id (free, public)
curl -s "https://carimage.dev/api/v1/vehicles?make=toyota&model=rav4" | jq -c '.data.vehicles[] | select(.year == 2026)'
# {"year":2026,"id":"veh_e5w75c93f6cvk"}// One representative model per class: its current model year, by catalog id.
// "or similar" lives in the label; the picture never promises the car on the lot.
export const CLASS_IMAGES = {
compact: { vehicle: "veh_fy7r2yvwt4p1v", label: "Toyota Corolla or similar" }, // 2026 Toyota Corolla
"midsize-suv": { vehicle: "veh_e5w75c93f6cvk", label: "Toyota RAV4 or similar" }, // 2026 Toyota RAV4
"fullsize-suv": { vehicle: "veh_9mzh9ejqjye43", label: "Chevrolet Tahoe or similar" }, // 2026 Chevrolet Tahoe
minivan: { vehicle: "veh_9jck7rrybf35p", label: "Chrysler Pacifica or similar" }, // 2026 Chrysler Pacifica
};id ไม่มีวันเปลี่ยน การแมปนี้จึงเป็นจุดเดียวที่ได้รับผลกระทบเมื่อเปลี่ยนรถในฟลีต: เมื่อคลาสขนาดกลางเปลี่ยนไปใช้รุ่นอื่นหรือปีรุ่นถัดไป ก็แค่แทนที่ id เดียว แล้วการ์ด ภาพหลัก และอีเมลทุกรายการที่อ่านจากการแมปนี้จะเปลี่ยนตาม
ขั้นตอนที่ 2: รูปแบบคำขอเดียวสำหรับทุกคลาส
มุมกล้องทั้ง 9 มุมจัดเฟรมเหมือนกันสำหรับรถทุกคันในแคตตาล็อก ซึ่งทำให้กริดเรียงได้ตรงกัน ส่วนที่เหลือของการ์ดคือพารามิเตอร์เพียงไม่กี่ตัวที่กำหนดเพียงครั้งเดียว:
view=sideสำหรับการ์ด: รถทุกคันหันไปทางเดียวกันบนเส้นฐานเดียวกัน มุมด้านซ้ายแต่ละมุมมีมุมคู่แบบ-right(side-right) เพื่อให้การ์ดหันเข้าหาเลย์เอาต์ได้ ส่วนหน้าของแต่ละประเภทรถใช้front-3-4ซึ่งเป็นมุมภาพหลักw=640&h=320&fit=contain: ขนาด 640×320 พอดีสำหรับทุกประเภทรถ และมองเห็นรถได้ทั้งคัน ส่วนหัวX-Image-WidthและX-Image-Heightในการตอบกลับจะยืนยันขนาดแคนวาสtrim=1&padding=6: ครอปให้เหลือเฉพาะตัวรถก่อนปรับขนาด โดยเว้นระยะขอบ 6% ของด้านที่ยาวกว่า เพื่อให้ Corolla และ Tahoe เต็มการ์ดในลักษณะเดียวกัน แทนที่จะลอยอยู่ในเฟรมต้นฉบับทรงสี่เหลี่ยมจัตุรัสbackground=f4f4f4จะทำให้การ์ดกลืนไปกับสีเทาของกริด หากไม่ใส่ไว้ พื้นหลังจะยังคงโปร่งใสและสีของการ์ดจะแสดงผ่านออกมาformat=webpสำหรับไฟล์ที่จัดเก็บไว้ และformat=autoบน signed URL ซึ่งจะส่ง WebP หรือ PNG ให้แต่ละเบราว์เซอร์ตามที่รองรับ
# the class card: the same box, view, crop, paint and backdrop for every class
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
"https://carimage.dev/api/v1/images/car?vehicle=veh_e5w75c93f6cvk&view=side&color=white&w=640&h=320&fit=contain&trim=1&padding=6&background=f4f4f4&format=webp" \
-o midsize-suv-card.webp
# the class page's hero: the three-quarter view, the full width the API delivers
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
"https://carimage.dev/api/v1/images/car?vehicle=veh_e5w75c93f6cvk&view=front-3-4&color=white&w=1024&format=webp" \
-o midsize-suv-hero.webpขั้นตอนที่ 3: สีที่มีชื่อเรียกเพียงสีเดียวสำหรับทั้งกริด
ค่าสีที่กำหนดไว้ล่วงหน้า 15 ค่าเป็นชื่อสีรถ: white, black, gray, silver, blue, red, green, brown, beige, tan, orange, yellow, gold, burgundy, purple ค่าเริ่มต้นคือ silver และ grey จะถูกอ่านเป็น gray เลือกสีเดียวสำหรับรถทุกประเภทเพื่อให้การ์ดดูเป็นชุดเดียวกัน ตัวอย่างด้านบนใช้ white ค่าสีที่กำหนดไว้ล่วงหน้าแต่ละค่าใช้แถบสีรถของตนเอง ไม่ใช่สี CSS ที่มีชื่อเดียวกัน (red คือ #b3151b) และทุกสี รวมถึงค่า hex คือการเปลี่ยนสีจากมุมมองที่เตรียมไว้หนึ่งมุมมอง ดังนั้นแต่ละสีจึงเท่ากับภาพรถหนึ่งภาพ
ขั้นตอนที่ 4: แคชไฟล์หรือ URL
ตารางประเภทรถเหมือนกันสำหรับผู้เข้าชมทุกคน จึงควรมีค่าใช้จ่ายต่อการเข้าชมแทบเป็นศูนย์ ซึ่งทำได้สองวิธี:
- จัดเก็บไฟล์ไว้ ดึงภาพการ์ดและภาพหลัก (hero) แต่ละภาพเพียงครั้งเดียวด้วย
GET /api/v1/images/carแล้วให้บริการจาก CDN ของคุณเหมือนไฟล์อื่น ๆ แพ็กเกจแบบชำระเงินอนุญาตให้เก็บสำเนาบนเซิร์ฟเวอร์ของคุณเองเพื่อใช้ในผลิตภัณฑ์ตลอดที่แพ็กเกจยังใช้งานอยู่ (ลบภายใน 30 วันหลังแพ็กเกจสิ้นสุด หรือซื้อ Image Pack เพื่อเก็บชุดภาพไว้อย่างถาวร) เก็บETagของแต่ละไฟล์ไว้ด้วย การตรวจสอบซ้ำทุกคืนด้วยIf-None-Matchจะได้รับ304และไม่มีค่าใช้จ่าย สำหรับกลุ่มรถข้างต้น จะใช้ภาพรถยนต์ 8 ภาพของเดือน ซึ่งนำกลับมาใช้ซ้ำได้บ่อยเท่าที่ต้องการ - หรือสร้าง signed URL เพียงครั้งเดียวแล้วนำกลับมาใช้ซ้ำ
POST /api/v1/image-urlsรับได้สูงสุด 50 ภาพต่อการเรียกหนึ่งครั้ง และส่งคืน URL ที่ไม่มีคีย์ซึ่งมีอายุได้นานสูงสุด 7 วัน URL ที่ใช้ได้ไม่จำกัดครั้งสามารถแคชแบบสาธารณะได้ตลอดอายุของมัน ดังนั้นเมื่อผู้เข้าชมทุกคนโหลด URL เดียวกัน การโหลดซ้ำจะมาจาก CDN โดยไม่มีค่าใช้จ่าย เพิ่มrenew: trueแล้ว URL ที่จัดเก็บไว้จะยังใช้งานได้ต่อหลังหมดอายุ การต่ออายุจะนับภาพหนึ่งครั้งในเดือนที่เกิดการต่ออายุ และไม่นับเพิ่มหากภาพนั้นถูกนับไปแล้วในเดือนนั้น ดังนั้นฟลีตรถข้างต้นจะอยู่ที่ไม่เกิน ภาพรถ 8 ภาพ ต่อเดือน
จำกัดคีย์ที่ใช้สร้าง URL ให้ใช้ได้เฉพาะโดเมนการจองของคุณในแดชบอร์ด แล้ว URL ที่ถูกคัดลอกไปไว้บนเว็บไซต์อื่นจะตอบกลับด้วย 403
import { CarImageClient, type ImageParams } from "@meterapp/car-image-sdk";
import { CLASS_IMAGES } from "./fleet";
const client = new CarImageClient({ apiKey: process.env.CAR_IMAGE_API_KEY });
// Framed once, for every class.
const card = (vehicle: string): ImageParams => ({
vehicle, view: "side", color: "white", width: 640, height: 320,
fit: "contain", trim: true, padding: 6, background: "#f4f4f4", format: "auto",
});
const hero = (vehicle: string): ImageParams => ({ vehicle, view: "front-3-4", color: "white", width: 1024, format: "auto" });
// Every visitor sees the same grid: mint the URLs once and serve the stored ones.
// renew keeps each URL working past its 7-day lifetime, one credit per image
// for each further week in which it is loaded.
const classes = Object.entries(CLASS_IMAGES);
const { data } = await client.createImageUrls(
classes.flatMap(([, entry]) => [card(entry.vehicle), hero(entry.vehicle)]),
{ ttlSeconds: 604800, renew: true, idempotencyKey: "class-grid-2026-10" }
);
await saveClassImages(
classes.map(([key], index) => ({ key, card: data[2 * index].url, hero: data[2 * index + 1].url, renewsUntil: data[2 * index].renews_until }))
);ขั้นตอนที่ 5: การยืนยันการจอง
อีเมลยืนยันจะแสดงคลาสที่จองไว้ และผู้เช่าจะเปิดอีเมลเมื่อใดก็ได้ที่สะดวก ให้สร้างภาพนั้นเป็น URL แบบต่ออายุได้: ttl_seconds ที่ค่าสูงสุด 7 วัน, renew: true และ renew_days ที่ครอบคลุมเลยวันคืนรถ สัปดาห์ที่ไม่มีใครเปิดอีเมลจะไม่มีค่าใช้จ่าย และหลังสิ้นสุดการเช่า URL จะหยุดต่ออายุ การใช้ Idempotency-Key หนึ่งค่าต่อการจองจะทำให้การส่งซ้ำได้ URL เดิมกลับมาแทนการสร้าง URL ใหม่ และการเก็บ URL ไว้กับข้อมูลการจองช่วยให้การส่งซ้ำทุกครั้งในภายหลังใช้ URL เดิมได้ อีเมลตามวงจรชีวิตลูกค้าของเราเองก็ทำงานแบบนี้ เรื่องราวเกี่ยวกับอีเมลมีรายละเอียดของกลไกและ HTML
// The booking confirmation: opened today, again at the counter, maybe never.
const { data: [classImage] } = await client.createImageUrls(card(booking.classVehicle), {
ttlSeconds: 604800,
renew: true,
// A week past the return date, and no renewals after it (365 days at most).
renewDays: Math.min(daysUntil(booking.returnDate) + 7, 365),
// A retried send replays the first URL instead of minting a second one.
idempotencyKey: `booking:${booking.id}:class`,
});
booking.classImageUrl = classImage.url; // later resends reuse itCLI, SDK และ MCP
- CLI:
npx @meterapp/car-image get --vehicle veh_… --view side --width 640 --height 320 --fit contain --trim --padding 6 --out card.webpเขียนการ์ดลงไฟล์ ส่วนcar-image url --batch classes.json --renewสร้าง URL เป็นชุด - SDK:
client.vehicles({ make, model })สำหรับ id,client.getImage(…)สำหรับไฟล์ และclient.createImageUrls(…)สำหรับ URL - MCP:
resolve_vehicleและcreate_car_image_urlsช่วยให้ผู้ช่วย AI ร่างการ์ดประเภทรถจากคำว่า "SUV ขนาดกลาง, RAV4 หรือรุ่นใกล้เคียง" ได้ ดูวิธีตั้งค่าได้ที่หน้า MCP
คำถามที่พบบ่อย
- การ์ดสามารถแสดงรถคันที่ผู้เช่าจะได้รับจริงได้หรือไม่
- ไม่ได้ และไม่ควรพยายามด้วย รถแต่ละคลาสให้เช่าแบบ “หรือรุ่นใกล้เคียง” และภาพเรนเดอร์แสดงยี่ห้อ รุ่น และปีจากแค็ตตาล็อก ไม่ใช่รถที่อยู่ในลานจอดของคุณ ให้แสดงรุ่นตัวแทนหนึ่งรุ่นพร้อมคำว่า “หรือรุ่นใกล้เคียง” ในป้ายกำกับ เมื่อมีการกำหนดรถคันที่แน่นอนที่เคาน์เตอร์แล้ว คุณสามารถถอดรหัส VIN ของรถคันนั้นได้ฟรีเพื่อหารถที่ตรงกันในแค็ตตาล็อก หากต้องการแสดงรุ่นนั้นแทน
- ทำไมรถในกริดจึงไม่ได้แสดงตามสัดส่วนขนาดจริง
- เพราะ trim จะครอปภาพเรนเดอร์แต่ละภาพให้พอดีกับตัวรถ และ fit=contain จะขยายรถให้เต็มการ์ด ดังนั้นรถคอมแพกต์และ SUV ขนาดใหญ่จึงเต็มการ์ดในลักษณะเดียวกัน นี่คือสิ่งที่ทำให้กริดดูเป็นระเบียบ ให้บอกขนาดเป็นข้อความแทน: ชื่อคลาส จำนวนที่นั่ง และจำนวนกระเป๋า
- กริดแสดงคลาสรถมีค่าใช้จ่ายเท่าใด
- ฟลีตรถ 4 ประเภท ที่แต่ละประเภทมีภาพการ์ดและภาพฮีโร่ จะใช้ ภาพรถ 8 ภาพ ต่อเดือน โดยแต่ละภาพส่งมอบได้บ่อยเท่าที่ต้องการในทุกขนาด หากจัดเก็บไฟล์ไว้บน CDN ของคุณขณะที่แพ็กเกจแบบชำระเงินยังใช้งานอยู่ ภาพจะถูกนับเฉพาะในเดือนที่คุณดึงภาพ พร้อมการตรวจสอบซ้ำด้วย ETag ฟรี หากใช้ Signed URL แบบต่ออายุแทน ภาพเดียวกันจะถูกนับหนึ่งครั้งในแต่ละเดือนที่มีการเปิดหน้าเว็บ
- ตารางรถควรใช้สีใด
- ใช้สีพรีเซ็ตเดียวกันสำหรับทุกคลาส เพื่อให้การ์ดดูเป็นชุดเดียวกัน สีพรีเซ็ตทั้ง 15 สีได้แก่ white, black, gray, silver, blue, red, green, brown, beige, tan, orange, yellow, gold, burgundy, purple และค่าเริ่มต้นคือ silver คุณใช้รหัส hex ใดก็ได้เช่นกันในราคาเดียวกัน ผู้เช่าจะได้รถสีที่มีอยู่ในลานจอด ซึ่งการ์ดไม่เคยรับประกันไว้
- หากรุ่นรถของประเภทรถเช่าไม่มีในแค็ตตาล็อกจะทำอย่างไร
- หากไม่มีรถคันนั้นในแคตตาล็อก ระบบจะตอบกลับเป็น 404 พร้อมคำแนะนำ ได้แก่ รถในแคตตาล็อกที่ใกล้เคียงที่สุดพร้อม id ของแต่ละคัน ให้ใช้หนึ่งในนั้น หรือขอเพิ่มรถบนบอร์ดคำขอสาธารณะ แคตตาล็อกนี้เป็นโอเพนซอร์ส