Overview
LOZY akan mengirim HTTP POST request ke endpoint yang kamu daftarkan setiap kali ada event Order atau ASN (Advanced Shipping Notice) di sistem kami.
How It Works
┌──────────┐ ┌──────────────┐ ┌──────────────┐
│ LOZY │──POST──▶│ Your Server │──200 OK─▶│ LOZY marks │
│ System │ │ /webhook │ │ as SUCCESS │
└──────────┘ └──────────────┘ └──────────────┘
│ │
│ (if non-2xx) │
▼ │
┌──────────────┐ │
│ Auto Retry │────────────┘
│ 30s/2m/8m │
└──────────────┘
Your Endpoint Requirements
| URL | Publicly accessible HTTPS URL |
| Method | Harus menerima POST |
| Timeout | Respond dalam 10 detik (jangan proses lama di sini) |
| Response | Return HTTP 200 dengan JSON body (lihat section Response) |
| Idempotent | Handle duplikat — gunakan X-Webhook-Delivery-Id untuk dedup |
200 OK segera, lalu proses data secara async (queue/background job). Jangan proses berat di dalam handler webhook.
Request Headers
Setiap webhook request dari LOZY akan menyertakan headers berikut:
| Header | Contoh | Deskripsi |
|---|---|---|
Content-Type | application/json | Format body |
X-Webhook-Event | order.created | Tipe event |
X-Webhook-Signature | a1b2c3...hex | HMAC-SHA256 signature |
X-Webhook-Delivery-Id | 12345 | Unique ID (untuk dedup) |
X-Webhook-Timestamp | 1700000000 | Unix timestamp |
User-Agent | LOZY-Webhook/1.0 | Identifier |
Order Events
Payload: order.created
{
"event": "order.created",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"order_id": "ORD-2024-00123",
"marketplace": "shopee",
"marketplace_order_id": "240115ABCDEF",
"status": "new",
"customer": {
"name": "John Doe",
"phone": "08123456789",
"address": {
"street": "Jl. Sudirman No. 1",
"city": "Jakarta Selatan",
"province": "DKI Jakarta",
"postal_code": "12190"
}
},
"items": [
{
"sku": "SKU-001",
"name": "Kaos Polos Hitam L",
"qty": 2,
"price": 75000,
"total": 150000
}
],
"shipping": {
"courier": "JNE",
"service": "REG",
"cost": 15000
},
"payment": {
"method": "COD",
"total_amount": 165000,
"currency": "IDR"
},
"created_at": "2024-01-15T10:30:00Z"
}
}
Payload: order.updated
{
"event": "order.updated",
"timestamp": "2024-01-15T14:00:00Z",
"data": {
"order_id": "ORD-2024-00123",
"marketplace_order_id": "240115ABCDEF",
"previous_status": "new",
"status": "processing",
"updated_fields": ["status"],
"updated_at": "2024-01-15T14:00:00Z"
}
}
Payload: order.cancelled
{
"event": "order.cancelled",
"timestamp": "2024-01-15T16:00:00Z",
"data": {
"order_id": "ORD-2024-00123",
"marketplace_order_id": "240115ABCDEF",
"reason": "Customer requested cancellation",
"cancelled_by": "customer",
"cancelled_at": "2024-01-15T16:00:00Z"
}
}
ASN Events (Advanced Shipping Notice)
Payload: asn.created
{
"event": "asn.created",
"timestamp": "2024-01-10T08:00:00Z",
"data": {
"asn_id": "ASN-2024-00045",
"reference_number": "PO-2024-100",
"warehouse": {
"code": "WH-JKT-01",
"name": "Warehouse Jakarta"
},
"supplier": {
"code": "SUP-001",
"name": "PT Supplier Utama"
},
"expected_arrival": "2024-01-15",
"items": [
{
"sku": "SKU-001",
"name": "Kaos Polos Hitam L",
"qty_expected": 100,
"unit": "pcs"
},
{
"sku": "SKU-002",
"name": "Kaos Polos Putih M",
"qty_expected": 50,
"unit": "pcs"
}
],
"notes": "Batch produksi Januari",
"created_at": "2024-01-10T08:00:00Z"
}
}
Payload: asn.shipped
{
"event": "asn.shipped",
"timestamp": "2024-01-12T14:00:00Z",
"data": {
"asn_id": "ASN-2024-00045",
"reference_number": "PO-2024-100",
"tracking": {
"courier": "JNE",
"tracking_number": "JNE1234567890",
"estimated_arrival": "2024-01-15T10:00:00Z"
},
"shipped_at": "2024-01-12T14:00:00Z"
}
}
Payload: asn.delivered
{
"event": "asn.delivered",
"timestamp": "2024-01-15T09:30:00Z",
"data": {
"asn_id": "ASN-2024-00045",
"reference_number": "PO-2024-100",
"received_by": "Ahmad (Staff Gudang)",
"items_received": [
{"sku": "SKU-001", "qty_expected": 100, "qty_received": 98, "qty_damaged": 2},
{"sku": "SKU-002", "qty_expected": 50, "qty_received": 50, "qty_damaged": 0}
],
"delivered_at": "2024-01-15T09:30:00Z"
}
}
Response Contract
Success Response
Respond dengan HTTP 200 dan body JSON berikut:
HTTP/1.1 200 OK
Content-Type: application/json
{
"success": true,
"message": "Webhook received"
}
| Field | Type | Required | Deskripsi |
|---|---|---|---|
success | boolean | Yes | Harus true jika berhasil diterima |
message | string | No | Pesan opsional |
- HTTP status code
2xx(200, 201, 202, dll) - Response diterima dalam 10 detik
Error / Rejection Response
Jika kamu ingin menolak webhook (misal: data tidak valid), respond:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"success": false,
"message": "Invalid order_id format"
}
200 dengan "success": false agar kami tidak retry.
Response yang TIDAK akan di-retry:
HTTP/1.1 200 OK
{
"success": false,
"message": "Duplicate event, already processed"
}
Response yang AKAN di-retry:
HTTP/1.1 500 Internal Server Error
// atau timeout (tidak respond dalam 10 detik)
// atau connection refused
Retry Mechanism
Jika endpoint kamu gagal (non-2xx atau timeout), kami akan otomatis retry:
| Attempt | Delay | Keterangan |
|---|---|---|
| 1st (initial) | Immediately | Pengiriman pertama |
| 2nd (retry 1) | 30 seconds | Retry pertama |
| 3rd (retry 2) | 2 minutes | Retry kedua |
| 4th (retry 3) | 8 minutes | Retry terakhir |
Setelah semua retry gagal, delivery ditandai failed. Tim LOZY bisa trigger manual retry jika diperlukan.
Verify Signature
Setiap request ditandatangani dengan HMAC-SHA256. Gunakan secret_key yang diberikan LOZY untuk verifikasi.
Signature di-generate dari raw request body (string JSON persis seperti yang dikirim).
Node.js / Express
const crypto = require('crypto');
const express = require('express');
const app = express();
// PENTING: capture raw body sebelum JSON.parse
app.use('/webhook', express.json({
verify: (req, res, buf) => { req.rawBody = buf.toString(); }
}));
app.post('/webhook', (req, res) => {
const SECRET_KEY = 'secret_key_dari_lozy';
const signature = req.headers['x-webhook-signature'];
// Hitung signature
const expected = crypto
.createHmac('sha256', SECRET_KEY)
.update(req.rawBody)
.digest('hex');
// Bandingkan (timing-safe)
const isValid = crypto.timingSafeEqual(
Buffer.from(signature || ''),
Buffer.from(expected)
);
if (!isValid) {
return res.status(401).json({ success: false, message: 'Invalid signature' });
}
// ✅ Signature valid — proses event
const event = req.headers['x-webhook-event'];
const deliveryId = req.headers['x-webhook-delivery-id'];
console.log(`[${event}] Delivery #${deliveryId}:`, req.body);
// Respond 200 segera
res.json({ success: true, message: 'Webhook received' });
});
PHP (Laravel)
Route::post('/webhook', function (Request $request) {
$secretKey = 'secret_key_dari_lozy';
$payload = $request->getContent();
$signature = $request->header('X-Webhook-Signature');
$expected = hash_hmac('sha256', $payload, $secretKey);
if (!hash_equals($expected, $signature)) {
return response()->json(['success' => false, 'message' => 'Invalid signature'], 401);
}
$event = $request->header('X-Webhook-Event');
$data = $request->json()->all();
// Process...
return response()->json(['success' => true, 'message' => 'Webhook received']);
});
Register Your Endpoint
Sebelum bisa menerima webhook, kamu perlu mendaftarkan endpoint URL kamu ke sistem LOZY. Hubungi tim LOZY atau gunakan form di bawah.
Yang perlu kamu siapkan:
| Info | Contoh | Keterangan |
|---|---|---|
| Nama | WMS Client A | Nama identifikasi kamu |
| Webhook URL | https://yourserver.com/webhook | URL yang akan menerima POST request |
| Events | order.created, asn.shipped | Event apa saja yang mau di-subscribe |
Register via API
POST http://webhook.lozy.co.id/api/clients
{
"name": "Your Company Name",
"url": "https://yourserver.com/webhook/lozy",
"subscribed_events": ["order.created", "order.updated", "asn.created", "asn.shipped", "asn.delivered"]
}
Response
{
"success": true,
"data": {
"id": 1,
"name": "Your Company Name",
"url": "https://yourserver.com/webhook/lozy",
"secret_key": "a1b2c3d4e5f6...64_karakter_hex",
"subscribed_events": ["order.created", "order.updated", "asn.created", "asn.shipped", "asn.delivered"],
"is_active": true
},
"message": "Client created. Save the secret_key — it will be used to sign webhook payloads."
}
secret_key — ini digunakan untuk verifikasi signature di setiap webhook yang kamu terima. Tidak bisa dilihat lagi setelah ini (hanya bisa di-regenerate).
pending_approval. Webhook belum akan dikirim sampai tim LOZY meng-approve registrasi kamu. Kamu akan dihubungi setelah di-approve.
Register via Form
Test Your Endpoint
Gunakan form di bawah untuk mengirim test webhook ke endpoint kamu. Ini akan mengirim request persis seperti yang akan dikirim di production.
LOZY Webhook Integration Guide v1.0