Cuscapi WhatsApp Webhook API

Postman collection for WhatsApp / Wave webhook integration APIs.

Generated from Postman collection. Selected API version.

Overview

This document describes the Cuscapi WhatsApp / Wave webhook integration APIs generated from the Postman collection.

Signed Wave endpoints require X-WAVE-TIMESTAMP and X-WAVE-SIGNATURE. Public APIs only require standard JSON headers.
This documentation currently shows the selected WhatsApp / Wave integration APIs only. Hidden endpoints can be added back later when they are ready to publish.

Base URLs

EnvironmentBase URL
Localhttp://cuscapi-merchant.localhost
Staginghttps://cuscapi-merchant-api.coder.com.my
ProductionTBC

Authenticating Requests

All Wave-authenticated webhook requests must include these headers:

HeaderValue
X-WAVE-TIMESTAMP{{wave_timestamp}}
X-WAVE-SIGNATURE{{wave_signature}}

Signature Formula

payload = "{timestamp}.{raw_request_body}"

signature = HMAC_SHA256(payload, webhook_secret)

The raw request body must match exactly. Do not reformat the JSON body after generating the signature.

const secret = pm.collectionVariables.get("wave_secret");

const timestamp = Math.floor(Date.now() / 1000).toString();
pm.collectionVariables.set("wave_timestamp", timestamp);

let body = "";

if (pm.request.body && pm.request.body.mode === "raw") {
    body = pm.variables.replaceIn(pm.request.body.raw || "");
}

const payload = timestamp + "." + body;

const signature = CryptoJS.HmacSHA256(payload, secret)
    .toString(CryptoJS.enc.Hex);

pm.collectionVariables.set("wave_signature", signature);
<?php

$secret = 'your-webhook-secret';

$body = [
    'phone' => '60168880366',
    'whatsapp_name' => 'Boon',
];

$json = json_encode($body, JSON_UNESCAPED_SLASHES);
$timestamp = time();
$payload = $timestamp . '.' . $json;

$signature = hash_hmac('sha256', $payload, $secret);

$headers = [
    'X-WAVE-TIMESTAMP' => $timestamp,
    'X-WAVE-SIGNATURE' => $signature,
];
$timestamp = $request->header('X-WAVE-TIMESTAMP');
$signature = $request->header('X-WAVE-SIGNATURE');

$payload = $timestamp . '.' . $request->getContent();

$expected = hash_hmac(
    'sha256',
    $payload,
    config('services.wave.webhook_secret')
);

if (! hash_equals($expected, $signature)) {
    abort(401, 'Invalid signature.');
}
Important: The request body must exactly match the body used to generate the signature. The server validates the signature using the raw HTTP request body.

Collection Variables

VariableDefault Value
base_urlhttps://your-domain.com
wave_secretyour-wave-signature
wave_timestamp{{$timestamp}}
sanctum_tokenyour-sanctum-token
phone60168880366
whatsapp_nameBoon
outlet_id1
product_id4
product_with_option_id6
product_option_group_id7
product_option_item_id2
wave_signature
base_url_local
base_url_staging

Integration Scenarios

Scenarios

These scenarios describe common WhatsApp / Wave integration flows and how the API endpoints are used together.

WhatsApp / Wave Flow
SC02

Real-Time Order Status

Scenario SC02

Wave authenticates the customer and retrieves the customer order list. When the order status changes, Cuscapi server triggers a WhatsApp message notification to update the customer in real time.

POST/customers/token GET/customers/orders
SYSTEMWhatsApp Message Notification

Flow Detail

  1. Wave calls POST /webhook-api/v1/customers/token using the customer phone number.
  2. Wave calls GET /webhook-api/v1/customers/orders to retrieve the customer's latest orders.
  3. When the order status changes, Cuscapi server sends a WhatsApp notification message to the customer.

Sample WhatsApp Message

Hi Boon, your order ORD-202606260001 is now Ready for Pickup.

Outlet: Sushi King Damansara
Status: Ready for Pickup
Thank you.
SC06

Web → WhatsApp Cart Sync

Scenario SC06

Customer starts cart activity on web and continues the same cart flow in WhatsApp.

POST/cart/items
WEBOpen WhatsApp
GET/cart

Endpoints

Public APIs

Outlet Listing

{{base_url}}/webhook-api/v1/outlets?latitude=1.15500000&longitude=110.15000000
GET
HeaderValue
Acceptapplication/json

No request body.

200 OKOutlet Listing Response
{
    "success": true,
    "message": "Outlet list retrieved successfully.",
    "data": [
        {
            "id": 3,
            "name": "City Mall Branch",
            "phone": "082888999",
            "address_line_1": "Level 2, City Mall",
            "address_line_2": "Unit C-12",
            "city": "Kuching",
            "postal_code": "93300",
            "state_id": 1982,
            "country_id": 129,
            "latitude": "1.15500000",
            "longitude": "110.15000000",
            "status": 1,
            "today_opening_hour": {
                "day_of_week": 5,
                "open_time": "10:00:00",
                "close_time": "23:00:00",
                "last_order_time": "22:30:00",
                "status": 1,
                "accept_order_status": 1,
                "accept_order_start_time": "10:00:00",
                "accept_order_end_time": "22:30:00",
                "last_order_time_override": null,
                "is_open_now": true,
                "is_accepting_order_now": true
            },
            "opening_hours": [
                {
                    "day_of_week": 1,
                    "open_time": "10:00:00",
                    "close_time": "22:30:00",
                    "last_order_time": "22:00:00",
                    "status": 1,
                    "accept_order_status": 1,
                    "accept_order_start_time": "10:00:00",
                    "accept_order_end_time": "22:00:00",
                    "last_order_time_override": null
                },
                {
                    "day_of_week": 5,
                    "open_time": "10:00:00",
                    "close_time": "23:00:00",
                    "last_order_time": "22:30:00",
                    "status": 1,
                    "accept_order_status": 1,
                    "accept_order_start_time": "10:00:00",
                    "accept_order_end_time": "22:30:00",
                    "last_order_time_override": null
                },
                {
                    "day_of_week": 7,
                    "open_time": "10:00:00",
                    "close_time": "22:00:00",
                    "last_order_time": "21:30:00",
                    "status": 1,
                    "accept_order_status": 1,
                    "accept_order_start_time": "10:00:00",
                    "accept_order_end_time": "21:30:00",
                    "last_order_time_override": null
                }
            ],
            "distance_km": 0,
            "distance_label": "0.00km away"
        },
        {
            "id": 2,
            "name": "SUSHI KING Damansara",
            "phone": "082123456",
            "address_line_1": "No. 1, Jalan Equine 9, Taman Equine, 43300 Seri Kembangan, Selangor",
            "address_line_2": null,
            "city": "Kuching",
            "postal_code": "93350",
            "state_id": 1983,
            "country_id": 129,
            "latitude": "1.55330000",
            "longitude": "110.35920000",
            "status": 1,
            "today_opening_hour": {
                "day_of_week": 5,
                "open_time": "11:00:00",
                "close_time": "21:30:00",
                "last_order_time": "21:30:00",
                "status": 1,
                "accept_order_status": 1,
                "accept_order_start_time": "11:00:00",
                "accept_order_end_time": "21:30:00",
                "last_order_time_override": null,
                "is_open_now": true,
                "is_accepting_order_now": true
            },
            "opening_hours": [
                {
                    "day_of_week": 0,
                    "open_time": "11:00:00",
                    "close_time": "21:30:00",
                    "last_order_time": "21:30:00",
                    "status": 1,
                    "accept_order_status": 1,
                    "accept_order_start_time": "11:00:00",
                    "accept_order_end_time": "21:30:00",
                    "last_order_time_override": null
                },
                {
                    "day_of_week": 7,
                    "open_time": "10:00:00",
                    "close_time": "22:00:00",
                    "last_order_time": "21:30:00",
                    "status": 1,
                    "accept_order_status": 1,
                    "accept_order_start_time": "10:00:00",
                    "accept_order_end_time": "21:30:00",
                    "last_order_time_override": null
                }
            ],
            "distance_km": 50.02,
            "distance_label": "50.02km away"
        },
        {
            "id": 6,
            "name": "SUSHI KING Taman Equine",
            "phone": null,
            "address_line_1": "No. 1, Jalan Equine 9, Taman Equine, 43300 Seri Kembangan, Selangor",
            "address_line_2": null,
            "city": "",
            "postal_code": "",
            "state_id": 1983,
            "country_id": 129,
            "latitude": "0.00000000",
            "longitude": "0.00000000",
            "status": 1,
            "today_opening_hour": {
                "day_of_week": 5,
                "open_time": "11:00:00",
                "close_time": "21:30:00",
                "last_order_time": "21:30:00",
                "status": 1,
                "accept_order_status": 1,
                "accept_order_start_time": "11:00:00",
                "accept_order_end_time": "21:30:00",
                "last_order_time_override": null,
                "is_open_now": true,
                "is_accepting_order_now": true
            },
            "opening_hours": [
                {
                    "day_of_week": 0,
                    "open_time": "11:00:00",
                    "close_time": "21:30:00",
                    "last_order_time": "21:30:00",
                    "status": 1,
                    "accept_order_status": 1,
                    "accept_order_start_time": "11:00:00",
                    "accept_order_end_time": "21:30:00",
                    "last_order_time_override": null
                }
            ],
            "distance_km": 12247.65,
            "distance_label": "12,247.65km away"
        }
    ]
}

No request-level script.

Public APIs

Outlet Menu

{{base_url}}/webhook-api/v1/outlets/{{outlet_id}}/menu
GET
HeaderValue
Acceptapplication/json

No request body.

200 OKOutlet Menu Response
{
    "success": true,
    "message": "Product list retrieved successfully.",
    "data": [
        {
            "id": 1,
            "sku": "5806",
            "slug": "ten-don-s-2-5806",
            "name": "Ten Don (S)",
            "description": "Japanese curry rice bowl with juicy chicken hambagu.",
            "category": "Rice",
            "price": 0
        },
        {
            "id": 2,
            "sku": "2519",
            "slug": "yakiniku-curry-2-2519",
            "name": "Yakiniku Curry",
            "description": "Crispy chicken katsu served with rich Japanese curry and rice.",
            "category": "Rice",
            "price": 22.9
        },
        {
            "id": 3,
            "sku": "OUT2-ABURISALMO",
            "slug": "aburi-salmon-rice-bowl",
            "name": "Aburi Salmon Rice Bowl",
            "description": "Seared salmon served over warm Japanese rice.",
            "category": "Rice",
            "price": 24.9
        },
        {
            "id": 4,
            "sku": "2897",
            "slug": "vida-zero-salty-lychee-2-2897",
            "name": "Vida Zero Salty Lychee",
            "description": "Rich broth ramen with chicken slices and egg.",
            "category": "Noodles",
            "price": 5.5
        },
        {
            "id": 5,
            "sku": "2899",
            "slug": "vida-zero-sakura-2-2899",
            "name": "Vida Zero Sakura",
            "description": "Spicy miso broth with noodles, egg, and toppings.",
            "category": "Noodles",
            "price": 5.5
        },
        {
            "id": 6,
            "sku": "2227",
            "slug": "chicken-nanban-2-2227",
            "name": "Chicken Nanban",
            "description": "Fresh salmon roll topped with creamy mentai sauce.",
            "category": "Combo",
            "price": 8.9
        },
        {
            "id": 7,
            "sku": "OUT2-SALMONCREA",
            "slug": "salmon-cream-cheese-mentaiyaki-with-spicy-karaage-set",
            "name": "Salmon Cream Cheese Mentaiyaki with Spicy Karaage Set",
            "description": "Aburi-style salmon topped with creamy cheese and mentai mayo, served with spicy karaage and rice.",
            "category": "Combo",
            "price": 28.9
        },
        {
            "id": 8,
            "sku": "OUT2-CHICKENKAT",
            "slug": "chicken-katsu-set",
            "name": "Chicken Katsu Set",
            "description": "Crispy chicken katsu with rice, salad, and soup.",
            "category": "Combo",
            "price": 26.9
        },
        {
            "id": 9,
            "sku": "OUT2-GYOZA",
            "slug": "gyoza",
            "name": "Gyoza",
            "description": "Pan-fried Japanese dumplings.",
            "category": "Sides",
            "price": 9.9
        },
        {
            "id": 10,
            "sku": "OUT2-KARAAGE",
            "slug": "karaage",
            "name": "Karaage",
            "description": "Japanese fried chicken bites.",
            "category": "Sides",
            "price": 12.9
        },
        {
            "id": 11,
            "sku": "OUT2-EDAMAME",
            "slug": "edamame",
            "name": "Edamame",
            "description": "Lightly salted boiled soybeans.",
            "category": "Sides",
            "price": 6.9
        },
        {
            "id": 12,
            "sku": "OUT2-COLAORIGIN",
            "slug": "cola-original-can",
            "name": "Cola Original (Can)",
            "description": "Chilled canned soft drink.",
            "category": "Drinks",
            "price": 3
        },
        {
            "id": 13,
            "sku": "OUT2-ICEDGREENT",
            "slug": "iced-green-tea",
            "name": "Iced Green Tea",
            "description": "Refreshing iced Japanese green tea.",
            "category": "Drinks",
            "price": 4.9
        },
        {
            "id": 27,
            "sku": "2310",
            "slug": "tenpura-udon-2-2310",
            "name": "Tenpura Udon",
            "description": "Japanese wheat noodles in soybean-based soup served with tenpura prawns & vegetables.",
            "category": "NOODLES",
            "price": 19.5
        },
        {
            "id": 28,
            "sku": "2305",
            "slug": "nabeyaki-udon-2-2305",
            "name": "Nabeyaki Udon",
            "description": "Japanese wheat noodles, beancurd skin, chicken, tenpura prawn, crabstick & lotus root served in soybean-based soup.",
            "category": "NOODLES",
            "price": 19.5
        }
    ]
}

Sample shown with selected menu items. Actual response may contain more products depending on the outlet menu.

No request-level script.

Public APIs

Product Search - POST

{{base_url}}/webhook-api/v1/products
POST
HeaderValue
Acceptapplication/json
Content-Typeapplication/json
{
  "keyword": "chicken",
  "outlet_id": {{outlet_id}}
}

No saved example response in the Postman collection.

No request-level script.

Public APIs

Product Detail

{{base_url}}/webhook-api/v1/products/{{product_id}}
GET
HeaderValue
Acceptapplication/json

No request body.

No saved example response in the Postman collection.

No request-level script.

Wave Auth + Cart APIs

Customer Token

{{base_url}}/webhook-api/v1/customers/token
POST
HeaderValue
Content-Typeapplication/json
X-WAVE-SIGNATURE{{wave_signature}}
X-WAVE-TIMESTAMP{{wave_timestamp}}
{
  "phone": "{{phone}}",
  "whatsapp_name": "{{whatsapp_name}}"
}
{
    "success": true,
    "customer": {
        "id": 4,
        "name": "Boon",
        "phone_number": null
    },
    "access_token": "49|8ScI0tDMy8JLZ4SkTYOSOYOqurjG9FFNR0uWhZFlf11c3084",
    "token_type": "Bearer",
    "login_token": "WAfucJARYrnbfRrEfM6OE66QZHPZxszqjoYbMFI4sYL4y7Y3qoG7x0iQHIEgvF5dVGWEAjjDMLwlcA5h",
    "login_url": "http://localhost:3000/wave-login?token=WAfucJARYrnbfRrEfM6OE66QZHPZxszqjoYbMFI4sYL4y7Y3qoG7x0iQHIEgvF5dVGWEAjjDMLwlcA5h",
    "expires_in_minutes": 10
}

No request-level script.

Wave Auth + Cart APIs

Select Outlet - Pickup

{{base_url}}/webhook-api/v1/outlets/{{outlet_id}}/pickup
POST
HeaderValue
Acceptapplication/json
Content-Typeapplication/json
X-WAVE-SIGNATURE{{wave_signature}}
X-WAVE-TIMESTAMP{{wave_timestamp}}
{
  "phone": "{{phone}}",
  "whatsapp_name": "{{whatsapp_name}}"
}
{
    "success": true,
    "message": "Outlet selected successfully.",
    "data": {
        "customer": {
            "id": 4,
            "name": "Boon",
            "phone": "60168880366"
        },
        "cart": {
            "id": 61,
            "outlet_id": 2,
            "delivery_option": "pickup"
        },
        "outlet": {
            "id": 2,
            "name": "SUSHI KING Damansara"
        }
    }
}

No request-level script.

Wave Auth + Cart APIs

Select Outlet - Dine In

{{base_url}}/webhook-api/v1/outlets/{{outlet_id}}/dine-in
POST
HeaderValue
Acceptapplication/json
Content-Typeapplication/json
X-WAVE-SIGNATURE{{wave_signature}}
X-WAVE-TIMESTAMP{{wave_timestamp}}
{
  "phone": "{{phone}}",
  "whatsapp_name": "{{whatsapp_name}}"
}
{
    "success": true,
    "message": "Outlet selected successfully.",
    "data": {
        "customer": {
            "id": 4,
            "name": "Boon",
            "phone": "60168880366"
        },
        "cart": {
            "id": 61,
            "outlet_id": 2,
            "delivery_option": "dine_in"
        },
        "outlet": {
            "id": 2,
            "name": "SUSHI KING Damansara"
        }
    }
}

No request-level script.

Wave Auth + Cart APIs

Show Cart

{{base_url}}/webhook-api/v1/cart
GET
HeaderValue
Acceptapplication/json
Content-Typeapplication/json
X-WAVE-SIGNATURE{{wave_signature}}
X-WAVE-TIMESTAMP{{wave_timestamp}}
{
  "phone": "{{phone}}",
  "whatsapp_name": "{{whatsapp_name}}"
}

No saved example response in the Postman collection.

No request-level script.

Wave Auth + Cart APIs

Add To Cart - Without Option

{{base_url}}/webhook-api/v1/cart/items
POST
HeaderValue
Acceptapplication/json
Content-Typeapplication/json
X-WAVE-SIGNATURE{{wave_signature}}
X-WAVE-TIMESTAMP{{wave_timestamp}}
{
  "phone": "{{phone}}",
  "whatsapp_name": "{{whatsapp_name}}",
  "product_id": {{product_id}},
  "quantity": 2,
  "note": "Less spicy"
}

No saved example response in the Postman collection.

Prerequest Script

const secret = pm.collectionVariables.get("wave_secret");

const timestamp = Math.floor(Date.now() / 1000).toString();
pm.collectionVariables.set("wave_timestamp", timestamp);

let body = "";

if (pm.request.body && pm.request.body.mode === "raw") {
    body = pm.variables.replaceIn(pm.request.body.raw || "");
}

const payload = timestamp + "." + body;

const signature = CryptoJS.HmacSHA256(payload, secret)
    .toString(CryptoJS.enc.Hex);

pm.collectionVariables.set("wave_signature", signature);

console.log("payload:", payload);
console.log("signature:", signature);
Wave Auth + Cart APIs

Add To Cart - With Option

{{base_url}}/webhook-api/v1/cart/items
POST
HeaderValue
Acceptapplication/json
Content-Typeapplication/json
X-WAVE-SIGNATURE{{wave_signature}}
X-WAVE-TIMESTAMP{{wave_timestamp}}
{
  "phone": "{{phone}}",
  "whatsapp_name": "{{whatsapp_name}}",
  "product_id": {{product_with_option_id}},
  "quantity": 1,
  "note": "No onion",
  "options": [
    {
      "product_option_group_id": {{product_option_group_id}},
      "product_option_item_ids": [
        {{product_option_item_id}}
      ]
    }
  ]
}

No saved example response in the Postman collection.

No request-level script.

Wave Auth APIs

Customer Search

{{base_url}}/webhook-api/v1/customers?phone={{phone}}
GET
HeaderValue
Acceptapplication/json
X-WAVE-SIGNATURE{{wave_signature}}
X-WAVE-TIMESTAMP{{wave_timestamp}}

No request body.

No saved example response in the Postman collection.

No request-level script.

Wave Auth APIs

Customer Orders

{{base_url}}/webhook-api/v1/customers/orders
GET
HeaderValue
Acceptapplication/json
X-WAVE-SIGNATURE{{wave_signature}}
X-WAVE-TIMESTAMP{{wave_timestamp}}
{
  "phone": "{{phone}}"
}

No saved example response in the Postman collection.

No request-level script.

Wave Auth APIs

Loyalty Point

{{base_url}}/webhook-api/v1/loyalty/4
GET
HeaderValue
Acceptapplication/json
X-WAVE-SIGNATURE{{wave_signature}}
X-WAVE-TIMESTAMP{{wave_timestamp}}

No request body.

Success 200 OK
{
    "success": true,
    "customer": {
        "id": 4,
        "name": "Boon",
        "phone": "60168880366"
    },
    "points_balance": 850,
    "points_summary": {
        "earned": 0,
        "redeemed": 0,
        "expired": 0,
        "adjusted": 0
    },
    "customer_vouchers": [
        {
            "id": 1,
            "voucher_id": 2,
            "name": "RM15 Voucher",
            "status": "available",
            "points_used": 150,
            "active": "2026-06-25 23:43:52",
            "expiry": null
        }
    ]
}
Customer Not found 404 Not Found
{
    "success": false,
    "message": "Endpoint not found."
}

No request-level script.

Wave Auth APIs

Loyalty Vouchers

{{base_url}}/webhook-api/v1/loyalty/1/vouchers
GET
HeaderValue
Acceptapplication/json
X-WAVE-SIGNATURE{{wave_signature}}
X-WAVE-TIMESTAMP{{wave_timestamp}}

No request body.

No saved example response in the Postman collection.

No request-level script.

Wave Auth APIs

Claim Loyalty

{{base_url}}/webhook-api/v1/loyalty/redeem
POST
HeaderValue
Acceptapplication/json
Content-Typeapplication/json
X-WAVE-SIGNATURE{{wave_signature}}
X-WAVE-TIMESTAMP{{wave_timestamp}}
{
  "phone": "{{phone}}",
  "whatsapp_name": "{{whatsapp_name}}",
  "voucher_id": 2
}

No saved example response in the Postman collection.

No request-level script.