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.

Protocol
HTTPS (recommended)
Method
POST
Content-Type
application/json

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

Penting: Endpoint kamu WAJIB memenuhi syarat berikut:
URLPublicly accessible HTTPS URL
MethodHarus menerima POST
TimeoutRespond dalam 10 detik (jangan proses lama di sini)
ResponseReturn HTTP 200 dengan JSON body (lihat section Response)
IdempotentHandle duplikat — gunakan X-Webhook-Delivery-Id untuk dedup
Best Practice: Respond 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:

HeaderContohDeskripsi
Content-Typeapplication/jsonFormat body
X-Webhook-Eventorder.createdTipe event
X-Webhook-Signaturea1b2c3...hexHMAC-SHA256 signature
X-Webhook-Delivery-Id12345Unique ID (untuk dedup)
X-Webhook-Timestamp1700000000Unix timestamp
User-AgentLOZY-Webhook/1.0Identifier

Order Events

order.created order.updated order.cancelled order.completed

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)

asn.created asn.updated asn.shipped asn.delivered

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

WAJIB: Endpoint kamu harus respond sesuai format di bawah. Jika tidak, kami anggap delivery GAGAL dan akan retry.

Success Response

Respond dengan HTTP 200 dan body JSON berikut:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "success": true,
  "message": "Webhook received"
}
FieldTypeRequiredDeskripsi
successbooleanYesHarus true jika berhasil diterima
messagestringNoPesan opsional
Kami menganggap delivery SUKSES jika:
  • 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"
}
Perhatian: Response non-2xx akan trigger auto-retry. Jika kamu sengaja menolak (bukan error sementara), respond 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:

AttemptDelayKeterangan
1st (initial)ImmediatelyPengiriman pertama
2nd (retry 1)30 secondsRetry pertama
3rd (retry 2)2 minutesRetry kedua
4th (retry 3)8 minutesRetry 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:

InfoContohKeterangan
NamaWMS Client ANama identifikasi kamu
Webhook URLhttps://yourserver.com/webhookURL yang akan menerima POST request
Eventsorder.created, asn.shippedEvent 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."
}
SIMPAN secret_key — ini digunakan untuk verifikasi signature di setiap webhook yang kamu terima. Tidak bisa dilihat lagi setelah ini (hanya bisa di-regenerate).
Perlu Approval: Setelah register, status kamu adalah 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