Push Integration API
REST API ที่ให้ระบบภายนอก (ERP, e-commerce, suppliers) push ข้อมูลเข้า WMS โดยตรง โดย authenticate ผ่าน API Key แทน JWT
Base URL:
https://api.example.com/api/v1/integration/v1Auth: ส่ง headerX-API-Key: <your-key>(หรือAuthorization: ApiKey <your-key>)
ภาพรวม
| หัวข้อ | รายละเอียด |
|---|---|
| Authentication | API Key (SHA-256 hashed at rest) |
| Identifier rules | อ้างถึง items ด้วย sku, partners ด้วย code, warehouses ด้วย warehouseCode — service จะ resolve เป็น UUID ให้ |
| Idempotency | Optional header Idempotency-Key (24h TTL) |
| Bulk endpoints | คืนค่า 207 Multi-Status เมื่อมีบางรายการ error |
| Tenant scoping | ผูก API Key กับ tenantId เพื่อจำกัดขอบเขตการมองเห็น |
1. Authentication
สร้าง API Key
เข้า Admin Portal → Settings → API Keys → กด + New Key หรือเรียก endpoint:
POST /api/v1/api-keys
Authorization: Bearer <admin-jwt>
Content-Type: application/json
{
"name": "ERP Integration",
"scopes": ["read", "write"],
"tenantId": "00000000-0000-0000-0000-000000000001",
"expiresAt": "2027-01-01T00:00:00.000Z"
}ระบบจะส่ง rawKey กลับมา ครั้งเดียว — เก็บไว้ดีๆ ขึ้นต้นด้วย wms_...
ใช้ใน request
curl -X POST https://api.example.com/api/v1/integration/v1/items \
-H "X-API-Key: wms_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ ... }'หรือใช้รูปแบบ Authorization:
curl ... -H "Authorization: ApiKey wms_xxxxxxxx..."Scopes
| Scope | สิทธิ์ |
|---|---|
read | เรียก GET endpoints (inquiry) |
write | POST/PUT (upsert master data, สร้าง ASN/GRN/orders) |
admin | super-scope — ผ่านทุก scope check |
2. Idempotency
ทุก POST endpoint รองรับ header Idempotency-Key:
curl -X POST .../integration/v1/orders \
-H "X-API-Key: wms_..." \
-H "Idempotency-Key: order-2026-06-15-abc123" \
-d '{ ... }'กฎ:
- ถ้าใช้ key เดิม + body เดิม ภายใน 24 ชม. → ระบบ replay response เดิม (มี header
Idempotent-Replay: true) - ถ้าใช้ key เดิมแต่ body ต่าง → ระบบตอบ
409 IDEMPOTENCY_KEY_MISMATCH - หลัง 24 ชม. → key หมดอายุ ระบบสร้าง request ใหม่
Best practice: ใช้ UUID หรือ external order number เป็น Idempotency-Key
3. Response Format
Success
{
"status": "success",
"data": { ... },
"requestId": "uuid-v4"
}Bulk (อาจเป็น success หรือ partial)
{
"status": "partial",
"total": 3,
"created": 2,
"updated": 0,
"errors": [
{ "index": 1, "key": "SKU-X", "error": "Item SKU \"SKU-X\" not found." }
],
"data": [
{ "index": 0, "key": "SKU-A", "status": "created", "data": { "id": "uuid", "sku": "SKU-A" } },
{ "index": 1, "key": "SKU-X", "status": "error", "error": "..." },
{ "index": 2, "key": "SKU-B", "status": "updated", "data": { "id": "uuid", "sku": "SKU-B" } }
],
"requestId": "uuid-v4"
}HTTP status: 200 ถ้าทุกรายการผ่าน, 207 ถ้ามี errors
Error
{
"status": "error",
"code": "VALIDATION_FAILED",
"message": "...",
"requestId": "uuid-v4"
}4. Endpoints
ทั้งหมดอยู่ใต้ prefix /api/v1/integration/v1
Master Data
POST /items — Upsert single item
curl -X POST .../integration/v1/items \
-H "X-API-Key: wms_..." \
-H "Content-Type: application/json" \
-d '{
"sku": "SKU-001",
"name": "น้ำดื่มตราสิงห์ 750ml",
"barcode": "8850000000001",
"baseUomCode": "EA",
"category": "Beverages",
"tracking": "lot",
"shelfLifeDays": 365,
"weightKg": 0.75
}'POST /items/bulk — Bulk upsert (max 500)
curl -X POST .../integration/v1/items/bulk \
-H "X-API-Key: wms_..." \
-d '{
"items": [
{ "sku": "SKU-001", "name": "Item A", "baseUomCode": "EA" },
{ "sku": "SKU-002", "name": "Item B", "baseUomCode": "BOX" }
]
}'POST /partners — Upsert partner
{
"code": "SUP-001",
"name": "บริษัท สยามเทรดดิ้ง จำกัด",
"type": "supplier",
"contact": { "phone": "081-234-5678" }
}POST /partners/bulk — Bulk upsert partners
{ "partners": [ {...}, {...} ] }POST /locations — Upsert location
{
"warehouseCode": "WH-BKK-01",
"code": "A-01-01-01",
"zone": "A",
"type": "storage",
"pickFace": true
}POST /locations/bulk — Bulk upsert locations
{ "locations": [ {...}, {...} ] }Inbound
POST /asn — Create ASN
curl -X POST .../integration/v1/asn \
-H "X-API-Key: wms_..." \
-H "Idempotency-Key: po-2026-001" \
-d '{
"warehouseCode": "WH-BKK-01",
"supplierCode": "SUP-001",
"expectedDate": "2026-06-15",
"referenceNo": "PO-2026-001",
"lines": [
{ "lineNo": 1, "sku": "SKU-001", "uomCode": "CASE", "expectedQty": 50 },
{ "lineNo": 2, "sku": "SKU-002", "uomCode": "EA", "expectedQty": 200, "lotNo": "LOT-A", "expiryDate": "2027-12-31" }
]
}'POST /asn/bulk — Multiple ASNs (max 100)
{ "asns": [ {...}, {...} ] }POST /grn — Create GRN
{
"warehouseCode": "WH-BKK-01",
"asnNo": "ASN-20260615-0001",
"lines": [
{ "asnLineNo": 1, "sku": "SKU-001", "uomCode": "CASE", "qty": 50 }
]
}ถ้าระบุ
asnNo: ระบบจะอัปเดตreceivedQtyของแต่ละ ASN line และย้าย ASN ไปสถานะreceivingอัตโนมัติ
Outbound
POST /orders — Create sales order
curl -X POST .../integration/v1/orders \
-H "X-API-Key: wms_..." \
-H "Idempotency-Key: ext-order-12345" \
-d '{
"warehouseCode": "WH-BKK-01",
"customerCode": "CUS-001",
"requiredDate": "2026-06-20",
"priority": 3,
"shipTo": { "name": "สมชาย", "address": "123 ถ.สุขุมวิท" },
"referenceNo": "EXT-ORDER-12345",
"lines": [
{ "lineNo": 1, "sku": "SKU-001", "uomCode": "EA", "qtyOrdered": 10 }
]
}'POST /orders/bulk — Multiple orders (max 100)
POST /orders/{orderNo}/cancel — Cancel by order number
curl -X POST .../integration/v1/orders/SO-20260615-0042/cancel \
-H "X-API-Key: wms_..."ปล่อย reservations และ allocated qty อัตโนมัติ ถ้ายังไม่ shipped/completed
Inquiry (read-only)
GET /orders/{orderNo}
ตอบกลับ order + lines + pickTasks + summary (totalOrdered/Allocated/Picked/Shipped)
GET /asn/{asnNo}
ตอบกลับ ASN + lines พร้อม receivedQty + รายการ GRNs ที่ผูกอยู่
GET /stock?sku=SKU-001&warehouseCode=WH-BKK-01[&lotNo=LOT-A]
{
"status": "success",
"data": {
"sku": "SKU-001",
"itemName": "...",
"tracking": "lot",
"totalOnHand": 1250,
"totalAllocated": 300,
"totalAvailable": 950,
"stocks": [
{ "warehouseCode": "WH-BKK-01", "locationCode": "A-01-01-01", "lotNo": "LOT-A", "qtyOnHand": 500, "qtyAllocated": 100, "qtyAvailable": 400 }
]
}
}5. Error Codes
| HTTP | Code / Pattern | สาเหตุที่พบบ่อย |
|---|---|---|
| 400 | VALIDATION_FAILED | DTO ไม่ผ่าน class-validator |
| 400 | Item SKU "X" not found. | SKU ที่อ้างไม่มีใน tenant |
| 400 | Warehouse code "X" not found... | warehouseCode ผิด |
| 400 | tenantId is required. | API Key ไม่ผูกกับ tenant และ DTO ไม่ส่ง tenantId มา |
| 401 | Missing API key | ไม่มี header |
| 401 | Invalid, revoked, or expired API key | key ไม่ถูกต้องหรือถูก revoke |
| 401 | API key missing required scope(s) | scope ไม่พอ |
| 404 | Order/ASN "X" not found. | ค้นหาด้วย number แล้วไม่เจอ |
| 207 | (multi-status) | bulk operation มีบางรายการ error |
| 409 | IDEMPOTENCY_KEY_MISMATCH | ส่ง Idempotency-Key เดิมแต่ body ต่าง |
6. Webhook Events (ส่งกลับให้ external systems)
ระบบ WMS รองรับ webhook ผ่าน WebhookSubscription — register ผ่าน Admin Portal แล้วเราจะส่ง POST ไปที่ URL ของคุณเมื่อเหตุการณ์ต่อไปนี้เกิดขึ้น:
| Event | Trigger |
|---|---|
asn.received | GRN ถูกสร้างต่อ ASN |
asn.completed | ASN รับครบทุก line |
order.allocated | Stock ถูก allocate ให้ order |
order.released | Order ถูก release ออกเป็น pick tasks |
order.shipped | Shipment ออกจากคลัง |
order.cancelled | Order ถูก cancel |
stock.adjusted | มี stock adjustment |
cyclecount.variance | นับสต๊อกพบความต่าง |
Payload format:
{
"event": "order.shipped",
"timestamp": "2026-06-15T10:30:00.000Z",
"data": { "orderNo": "SO-...", "shipmentNo": "SHIP-...", "trackingNo": "TH123..." }
}7. Limits
| ทรัพยากร | จำกัด |
|---|---|
| Bulk items / partners / locations | 500 รายการ / request |
| Bulk ASNs / orders | 100 รายการ / request |
| ขนาด request body | 1 MB |
| Idempotency-Key TTL | 24 ชั่วโมง |
ติดต่อ admin ถ้าต้องการ rate-limit หรือ batch ขนาดใหญ่กว่านี้
8. Security & Rate Limiting
แต่ละ API Key สามารถตั้งค่าได้ใน Admin Portal → API Keys
IP Whitelist
- กำหนดได้ทั้ง exact IP (เช่น
203.0.113.5) และ CIDR (เช่น203.0.113.0/24) - ระบบดูจาก
X-Forwarded-For(header แรก) เมื่อ deploy ผ่าน proxy เช่น Render - ถ้า list ว่าง → อนุญาตทุก IP
- ถ้า request มาจาก IP นอก list → ตอบกลับ 403 Forbidden
{
"statusCode": 403,
"message": "Client IP \"198.51.100.7\" is not in this API key's whitelist."
}Allowed Origins (CORS)
- ใช้สำหรับ key ที่อนุญาตให้เรียกจากเบราว์เซอร์เท่านั้น
- ระบบเช็ค
Originheader ของ request - ถ้า header
Originไม่มี (เช่น server-to-server) → อนุญาตเสมอ - ถ้ามี
Originแต่ไม่อยู่ใน list → ตอบกลับ 403 Forbidden
Rate Limit
- กำหนดเป็น requests per minute ต่อ key
- ตั้ง
0หรือเว้นว่าง = ไม่จำกัด - ใช้ sliding window 60 วินาที (in-memory)
- เมื่อเกิน → ตอบกลับ 429 Too Many Requests พร้อม header
Retry-After
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
{
"statusCode": 429,
"message": "Rate limit exceeded for API key (60 req/min). Retry in 23s.",
"error": "Too Many Requests",
"retryAfter": 23
}Response header ที่ส่งกลับทุก request เมื่อ key มี rate limit:
| Header | ความหมาย |
|---|---|
X-RateLimit-Limit | จำนวน req/min ที่อนุญาต |
X-RateLimit-Remaining | จำนวน req คงเหลือใน window ปัจจุบัน |
Retry-After | (เฉพาะตอน 429) วินาทีที่ต้องรอ |
Request Logging
ทุก request ผ่าน guard จะถูกบันทึกใน api_key_request (async หลัง response):
- endpoint, method, IP, origin
- status code, response time
- ใช้สำหรับ stats / audit ใน Admin Portal
Best practices
- Production keys ควรมี IP whitelist เสมอ — ลดความเสียหายถ้า key รั่ว
- Rate limit ตามจริง — ตั้งให้สูงพอแต่ป้องกัน abuse (เช่น 120/min สำหรับ ERP)
- Allowed Origins ตั้งเฉพาะ key สำหรับเบราว์เซอร์ — server-to-server ไม่ต้อง
- Rotate ทุก 90 วัน — สร้าง key ใหม่ แล้วค่อย revoke ของเก่าหลัง verify
- แยก key ต่อ integration — ปิด/หมุนได้ทีละตัวโดยไม่กระทบระบบอื่น