API Key
x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5

Authentication

Every request must include an x-api-key header. The value is matched against a server-side environment variable. Requests with a missing or incorrect key receive a 401 response immediately, before any business logic runs.

HeaderTypeRequiredDescription
x-api-key string required Your assigned API key. Validated server-side against an environment variable.
Content-Type string required Must be application/json for all POST requests.
Example request headers
x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5 Content-Type: application/json

Customer Signup

Registers a new customer using a two-step OTP verification flow. The same endpoint URL handles both steps — the presence or absence of the otp field in the request body determines which step is executed.

Step 1
Request OTP
Submit customer details without otp. Server sends a 4-digit code via SMS or WhatsApp.
Step 2
Verify & Complete
Re-submit the same body with otp added. Returns login token and customer record.
ℹ️
OTP delivery channel — UAE (+971) and KSA (+966) numbers receive the OTP via SMS. All other country codes receive it via WhatsApp. The OTP expires after 5 minutes and is single-use.
POST /customers/signup Step 1 · Request OTP

Validates the supplied customer details, checks for duplicate mobile/email, generates a 4-digit OTP, and delivers it via SMS (UAE/KSA) or WhatsApp (all other regions). Returns step: "otp_sent" on success.

POST · /customers/signup
{ "mobileNumber": "971501234567", // required — E.164 without leading + "first_name": "Aisha", "last_name": "Al Mansoori", "branch_id": 12, // Optional fields "email": "[email protected]", "birthdate": "1992-06-15", // YYYY-MM-DD "gender": "female", // male | female | other "country_id": 1, "brand_id": 3 }
ParameterTypeRequiredDescription
mobileNumber string required Mobile number in E.164 format without the leading +. Examples: 971501234567 (UAE), 966501234567 (KSA).
first_name string required Customer's first name.
last_name string required Customer's last name.
branch_id integer required ID of the branch the customer is registering at.
email string optional Valid email address. Returns 400 if format is invalid; 409 if already registered.
birthdate string optional Date of birth in YYYY-MM-DD format. Returns 400 if format is invalid.
gender string optional One of: male, female, other. Returns 400 for any other value.
country_id integer optional Country reference ID from the platform's country table.
brand_id integer optional Brand reference ID for multi-brand deployments.
200 OK
{ "success": true, "step": "otp_sent", "message": "OTP sent to 971501234567 via SMS", "expires_in": 300 // seconds until OTP expires }
POST /customers/signup Step 2 · Verify OTP & Complete Signup

Re-submit the Step 1 body with the otp field added. The server validates the OTP (checks it is not expired, not previously used, and matches the stored code), then creates the customer record with customer_level: WHITE, creates a loyalty wallet, and generates a referral code. Returns a login token and the full customer object.

POST · /customers/signup — with otp field
{ "mobileNumber": "971501234567", "first_name": "Aisha", "last_name": "Al Mansoori", "branch_id": 12, "email": "[email protected]", "birthdate": "1992-06-15", "gender": "female", "country_id": 1, "brand_id": 3, "otp": "8472" // 4-digit code received by customer }
ParameterTypeRequiredDescription
mobileNumber string required Must match the number used in Step 1 exactly.
otp string required The 4-digit OTP received by the customer. Returns 400 if expired, already used, or incorrect.
first_name, last_name, branch_id required Same values as Step 1. Re-validated on submission.
email, birthdate, gender, country_id, brand_id optional Same optional fields as Step 1. Include them if they were part of the Step 1 payload.
200 OK
{ "success": true, "step": "signup_complete", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "customer": { "id": 4821, "first_name": "Aisha", "last_name": "Al Mansoori", "mobileNumber": "971501234567", "email": "[email protected]", "birthdate": "1992-06-15", "gender": "female", "customer_level": "WHITE", "referral_code": "AISHA-K9X2", "country_id": 1, "brand_id": 3, "wallet": { "id": 9043, "balance": 0, "currency": "AED" }, "branch": { "id": 12, "name": "Downtown Dubai", "city": "Dubai" }, "created_at": "2025-06-12T10:34:21.000Z" } }

Error Reference

StatusCode / ConditionDescription
400 missing_fields One or more required fields (mobileNumber, first_name, last_name, branch_id) are absent from the request body.
400 invalid_email The email field is present but is not a valid email address format.
400 invalid_birthdate The birthdate field is present but does not match YYYY-MM-DD format.
400 invalid_gender The gender field is present but is not one of male, female, or other.
400 otp_invalid The supplied OTP does not match the stored code for this mobile number.
400 otp_expired The OTP was generated more than 5 minutes ago and is no longer valid. Restart from Step 1.
400 otp_already_used This OTP has already been successfully verified once. Restart from Step 1.
401 unauthorized The x-api-key header is missing or does not match the server-side value.
409 mobile_exists A customer with this mobile number is already registered. Direct the user to login instead.
409 email_exists A customer with this email address is already registered.
503 delivery_failure The SMS or WhatsApp provider was unable to deliver the OTP. Retry after a short delay; if persistent, check the number format.
500 internal_error An unexpected server-side error occurred. Contact the platform team with the request ID if available.

Integration Notes

📱
Phone number format — submit mobileNumber in E.164 format without the leading +. For example, a UAE number would be 971501234567 rather than +971501234567 or 0501234567.
⏱️
OTP expiry — the OTP is valid for 5 minutes (300 seconds) from generation. Build your UI to surface a countdown and a "Resend OTP" action that re-triggers Step 1 with the same payload.
⚠️
Single-use OTPs — each OTP can be verified exactly once. After a successful Step 2, the code is invalidated. Do not retry Step 2 with the same OTP if it has already succeeded.

Get Member Details

Looks up a customer record by phone number within a specific branch's loyalty club. Requires three request headers in addition to the body — all three are validated before any lookup is performed.

POST /v1/getMemberDetails Look up member by phone number

Returns full membership details for a customer matched by their phone number within the specified branch club. The phone number is normalised through validateAndFormat() before the lookup — submitting a malformed number returns a 400 before any DB query runs.

HeaderTypeRequiredDescription
x-api-key string required Must match the API_KEY server environment variable. Returns 401 if absent or incorrect.
x-branch-id string required The branch's club_id (string identifier, not the numeric branch row ID). Returns 400 if missing or if the value doesn't match any branch's club_id.
x-source-type string required Must be the exact string "source-type". Any other value returns 400.
Content-Type string required Must be application/json.
Example headers
x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5 x-branch-id: CLUB-DXB-001 x-source-type: source-type Content-Type: application/json
POST · /v1/getMemberDetails
{ "customer": { "phoneNumber": "+971501234567" // E.164 format with leading + } }
FieldTypeRequiredDescription
customer object required Wrapper object. Returns 400 if absent or not an object.
customer.phoneNumber string required Customer's phone number in E.164 format (e.g. +971501234567). Passed through validateAndFormat() — returns 400 if the number is invalid or unrecognised.
ℹ️
Phone format note — unlike the Signup endpoint, this endpoint expects a leading + (standard E.164). Example: +971501234567, not 971501234567.
200 OK
{ "membership": { "firstName": "John", "lastName": "Doe", "email": "[email protected]", "gender": "male", "phoneNumber": "+971501234567", "status": "Active", "createdOn": "2024-01-15T10:30:00.000Z", "memberId": "MEM123456", "mobileAppUsed": false, "mobileAppUsedLastDate": null, "birthday": "1990-05-20", // omitted if no birthdate on record "pointsBalance": { "balance": { "monetary": 25.50, "nonMonetary": 340 } } } }

Response Field Reference

FieldTypeDescription
membership.firstName string Customer's first name.
membership.lastName string Customer's last name.
membership.email string Customer's email address.
membership.gender string One of: male, female, other.
membership.phoneNumber string Normalised E.164 phone number as stored.
membership.status string Membership status, e.g. Active.
membership.createdOn string (ISO 8601) UTC timestamp of when the membership was created.
membership.memberId string Unique membership identifier.
membership.mobileAppUsed boolean true if the customer has logged in via the mobile app.
membership.mobileAppUsedLastDate string | null ISO 8601 timestamp of last mobile app login, or null if never used.
membership.birthday string (YYYY-MM-DD) Conditionally included. Only present in the response if a birthdate is on record for this customer. Omitted entirely if not set.
membership.pointsBalance.balance.monetary number Monetary points balance (decimal).
membership.pointsBalance.balance.nonMonetary integer Non-monetary points balance (whole number).

Error Reference

StatusConditionDescription
400 missing x-branch-id The x-branch-id header was not included in the request.
400 invalid x-branch-id The x-branch-id value does not match any branch's club_id. Note: this is the club identifier string, not the numeric branch row ID.
400 invalid x-source-type x-source-type is present but is not exactly the string "source-type". The value is matched literally.
400 missing customer object The request body does not contain a customer object.
400 missing phoneNumber The customer object is present but phoneNumber is absent.
400 invalid phone number The phoneNumber value failed validateAndFormat() — the number is not a recognised or properly formatted E.164 number.
401 unauthorized The x-api-key header is missing or does not match the server-side API_KEY environment variable.
404 member not found No customer record exists for the given phone number within the specified branch club.

Reservations

GET /booking/reservations/filter Filtered reservation list

Returns a paginated, filterable list of reservations. No authentication required. Each item in data[] is a fully joined reservation object including customer, brand, branch (with country), section, tables, and payments. The response also echoes back a filters_applied summary of all active filter values.

ParameterTypeRequiredDefaultDescription
reservation_status string optional Filter by reservation status. Accepts comma-separated values e.g. CONFIRMED,PENDING.
status string optional Alias for reservation_status. Behaves identically.
payment_status string optional PAID,PAYLATER Filter by payment status. Accepts comma-separated values e.g. PAID,PAYLATER,REFUNDED. If omitted, defaults to PAID,PAYLATER only.
branch_id number optional Filter by a specific branch.
brand_id number optional Filter by brand.
country_id number optional Filter by the branch's country ID.
country string optional Filter by country string field on the reservation record.
reservation_id string optional Exact match on the string reservation ID e.g. RES123.
date ISO date optional Filters the full calendar day by start_time. Takes priority over start_date, end_date, start_time, and end_time — all four are ignored when date is present.
start_date ISO date optional Range start date. Only used when date is not provided.
end_date ISO date optional Range end date. Only used when date is not provided.
start_time ISO datetime optional Fine-grained datetime filter. Only applied when date is absent.
end_time ISO datetime optional Fine-grained datetime filter. Only applied when date is absent.
search string optional Case-insensitive search across mobile_number, reservation_id, first_name, and last_name.
sort_by string optional created_date Any reservation field name to sort by.
sort_order ASC | DESC optional DESC Sort direction.
page number optional 1 Page number for pagination.
limit number optional 10 Records per page.
curl -X GET "https://api1.staging.restroengage.com/api/booking/reservations/filter?branch_id=5&date=2024-12-01&reservation_status=CONFIRMED&page=1&limit=20"
With comma-separated statuses
curl -X GET "https://api1.staging.restroengage.com/api/booking/reservations/filter?branch_id=5&reservation_status=CONFIRMED,PENDING&payment_status=PAID,PAYLATER,REFUNDED"
With date range + search
curl -X GET "https://api1.staging.restroengage.com/api/booking/reservations/filter?start_date=2024-12-01&end_date=2024-12-31&search=john&sort_by=start_time&sort_order=ASC"
200 OK
{ "success": true, "data": [ { /* reservation fields */ "id": 1042, "reservation_id": "RES123", "reservation_status": "CONFIRMED", "payment_status": "PAID", "start_time": "2024-12-01T19:00:00.000Z", "party_size": 4, /* joined relations */ "customer": { "id": 88, "first_name": "John", "last_name": "Doe", "mobile_number": "+971501234567" }, "brand": { "id": 3, "name": "Brand Name" }, "branch": { "id": 5, "name": "Downtown", "country": { "id": 1, "name": "UAE" } }, "section": { "id": 2, "name": "Outdoor" }, "tables": [{ "id": 14, "table_number": "T-7" }], "payments": [{ "id": 55, "amount": 150.00, "status": "PAID" }] } ], "pagination": { "total": 42, "pages": 3, "page": 1, "limit": 20, "hasNext": true, "hasPrev": false }, "filters_applied": { "reservation_status": "CONFIRMED", "payment_status": null, "branch_id": "5", "brand_id": null, "country_id": null, "date_range": null, "search_query": null, "sort_by": "created_date", "sort_order": "desc" } }
💳
Default payment_status filter — if payment_status is omitted entirely, the API automatically restricts results to PAID and PAYLATER records. To include other statuses such as REFUNDED or PENDING, pass them explicitly.
📅
date takes full priority — when date is provided, the server filters the entire calendar day by start_time and completely ignores start_date, end_date, start_time, and end_time. Use start_date + end_date (or the time variants) only when date is absent.
🔤
Comma-separated multi-value filters — both reservation_status and payment_status accept multiple comma-separated values in a single parameter. Examples:
reservation_status=CONFIRMED,PENDING payment_status=PAID,PAYLATER,REFUNDED

Waiting List

GET /booking/waiting-list List or single waiting list entry

Returns waiting list entries for a branch, with joined customer, branch (including country and brand), and table data. When id is supplied, returns a single entry object directly instead of a paginated list. No authentication required.

ParameterTypeRequiredDefaultDescription
id number optional Fetch a single entry by its numeric ID. When present, all pagination and filter params are ignored. Returns 404 if not found.
branch_id number | string optional Filter by branch. Accepts either the numeric branch ID or the branch code string (e.g. DT01). Both are resolved server-side.
brand_id number optional Filter by brand.
status string optional Filter by entry status. Known values: WAITING, READY, EXPIRED.
customer_id number optional Filter by customer ID directly.
mobile_number string optional Looks up the customer by mobile number first, then filters by their customer_id. If no customer is found for that number, an empty list is returned immediately.
date ISO date optional Filters by createdAt — matches the full calendar day for the given date.
sort_by string optional createdAt Field to sort by. Allowed values: id, createdAt, updatedAt, status, party_size.
sort_order asc | desc optional desc Sort direction.
page number optional 1 Page number.
limit number optional 10 (max 100) Records per page. Maximum allowed value is 100.
List — filtered by branch and status
curl -X GET "https://api1.staging.restroengage.com/api/booking/waiting-list?branch_id=5&status=WAITING&page=1&limit=10"
Single entry by ID
curl -X GET "https://api1.staging.restroengage.com/api/booking/waiting-list?id=1"
Lookup by mobile number
curl -X GET "https://api1.staging.restroengage.com/api/booking/waiting-list?mobile_number=%2B971501234567&branch_id=5"
Filtered by date + branch code
curl -X GET "https://api1.staging.restroengage.com/api/booking/waiting-list?branch_id=DT01&date=2024-12-01&sort_by=party_size&sort_order=asc"
200 OK — List
{ "success": true, "data": [ { "id": 1, "customer_id": 12, "branch_id": 5, "status": "WAITING", "party_size": 4, "notes": "Window seat preferred", "archive": false, "createdAt": "2024-12-01T10:30:00.000Z", "updatedAt": "2024-12-01T10:30:00.000Z", "customer": { "id": 12, "mobile_number": "971501234567", "first_name": "John", "last_name": "Doe", "email": "[email protected]", "customer_type": "REGULAR" }, "branch": { "id": 5, "branch_name": "Downtown Branch", "branch_code": "DT01", "address": "123 Main St", "country": { "id": 1, "country_id": "AE", "currency": "AED" }, "brand": { "brand_Id": 2, "brand_name": "My Brand" } }, "tables": [ { "table": { "id": 7, "table_number": "T3", "capacity": 4, "shape": "round", "section_id": 2 } } ] } ], "total": 35, "page": 1, "pages": 4, "meta": { "total": 35, "filters": { "branch_id": "5", "status": "WAITING", "customer_id": null, "mobile_number": null, "date": null }, "sort": { "sort_by": "createdAt", "sort_order": "desc" } } }
200 OK — Single entry (?id=1)
{ "success": true, "data": { /* same single object structure as above */ } }
404 Not Found
{ "success": false, "message": "Waiting list entry not found" }
Auto-expiry of READY entries — on every request, the server silently runs an expiry check and marks any overdue READY entries as EXPIRED before returning results. This happens in the background and is not reflected in the response metadata.
📱
mobile_number resolution — when mobile_number is provided, the server first looks up the customer record by that number and then filters waiting list entries by the resolved customer_id. If no customer exists for that number, an empty data array is returned immediately — no error is thrown.
🏢
branch_id accepts numeric ID or branch code — pass either the integer branch row ID (e.g. 5) or the string branch code (e.g. DT01). The server resolves both to the same branch record.
🔢
Single-entry mode — when id is present, all other filter and pagination parameters are ignored. The response shape changes: data becomes a single object (not an array), and total, page, pages, and meta are omitted.

Gift Cards

A single endpoint with six distinct modes, selected by which query parameters are present. Modes are evaluated in strict priority order — the first matching mode wins and all others are ignored. No authentication required.

Mode priority order — evaluated top to bottom, first match wins:
1. id  →  2. regular_gift_card_id  →  3. bulk_table=true  →  4. batch_id  →  5. balance=true + card_number  →  6. default list
GET /api/gift-cards Multi-mode gift card endpoint

All six modes share this URL. The response shape — and whether a JSON body or a CSV file stream is returned — is determined entirely by which query parameters are supplied. All results are sorted by created_at DESC.

GET /api/gift-cards?id={id} Mode 1 · Single fetch by ID

Fetches a single gift card record by its numeric row ID. Returns a single object under data, not an array. Triggers 404 if no card exists with that ID.

ParameterTypeRequiredDescription
idnumberrequiredNumeric row ID of the gift card (e.g. 4513).
curl -X GET \ "https://api1.staging.restroengage.com/api/gift-cards?id=4513"
200 OK
{ "success": true, "data": { "id": 4513, "card_number": "1521543829", "regular_gift_card_id": null, "bulk_gift_card_id": 530, "vendor_gift_card_id": null, "amount_type": "FIXED", "currency": "SAR", "amount": 300, "remaining_balance": 300, "is_active": true, "expiry_date": "2027-04-28T00:00:00.000Z", "brand_id": 2, "branch_id": null, "created_at": "2026-04-28T12:32:32.349Z", "updated_at": null } }
404 Not Found
{ "success": false, "message": "Gift card not found" }
GET /api/gift-cards?regular_gift_card_id={id} Mode 2 · Cards by template

Returns a paginated list of gift cards generated from a specific regular gift card template.

ParameterTypeRequiredDefaultDescription
regular_gift_card_idnumberrequiredID of the gift card template.
pagenumberoptional1Page number.
limitnumberoptional10Records per page.
curl -X GET \ "https://api1.staging.restroengage.com/api/gift-cards?regular_gift_card_id=3&page=1&limit=10"
200 OK
{ "success": true, "data": [ /* gift card objects */ ], "meta": { "total": 20, "page": 1, "limit": 10, "pages": 2 } }
GET /api/gift-cards?bulk_table=true Mode 3 · Bulk batch list

Returns a paginated list of bulk gift card batches — one row per batch, showing its ID, batch reference, name, and total card count. Does not return individual cards.

ParameterTypeRequiredDefaultDescription
bulk_tablebooleanrequiredMust be true to trigger this mode.
pagenumberoptional1Page number.
limitnumberoptional10Records per page.
curl -X GET \ "https://api1.staging.restroengage.com/api/gift-cards?bulk_table=true&page=1&limit=10"
200 OK
{ "success": true, "data": [ { "id": 530, "batch_id": "BATCH-001", "card_name": "Saudi Aramco", "cards_count": 50 } ], "meta": { "total": 5, "page": 1, "limit": 10, "pages": 1 } }
GET /api/gift-cards?batch_id={batch_id} Mode 4a · Cards inside a batch

Returns a single batch object including its metadata and up to 100 gift card records, sorted latest first. Returns 404 if the batch_id string does not match any batch.

ParameterTypeRequiredDescription
batch_idstringrequiredBatch reference string (e.g. BATCH-001). Must not include download_csv=true to stay in this mode.
curl -X GET \ "https://api1.staging.restroengage.com/api/gift-cards?batch_id=BATCH-001"
200 OK
{ "success": true, "data": { "id": 530, "batch_id": "BATCH-001", "card_name": "Saudi Aramco", "gift_cards": [ /* up to 100 gift card objects, sorted created_at DESC */ ] } }
404 Not Found
{ "success": false, "message": "Batch not found" }
GET /api/gift-cards?batch_id={batch_id}&download_csv=true Mode 4b · Download batch as CSV

Streams the gift cards in a batch as a downloadable CSV file. The response body is a raw file stream — not JSON. download_csv=true only works alongside batch_id; without it, the parameter is ignored.

⚠️
This mode returns a file stream, not a JSON response. Do not attempt to parse the response body as JSON. Pipe it directly to a file.
ParameterTypeRequiredDescription
batch_idstringrequiredBatch reference string.
download_csvbooleanrequiredMust be true. Switches the response to a CSV file stream.
curl -X GET \ "https://api1.staging.restroengage.com/api/gift-cards?batch_id=BATCH-001&download_csv=true" \ -o gift-cards-batch-BATCH-001.csv
Response Headers
Content-Type: text/csv Content-Disposition: attachment; filename="gift-cards-batch-BATCH-001.csv"
Response Body (CSV stream)
Card Number,Card Name,Amount,Currency,Balance,Status,Expiry Date,Created Date 1521543829,Saudi Aramco,300,SAR,300,Active,2027-04-28,2026-04-28 12:32:32 1478811922,Saudi Aramco,300,SAR,300,Active,2027-04-28,2026-04-28 12:32:32 1412571969,Saudi Aramco,300,SAR,300,Active,2027-04-28,2026-04-28 12:32:32
GET /api/gift-cards?balance=true&card_number={card_number} Mode 5 · Card balance check

Looks up a single card by its card number and returns its current balance, currency, and status. Returns 404 if no card exists with that number.

ParameterTypeRequiredDescription
balancebooleanrequiredMust be true to trigger balance check mode.
card_numberstringrequiredThe gift card number to check (e.g. 1521543829).
curl -X GET \ "https://api1.staging.restroengage.com/api/gift-cards?balance=true&card_number=1521543829"
200 OK
{ "success": true, "data": { "card_number": "1521543829", "balance": 300.00, "currency": "SAR", "status": "active" // active | disabled | expired } }
404 Not Found
{ "success": false, "message": "Gift card not found" }
GET /api/gift-cards Mode 6 · Default filtered list

The default mode — triggered when none of the special params above are present. Returns a paginated list of gift cards across all types. All filters are optional. Results sorted by created_at DESC.

ParameterTypeRequiredDefaultDescription
branch_idnumberoptionalFilter by branch.
brand_idnumberoptionalFilter by brand.
customer_idnumberoptionalFilter by customer.
card_numberstringoptionalExact match on card number.
statusstringoptionalFilter by status. Accepted values: active, inactive.
mobile_numberstringoptionalResolves to a customer via the transaction table. Returns empty list immediately if no transactions found for that number.
bulk_gift_card_idnumberoptionalReturns only unpurchased cards belonging to this bulk batch ID. Returns 400 if value is non-numeric.
pagenumberoptional1Page number.
limitnumberoptional10Records per page.
Basic paginated list
curl -X GET \ "https://api1.staging.restroengage.com/api/gift-cards?page=1&limit=5"
Filter by brand + active status
curl -X GET \ "https://api1.staging.restroengage.com/api/gift-cards?brand_id=2&status=active&page=1&limit=20"
Unpurchased cards from a bulk batch
curl -X GET \ "https://api1.staging.restroengage.com/api/gift-cards?bulk_gift_card_id=530&status=active"
200 OK
{ "success": true, "data": [ { "id": 4513, "card_number": "1521543829", "regular_gift_card_id": null, "bulk_gift_card_id": 530, "vendor_gift_card_id": null, "amount_type": "FIXED", "currency": "SAR", "amount": 300, "remaining_balance": 300, "min_value": null, "max_value": null, "is_active": true, "purchase_date": null, "expiry_date": "2027-04-28T00:00:00.000Z", "duration": null, "user_id": null, "brand_id": 2, "branch_id": null, "form_details": null, "created_at": "2026-04-28T12:32:32.349Z", "updated_at": null } ], "meta": { "total": 1535, "page": 1, "limit": 5, "pages": 307 } }
FieldTypeDescription
idnumberNumeric row ID.
card_numberstringUnique card number used for redemption and balance checks.
regular_gift_card_idnumber | nullSet if this card was issued from a regular (template-based) gift card.
bulk_gift_card_idnumber | nullSet if this card belongs to a bulk batch.
vendor_gift_card_idnumber | nullSet if this card was issued by a third-party vendor.
amount_typestringFIXED — card has a set value. Other types may exist for variable-amount cards.
currencystringISO 4217 currency code (e.g. SAR, AED).
amountnumberOriginal face value of the card.
remaining_balancenumberCurrent remaining balance. Equals amount for unpurchased or unused cards.
min_value / max_valuenumber | nullFor variable-amount cards. null for FIXED cards.
is_activebooleantrue if the card is currently usable.
purchase_dateISO datetime | nullWhen the card was purchased. null for unpurchased bulk cards.
expiry_dateISO datetime | nullCard expiry datetime in UTC.
durationnumber | nullValidity duration in days from purchase, if applicable.
user_idnumber | nullAdmin user who created the card, if applicable.
brand_id / branch_idnumber | nullScoping references. branch_id may be null for brand-wide cards.
form_detailsobject | nullCustom form data collected at purchase, if any.
created_atISO datetimeRecord creation timestamp (UTC). Primary sort key.
updated_atISO datetime | nullLast update timestamp. null if never updated.

Error Reference

StatusConditionDescription
404 ?id= not found No gift card exists with the specified numeric ID.
404 ?batch_id= not found No bulk batch exists with the specified batch ID string.
404 ?balance=true card not found No gift card matches the provided card_number.
400 Invalid bulk_gift_card_id bulk_gift_card_id was provided but is not a valid number.

Integration Notes

🔀
Dual response behavior — this single controller action returns either a JSON body or a CSV file stream depending on the query parameters. When batch_id + download_csv=true is detected, the controller sets Content-Type: text/csv and streams the file. All other modes return application/json. Clients must branch on which response to expect based on the params they send.
📱
mobile_number in Mode 6 — resolves via the transaction table, not the customer table directly. If no transactions exist for that number, the response is an empty data array with total: 0 — no error is thrown.
📦
bulk_gift_card_id vs batch_id — these are different identifiers. bulk_gift_card_id is the numeric row ID of the bulk batch record; used in Mode 6 to filter unpurchased cards. batch_id is the string reference (e.g. BATCH-001); used in Modes 4a and 4b to look up the batch itself.

Public — Country

Returns country configuration records including timezone, currency, and per-country redemption behaviour flags. Has two modes: single fetch by ID or a filtered list. Requires x-api-key authentication.

🔑
Authentication required — include your API key in the x-api-key request header. Returns 401 immediately if the header is absent or the value does not match the server key.
GET /api/public/country Single fetch or filtered list

Mode is determined by the presence of ?id=. When supplied, returns a single country object. When absent, returns a paginated list with optional name/code filters. All list results are sorted by country_name ASC. Pagination uses offset-based skip, not a page parameter.

Required header
x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5
GET /api/public/country?id={id} Mode 1 · Single fetch by ID

Fetches a single country record by its numeric row ID. Returns the country object directly under data (not an array). Returns 404 if no country exists with that ID.

ParameterTypeRequiredDescription
id number required Numeric row ID of the country record (e.g. 2 for UAE).
curl -X GET \ "https://api1.staging.restroengage.com/api/public/country?id=1" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
200 OK
{ "success": true, "data": { /* single country object — see field reference */ } }
404 Not Found
{ "success": false, "message": "Country not found" }
401 Unauthorized
{ "success": false, "message": "Invalid or missing API key" }
GET /api/public/country Mode 2 · Filtered list

Returns a list of countries sorted by country_name ASC. Supports case-insensitive partial matching on name and code. Pagination is offset-based — use skip to advance pages, not a page parameter. The page field in the response is calculated server-side as Math.floor(skip / limit) + 1.

⚠️
Offset pagination, not page pagination. To get page 2 with 10 records per page, pass skip=10&limit=10. Passing page=2 has no effect — page is only an output field in the response.
ParameterTypeRequiredDefaultDescription
limit number optional 10 Maximum number of records to return per call.
skip number optional 0 Number of records to skip (offset). Use to paginate: page 2 = skip=10, page 3 = skip=20, etc.
country_name string optional Case-insensitive partial match (SQL ILIKE) on the country name. e.g. bah matches BAHRAIN.
country_id string optional Case-insensitive partial match on the country code (e.g. UAE, KSA). Known codes: BHD, JOR, KSA, KWT, OMN, ONL, QAT, UAE, GBR.
All countries (default)
curl -X GET \ "https://api1.staging.restroengage.com/api/public/country" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
Filter by country code
curl -X GET \ "https://api1.staging.restroengage.com/api/public/country?country_id=UAE" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
Offset pagination — page 2 (10 per page)
curl -X GET \ "https://api1.staging.restroengage.com/api/public/country?skip=10&limit=10" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
200 OK
{ "success": true, "data": [ { "id": 3, "country_id": "BHD", "country_name": "BAHRAIN", "currency": "BHD", "is_active": true, "discount_wallet_redeem_otp": true, "discount_wallet_redeem_pin": false, "points_redeem_otp": true, "points_redeem_pin": false, "created_date": "2025-05-14T17:46:46.749Z", "updated_date": "2025-08-12T07:46:22.301Z", "time_zone": { "value": "Asia/Kuwait", "label": "(GMT+3:00) Kuwait, Riyadh", "offset": 3, "abbrev": "AST", "altName": "Arabian Standard Time" } }, { "id": 2, "country_id": "UAE", "country_name": "UAE", "currency": "AED", "is_active": true, "discount_wallet_redeem_otp": false, "discount_wallet_redeem_pin": false, "points_redeem_otp": false, "points_redeem_pin": false, "created_date": "2025-05-07T10:35:33.646Z", "updated_date": "2026-03-17T15:49:31.976Z", "time_zone": { "value": "Asia/Dubai", "label": "(GMT+4:00) Abu Dhabi, Muscat", "offset": 4, "abbrev": "GST", "altName": "Gulf Standard Time" } } ], "total": 9, "page": 1, "pages": 1 }

Country Object — Field Reference

FieldTypeDescription
id number Numeric row ID. Use this with ?id= for single fetch.
country_id string Short country code identifier. Known values: BHD, JOR, KSA, KWT, OMN, ONL, QAT, UAE, GBR.
country_name string Full country name in uppercase. Used as the primary sort key (ASC).
currency string ISO 4217 currency code for this country (e.g. AED, SAR, BHD).
is_active boolean Whether this country is currently enabled on the platform.
discount_wallet_redeem_otp boolean When true, discount wallet redemptions in this country require OTP verification.
discount_wallet_redeem_pin boolean When true, discount wallet redemptions require PIN verification instead of (or in addition to) OTP.
points_redeem_otp boolean When true, loyalty points redemptions require OTP verification.
points_redeem_pin boolean When true, loyalty points redemptions require PIN verification.
created_date ISO datetime UTC timestamp of when the country record was created.
updated_date ISO datetime UTC timestamp of the last update to this country record.
time_zone object | null Timezone configuration. null for some countries. When present, includes the sub-fields below.
time_zone.value string IANA timezone identifier, e.g. Asia/Dubai.
time_zone.label string Human-readable label with UTC offset, e.g. (GMT+4:00) Abu Dhabi, Muscat.
time_zone.offset number UTC offset in hours (integer). e.g. 4 for Gulf Standard Time.
time_zone.abbrev string Short timezone abbreviation, e.g. GST, AST.
time_zone.altName string Full timezone name, e.g. Gulf Standard Time.

Error Reference

StatusConditionDescription
401 Missing / invalid x-api-key The x-api-key header is absent or does not match the server API key. Checked before any query logic runs.
404 ?id= not found No country record exists with the provided numeric ID.

Integration Notes

📄
Offset-based pagination — this endpoint uses skip (offset), not page, to control pagination. The page field in the response is read-only and derived as Math.floor(skip / limit) + 1. To iterate pages, increment skip by limit on each request: skip=0, skip=10, skip=20, etc.
🌍
time_zone may be null — not all country records have a timezone configured. Always null-check before reading time_zone.value or any sub-field. When present, time_zone.value is a valid IANA identifier suitable for use with libraries like Intl.DateTimeFormat or dayjs/moment.
🔒
Redemption flags drive client behaviour — the four boolean flags (discount_wallet_redeem_otp, discount_wallet_redeem_pin, points_redeem_otp, points_redeem_pin) are per-country and control which verification step your frontend or POS should prompt for during redemption. Read these at startup and cache per country rather than hardcoding verification flows.

Public — Brand

Returns brand records including colour palette, CDN media URLs, social links, payment provider, and the full nested country object. Two modes: single fetch (by id, brand_Id, or brand_code) or a filtered paginated list. Requires x-api-key authentication.

🔑
Authentication required — include your API key in the x-api-key request header on every call. Returns 401 if absent or incorrect, before any query runs.
GET /api/public/brand Single fetch or filtered list · sorted brand_name ASC

Mode is determined by the presence of id, brand_Id, or brand_code. Any one of those triggers single-fetch mode. Without them, the endpoint returns a paginated list sorted brand_name ASC. The hasFiles response field differs between modes — a detailed per-field object in single mode, true in list mode.

Required header
x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5
GET /api/public/brand?id={id}  |  ?brand_Id={code}  |  ?brand_code={code} Mode 1 · Single fetch

Fetches a single brand by numeric ID, brand code (brand_Id), or its alias brand_code. All three resolve the same record. Returns the full brand object plus a detailed hasFiles map showing which file fields have content. Returns 404 if no brand matches.

ParameterTypeRequiredDescription
id number one of Numeric database primary key of the brand.
brand_Id string one of Exact brand code, e.g. PKR02501. Case-insensitive in list mode; exact match in single mode.
brand_code string one of Alias for brand_Id. Behaves identically. Use whichever your codebase prefers.

Supply any one of id, brand_Id, or brand_code to enter single-fetch mode.

Fetch by numeric ID
curl -X GET \ "https://api1.staging.restroengage.com/api/public/brand?id=2" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
Fetch by brand code
curl -X GET \ "https://api1.staging.restroengage.com/api/public/brand?brand_Id=PKR02501" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
200 OK
{ "success": true, "data": { /* full brand object — see field reference */ }, "hasFiles": { "logo_image": true, "cover_image": false, "cover_video": false, "logo_image_url": true, "cover_image_url": false, "cover_video_url": false, "slider_image_1": false, "slider_image_2": false, "slider_image_3": false, "slider_image_1_url": false, "slider_image_2_url": false, "slider_image_3_url": false } }
404 Not Found
{ "success": false, "message": "Brand not found" }
401 Unauthorized
{ "success": false, "message": "Invalid or missing API key" }
GET /api/public/brand Mode 2 · Filtered paginated list

Returns a paginated list of brands sorted brand_name ASC. Supports name/code partial matching, country filtering (single or multi via comma-separated IDs), and a lightweight excludeFiles flag that replaces raw binary fields with booleans for lighter payloads. The response always includes "hasFiles": true at the root in list mode.

ParameterTypeRequiredDefaultDescription
page number optional 1 Page number for pagination.
limit number optional 10 Records per page.
brand_name string optional Case-insensitive partial match (ILIKE) on the brand display name.
brand_Id string optional Case-insensitive partial match on brand code. In list mode this is a partial search, unlike single mode where it is an exact match.
country_id number optional Filter brands by a single country's numeric ID.
country_ids string optional Comma-separated list of country IDs e.g. 1,2,3. Takes priority over country_id when both are supplied.
excludeFiles string optional Pass "true" to replace raw binary file fields (logo_image, cover_image, cover_video, slider fields) with true (if file exists) or null (if not). Use this to reduce payload size when you only need to know whether a file is present.
All brands — page 1
curl -X GET \ "https://api1.staging.restroengage.com/api/public/brand?page=1&limit=10" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
Filter by name + country
curl -X GET \ "https://api1.staging.restroengage.com/api/public/brand?brand_name=park&country_id=1" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
Multi-country filter + lightweight payload
curl -X GET \ "https://api1.staging.restroengage.com/api/public/brand?country_ids=1,2,3&excludeFiles=true" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
200 OK
{ "success": true, "data": [ { "id": 2, "brand_name": "PARKERS", "brand_Id": "PKR02501", "description": "", "social_links": [ "https://www.instagram.com/parkers.ksa/", "https://www.tiktok.com/@byparkers" ], "logo_image": null, "cover_image": null, "cover_video": null, "logo_image_url": "https://api.indpt.restroengage.com/api/media/PKR02501/PKR02501_logo_image.png", "cover_image_url": "https://api.indpt.restroengage.com/api/media/PKR02501/PKR02501_cover_image.png", "cover_video_url": null, "brand_main_color": "#ffffff", "brand_font_color": "#000000", "brand_hover_color": "#000000", "brand_secondary_color": "#2b7a4d", "payment_provider": "OTHER", "is_active": false, "whatsapp_message": false, "email_message": false, "country_id": 1, "created_date": "2025-05-06T09:35:57.393Z", "updated_date": "2026-03-10T12:05:52.854Z", "country": { "id": 1, "country_id": "KSA", "country_name": "KSA", "currency": "SAR", "is_active": true, "discount_wallet_redeem_otp": false, "discount_wallet_redeem_pin": false, "points_redeem_otp": false, "points_redeem_pin": false, "time_zone": { "value": "Asia/Kuwait", "label": "(GMT+3:00) Kuwait, Riyadh", "offset": 3, "abbrev": "AST", "altName": "Arabian Standard Time" } } } ], "total": 21, "page": 1, "pages": 3, "hasFiles": true }

Brand Object — Field Reference

FieldTypeDescription
id number Numeric database primary key.
brand_name string Display name of the brand. Primary sort key (ASC).
brand_Id string Unique brand code identifier (e.g. PKR02501). Used in media URL paths.
description string Brand description text. May be an empty string.
social_links string[] Array of social media URLs. May be an empty array [].
logo_image null Raw binary field — always null in API responses. Use logo_image_url instead.
cover_image null Raw binary field — always null in API responses. Use cover_image_url instead.
cover_video null Raw binary field — always null in API responses. Use cover_video_url instead.
logo_image_url string | null CDN URL for the brand logo PNG. Pattern: /api/media/{brand_Id}/{brand_Id}_logo_image.png.
cover_image_url string | null CDN URL for the brand cover image.
cover_video_url string | null CDN URL for the brand cover video. null if no video is uploaded.
brand_main_color hex string Primary brand colour as a hex value (e.g. #ffffff).
brand_font_color hex string Text/font colour for branded UI surfaces.
brand_hover_color hex string Hover or interactive state colour.
brand_secondary_color hex string Secondary accent colour for branded surfaces.
payment_provider string Payment provider configured for this brand (e.g. OTHER, GEIDEA).
is_active boolean Whether the brand is currently active on the platform.
whatsapp_message boolean Whether WhatsApp messaging notifications are enabled for this brand.
email_message boolean Whether email notifications are enabled for this brand.
country_id number Foreign key referencing the brand's country record.
country object Full nested country object including redemption flags and time_zone. Same schema as the Public Country field reference.
created_date ISO datetime UTC timestamp of brand record creation.
updated_date ISO datetime UTC timestamp of the last update to this brand record.
hasFiles (single mode) object Detailed per-field boolean map. Each key corresponds to a file field and is true if content exists, false if not. Covers: logo_image, cover_image, cover_video, *_url variants, and slider_image_1/2/3 + URL variants.
hasFiles (list mode) boolean Always true at the response root in list mode. Per-item file presence is not individually mapped.

Error Reference

StatusConditionDescription
401 Missing / invalid x-api-key The x-api-key header is absent or incorrect. Validated before any query logic runs.
404 Single fetch — brand not found No brand matches the provided id, brand_Id, or brand_code.

Integration Notes

🖼️
Always use URL fields for medialogo_image, cover_image, and cover_video are raw binary DB columns and are always null in API responses. Use logo_image_url, cover_image_url, and cover_video_url for any display or download. The URL pattern is /api/media/{brand_Id}/{brand_Id}_{field_name}.png.
Use excludeFiles=true for lighter list payloads — when building index pages or dropdowns where you only need to know whether a file exists (not the content), pass excludeFiles=true. Binary fields are replaced with true or null, significantly reducing payload size for large result sets.
🌍
country_ids takes priority over country_id — when both are supplied in the same request, country_ids (comma-separated) wins and country_id is ignored. To filter by multiple countries, always use country_ids=1,2,3 rather than making multiple requests.
🔤
brand_Id behaves differently per mode — in single-fetch mode it is an exact match; in list mode it is a case-insensitive partial match (ILIKE). Use the single-fetch mode with the exact brand code when you need a guaranteed single result.

Public — Branch

Returns branch records with nested brand, country (with timezone), sections, and waiting list. Two modes: single fetch by ID (with capacity adjusted for live reservations) or a filtered paginated list. Requires x-api-key authentication.

🔑
Authentication required — include your API key in the x-api-key request header. Returns 401 before any query runs if absent or incorrect.
GET /api/public/branch Single fetch or filtered list · sorted branch_name ASC

Mode is selected by the presence of ?id=. Without it, returns a paginated list. The public variant (/api/public/branch) always includes a waiting_list[] per branch — the private version does not. All list results sorted branch_name ASC.

Required header
x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5
GET /api/public/branch?id={id} Mode 1 · Single fetch by ID

Returns a single branch by numeric row ID. Section capacities are live-adjusted — each section's capacity reflects the raw capacity minus the count of current paid active reservations for that section. Also returns a per-field hasFiles object. Returns 404 if no branch matches.

ParameterTypeRequiredDescription
id number required Numeric database primary key of the branch (e.g. 1096).
curl -X GET \ "https://api1.staging.restroengage.com/api/public/branch?id=1096" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
200 OK
{ "success": true, "data": { /* full branch object — see field reference below */ }, "hasFiles": { "logo_image": false, "cover_image": false, "cover_video": false, "logo_image_url": true, "cover_image_url": true, "cover_video_url": false } }
404 Not Found
{ "success": false, "message": "Branch not found" }
401 Unauthorized
{ "success": false, "message": "Invalid or missing API key" }
GET /api/public/branch Mode 2 · Filtered paginated list

Returns a paginated list of branches sorted branch_name ASC. Each item includes nested brand, country, sections[], and waiting_list[]. Supports filtering by brand (single ID, comma-separated IDs, or brand code), country, name/code search, and feature flags. Setting limit >= 500 disables pagination and returns all results in one page.

ParameterTypeRequiredDefaultDescription
page number optional 1 Page number.
limit number optional 10 Records per page. Setting 500 or above disables pagination — returns all results in a single page with pages: 1.
branch_name string optional Case-insensitive partial match (ILIKE) on branch display name.
branch_code string optional Case-insensitive partial match on branch code.
brand_id number optional Filter by a single brand's numeric ID.
brand_ids string optional Comma-separated brand numeric IDs, e.g. 2,4,7. Takes priority over brand_id when both are present.
brand_Id string optional Filter by brand code string (e.g. PKR02501). Takes priority over brand_ids — resolved to a numeric brand ID server-side before querying.
country number optional Filter by country numeric ID.
isbookable string optional Pass "true" to return only branches where is_active = true (bookable branches only).
play_enable string optional Pass "true" or "false" to filter by the play_enable feature flag.
search string optional Case-insensitive search across branch_name, branch_code, and numeric id.
excludeFiles string optional Pass "true" to strip raw binary file columns (logo_image, cover_image, cover_video) for a lighter payload.
All branches — page 1
curl -X GET \ "https://api1.staging.restroengage.com/api/public/branch?page=1&limit=10" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
Filter by brand code · bookable only · light payload
curl -X GET \ "https://api1.staging.restroengage.com/api/public/branch?brand_Id=PKR02501&isbookable=true&excludeFiles=true" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
Multi-brand filter + play feature
curl -X GET \ "https://api1.staging.restroengage.com/api/public/branch?brand_ids=2,4,7&play_enable=true" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
Fetch ALL branches in one page (limit ≥ 500)
curl -X GET \ "https://api1.staging.restroengage.com/api/public/branch?limit=500" \ -H "x-api-key: x4d-loyality-api-7f3a12b8e9c6d5f4a2b1e0d9c8b7a6f5"
200 OK
{ "success": true, "data": [ { "id": 1096, "brand_id": 4, "branch_name": "10 Years Later.same Park.New Chapter", "branch_code": "Parkers-Events-2", "latitude": null, "longitude": null, "map_code": "https://maps.app.goo.gl/v6eJ8BJuWrchFi5i6", "address": "Al Khazan Park", "logo_image": null, "cover_image": null, "cover_video": null, "logo_image_url": "/media/PAK2001/Parkers-Events-2/logo_image.webp", "cover_image_url": "https://api.indpt.restroengage.com/api/media/.../cover_image.jpeg", "cover_video_url": null, "club_id": "", "chefz_id": null, "chefz_reservation": false, "is_active": true, "is_active_waitinglist": false, "is_active_payment": false, "new_customer_reservation_otp": false, "new_customer_waiting_list_otp": false, "remaining_capacity_hide": true, "time_slot_hide": false, "section_wise_time_slots": false, "show_time_range": false, "show_slots_header": true, "show_period_headers": true, "child_seats": false, "play_enable": false, "description": null, "waiting_list_config": null, "venue_details": { "availability_title": "t", "availability_subtitle": null, "availability_title_font_size": null, "availability_subtitle_font_size": null }, "reservation_link": null, "oracle_token_id": null, "orgShortName": null, "locRef": null, "rvcRef": null, "country_id": 2, "organization_id": 1, "created_date": "2026-04-27T14:16:46.888Z", "updated_date": "2026-06-11T10:44:38.610Z", "brand": { "id": 4, "brand_name": "PARKERS", "brand_Id": "PAK2001", "brand_main_color": "#ffffff", "brand_font_color": "#000000", "payment_provider": "OTHER", "is_active": false }, "country": { "id": 2, "country_id": "UAE", "country_name": "UAE", "currency": "AED", "is_active": true, "time_zone": { "value": "Asia/Dubai", "label": "(GMT+4:00) Abu Dhabi, Muscat", "offset": 4 } }, "sections": [ { "id": 1757, "name": "all", "capacity": 200, "is_active": true } ], "waiting_list": [] } ], "total": 141, "page": 1, "pages": 15, "hasFiles": true }

Branch Object — Field Reference

FieldTypeDescription
idnumberNumeric database primary key.
branch_namestringDisplay name. Primary sort key (ASC).
branch_codestringUnique branch code identifier. Used in media URL paths.
brand_idnumberForeign key referencing the parent brand.
country_idnumberForeign key referencing the branch country.
latitude / longitudenumber | nullGPS coordinates. May be null if not configured.
map_codestring | nullGoogle Maps share URL for the branch location.
addressstring | nullPhysical address text.
logo_image / cover_image / cover_videonullRaw binary DB columns — always null in responses. Use the _url variants instead.
logo_image_urlstring | nullCDN URL for branch logo. Pattern: /media/{brand_Id}/{branch_code}/logo_image.webp.
cover_image_urlstring | nullCDN URL for branch cover image.
cover_video_urlstring | nullCDN URL for branch cover video. null if not uploaded.
club_idstringExternal loyalty club identifier. May be an empty string if not configured.
chefz_idstring | nullChefz platform integration ID.
chefz_reservationbooleantrue if Chefz-sourced reservations are enabled for this branch.
is_activebooleanWhether reservation booking is active. Filtered by isbookable=true.
is_active_waitinglistbooleanWhether the waiting list feature is active for this branch.
is_active_paymentbooleanWhether payment collection at booking is enabled.
new_customer_reservation_otpbooleantrue if new customers must verify via OTP to complete a reservation.
new_customer_waiting_list_otpbooleantrue if new customers must verify via OTP to join the waiting list.
remaining_capacity_hidebooleantrue hides remaining slot capacity from the public-facing booking UI.
time_slot_hidebooleantrue hides time slots from the booking UI.
section_wise_time_slotsbooleanWhen true, time slots are managed and shown per section rather than for the whole branch.
show_slots_headerbooleanDisplay the slots header in the booking UI.
show_period_headersbooleanDisplay period labels (e.g. Morning / Evening) in the booking UI.
show_time_rangebooleanShow start–end time ranges instead of single slot times.
child_seatsbooleanWhether child seat options are shown during booking.
play_enablebooleanPlay feature flag. Filterable via play_enable=true/false.
descriptionstring | nullBranch description text.
waiting_list_configobject | nullWaiting list behaviour configuration. null if defaults apply.
venue_detailsobject | nullCustom availability UI text: availability_title, availability_subtitle, and font size overrides. null if not configured.
reservation_linkstring | nullExternal reservation URL override, if applicable.
oracle_token_idstring | nullOracle Simphony POS integration token.
orgShortName / locRef / rvcRefstring | nullOracle POS reference fields for organisation, location, and revenue centre.
checkEmployeeRef / orderTypeRefstring | nullAdditional Oracle POS integration references.
organization_idnumberPlatform organisation FK.
created_date / updated_dateISO datetimeUTC creation and last-update timestamps.
brandobjectNested brand summary: id, brand_name, brand_Id, colour fields, payment_provider, is_active.
countryobjectNested country with currency, is_active, and time_zone (IANA value + label + offset).
sectionsarrayBranch sections — each with id, name, capacity, is_active. In single-fetch mode, capacity is adjusted by subtracting the current count of paid active reservations.
waiting_listarrayCurrent waiting list entries for the branch. Always included in public mode (/api/public/branch); absent in the private endpoint. May be an empty array.

Error Reference

StatusConditionDescription
401 Missing / invalid x-api-key The x-api-key header is absent or does not match the server key. Evaluated before any query runs.
404 ?id= not found No branch record exists with the specified numeric ID.

Integration Notes

📊
Live-adjusted section capacity in single mode — when fetching a single branch by ID, sections[].capacity is not the raw configured value. The server subtracts the count of current paid active reservations for that section before returning. Use this for real-time availability displays. The list mode returns raw capacity values.
📋
waiting_list[] is public-onlywaiting_list[] per branch is only included in responses from /api/public/branch. The private /api/branch endpoint does not include it. Always use the public endpoint if you need waiting list data alongside branch details.
Fetch all branches in one call — pass limit=500 (or any value ≥ 500) to disable pagination entirely. The response returns all matching branches in a single page with pages: 1. Useful for populating dropdowns or branch maps at app startup.
🏷️
Brand filter priority: brand_Id (string code) brand_ids (comma-separated IDs) brand_id (single numeric ID). When brand_Id is present it takes highest priority; it is resolved to a numeric ID server-side before the query runs.
🖼️
Always use URL fields for medialogo_image, cover_image, and cover_video are raw binary DB columns and are always null in API responses. Use logo_image_url, cover_image_url, and cover_video_url for display. URL pattern: /media/{brand_Id}/{branch_code}/{field}.webp.

Salt Al Bahar — Public API

Unauthenticated public endpoints for Salt Al Bahar event slots, reservations, and wallet transactions. Used by the Salt Al Bahar booking flow and legacy public clients.

🔓
No authentication required — these routes do not use x-api-key or JWT. They are exposed under the /api/public/salt-al-bahar/ and /api/public/booking/ prefixes.
GET /api/public/salt-al-bahar/available-slots Event slot grid

Returns available time slots for a Salt Al Bahar event on a given date, including capacity, pricing, entry fee, and bookable flags. Accounts for paid bookings and temporary cart holds.

ParameterTypeRequiredDescription
event_idnumberrequiredSalt Al Bahar event ID.
datestringrequiredBooking date (YYYY-MM-DD).
party_sizenumberoptionalParty size for capacity checks. Default 1.
curl -X GET \ "https://api1.staging.restroengage.com/api/public/salt-al-bahar/available-slots?event_id=1&date=2026-06-11&party_size=2"
200 OK
{ "success": true, "data": [ { "event_rule_id": 12, "start_time": "2026-06-11T17:00:00.000Z", "end_time": "2026-06-11T19:00:00.000Z", "available": true, "remaining_capacity": 8, "price": 150, "entry_fee": 25, "total_price": 175 } ], "event": { /* event metadata */ } }
POST /api/public/salt-al-bahar/available-slots/slots Bookable slots (POST body)

Returns bookable slots grouped into regular_slots and mid_night_slots, with event rules and capacity counts. Only rules marked bookable are included.

FieldTypeRequiredDescription
event_idnumberrequiredEvent ID.
booking_datestringrequiredDate (YYYY-MM-DD).
party_sizenumberoptionalDefault 1.
curl -X POST \ "https://api1.staging.restroengage.com/api/public/salt-al-bahar/available-slots/slots" \ -H "Content-Type: application/json" \ -d '{"event_id":1,"booking_date":"2026-06-11","party_size":2}'
GET POST PUT /api/public/salt-al-bahar/reservations Event bookings

List, create, or update Salt Al Bahar event bookings (EventBooking). Creates a customer by mobile if one does not exist. PUT requires ?id={numeric_id}.

GET — query parameters

ParameterTypeRequiredDescription
booking_idstringoptionalFilter by booking ID (e.g. SALT12345).
customer_idnumberoptionalFilter by customer ID.
event_idnumberoptionalFilter by event ID.
statusstringoptionalFilter by booking status.
limitnumberoptionalPage size. Default 10.
skipnumberoptionalOffset pagination. Default 0.

POST — body (create)

FieldTypeRequiredDescription
mobile_numberstringrequiredCustomer mobile (used as lookup / create key).
event_idnumberrequiredEvent to book.
quantitynumberoptionalDefault 1.
event_rule_idnumberoptionalRule for pricing.
datestringoptionalBooking date.
start_timestringoptionalSlot start time.
end_timestringoptionalSlot end time.
payment_typestringoptionalCASH or CARD. Default CASH (marks PAID).

PUT — body (update) · query ?id=

FieldTypeDescription
statusstringe.g. CANCELED
payment_statusstringPayment status update.
datestringReschedule booking date.
start_timestringUpdated start time.
end_timestringUpdated end time.
Example — create reservation
curl -X POST \ "https://api1.staging.restroengage.com/api/public/salt-al-bahar/reservations" \ -H "Content-Type: application/json" \ -d '{"mobile_number":"966501234567","event_id":1,"quantity":1}'
POST GET /api/public/salt-al-bahar/transaction Wallet credits / debits

Create wallet transactions or list transaction history. Summary available at /api/public/salt-al-bahar/transaction/summery (note: path spelling matches the API).

POST — body

FieldTypeRequiredDescription
customer_idnumberrequiredCustomer ID.
wallet_idnumberrequiredWallet ID.
amountnumberrequiredTransaction amount.
transaction_typestringrequiredCREDIT or DEBIT.
payment_methodstringoptionalPayment method label.
notestringoptionalTransaction note.
event_booking_idnumberoptionalLinked event booking.

GET — query parameters

ParameterTypeDescription
wallet_idnumberFilter by wallet.
customer_wallet_idnumberFilter by customer-wallet link.
limitnumberPage size.
skipnumberOffset.
GET /api/public/salt-al-bahar/transaction/summery Summary

Returns aggregated wallet transaction summary. Supports the same filter query params as the list endpoint.

Public Booking API

Branch-level reservation slots and lists for the RestroEngage booking widget. Used by restroengage-dashboard and restroengage-reservation.

🔓
No authentication required — base path /api/public/booking. All endpoints below are GET in the current Nest API (list / fetch modes).
GET /api/public/booking/available-slots Branch table slots

Returns section-based available slots for a branch on a given date, respecting custom availability rules, timezone, and live reservation counts.

ParameterTypeRequiredDescription
branchIdnumber | stringrequiredBranch numeric ID or branch_code.
datestringrequiredDate (YYYY-MM-DD).
partySizenumberoptionalDefault 2.
customRuleIdnumberoptionalForce a specific custom availability rule.
showHiddenbooleanoptionalPass true to include hidden slots.
cURL
curl -X GET \ "https://api1.staging.restroengage.com/api/public/booking/available-slots?branchId=1096&date=2026-06-11&partySize=4"
GET /api/public/booking/reservation Branch reservations

Fetch one reservation by ?id= or a paginated list with optional filters (branch, customer mobile, date range, status).

ParameterTypeDescription
idnumberSingle reservation by row ID.
pagenumberPage number. Default 1.
limitnumberPage size. Default 10.
branch_idnumberFilter by branch.
customer_mobile_numberstringFilter by customer mobile.
GET /api/public/booking/waiting-list Waiting list

Returns waiting list entries for a branch. Auto-expires READY entries older than 15 minutes to NOSHOW before responding. Use ?id= for a single entry or list filters for multiple.

GET /api/public/booking/walkin-list Walk-in list

Returns walk-in queue entries for a branch. Supports single fetch via ?id= or filtered list mode.