openapi: 3.0.3
info:
title: Pixalink
description: ''
version: 1.0.0
servers:
-
url: '{{ config("app.url") }}'
tags:
-
name: Categories
description: "\nCategories provide a hierarchical classification system for organising spaces within the platform.
\nThe system uses a two-level structure where main categories (e.g., Food & Beverage, Retail)\ncontain subcategories (e.g., Restaurant, Cafe, Hotel) that spaces can be assigned to.
\nThis helps in filtering and discovering spaces based on their business type or industry."
-
name: Credits
description: ''
-
name: 'Customer Paid Memberships'
description: ''
-
name: 'Customer Rewards'
description: "\nAPIs for managing customer rewards"
-
name: Customers
description: ''
-
name: Organisations
description: ''
-
name: 'POS Credits'
description: ''
-
name: 'POS Transaction'
description: ''
-
name: 'Paid Membership Types'
description: ''
-
name: Payments
description: ''
-
name: Plans
description: ''
-
name: 'Reward Validation'
description: "\nAPIs for validating reward redemption codes"
-
name: Rewards
description: ''
-
name: Roles
description: ''
-
name: Spaces
description: ''
-
name: Transactions
description: ''
-
name: Users
description: ''
-
name: Calendars
description: ''
-
name: Reservations
description: ''
-
name: Endpoints
description: ''
-
name: 'Location Management'
description: ''
-
name: SSO
description: ''
components:
securitySchemes:
default:
type: http
scheme: bearer
description: 'To use our API, please contact our support team to request API access. Once approved, you can generate your access token through your admin panel by visiting Personal Access Tokens page.'
security:
-
default: []
paths:
/api/categories:
get:
summary: 'List categories'
operationId: listCategories
description: "Returns a paginated list of categories. By default, only returns second level categories\n(categories that have a parent)."
parameters:
-
in: query
name: page
description: 'Page number for pagination.'
example: 1
required: false
schema:
type: integer
description: 'Page number for pagination.'
example: 1
-
in: query
name: per_page
description: 'Number of items per page.'
example: 15
required: false
schema:
type: integer
description: 'Number of items per page.'
example: 15
-
in: query
name: include
description: "Comma-separated list of relations to include. Allowed values:\n* parent"
example: parent
required: false
schema:
type: string
description: "Comma-separated list of relations to include. Allowed values:\n* parent"
example: parent
-
in: query
name: sort
description: "Field to sort by. Prefix with - for descending order. Allowed values:\n* name\n* created_at\n* updated_at"
example: '-created_at'
required: false
schema:
type: string
description: "Field to sort by. Prefix with - for descending order. Allowed values:\n* name\n* created_at\n* updated_at"
example: '-created_at'
-
in: query
name: 'filter[name]'
description: 'Filter by name (partial match).'
example: Restaurant
required: false
schema:
type: string
description: 'Filter by name (partial match).'
example: Restaurant
-
in: query
name: 'filter[parent_id]'
description: 'Filter by parent ID. If set to null or 0, returns main categories.'
example: 1
required: false
schema:
type: integer
description: 'Filter by parent ID. If set to null or 0, returns main categories.'
example: 1
-
in: query
name: 'filter[id]'
description: 'Filter by ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by ID.'
example: 1
-
in: query
name: 'filter[slug]'
description: 'Filter by exact slug match.'
example: restaurant
required: false
schema:
type: string
description: 'Filter by exact slug match.'
example: restaurant
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Default (Second Level Categories)'
type: object
example:
data:
-
id: 5
name: Restaurant
parent_id: 1
-
id: 6
name: Cafe
parent_id: 1
-
id: 7
name: Hotel
parent_id: 2
links:
first: '@{{$baseUrl}}/api/categories?page=1'
last: '@{{$baseUrl}}/api/categories?page=1'
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
path: '@{{$baseUrl}}/api/categories'
per_page: 15
to: 3
total: 3
properties:
data:
type: array
example:
-
id: 5
name: Restaurant
parent_id: 1
-
id: 6
name: Cafe
parent_id: 1
-
id: 7
name: Hotel
parent_id: 2
items:
type: object
properties:
id:
type: integer
example: 5
name:
type: string
example: Restaurant
parent_id:
type: integer
example: 1
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/api/categories?page=1'
last:
type: string
example: '@{{$baseUrl}}/api/categories?page=1'
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
path:
type: string
example: '@{{$baseUrl}}/api/categories'
per_page:
type: integer
example: 15
to:
type: integer
example: 3
total:
type: integer
example: 3
-
description: 'With Parent Included'
type: object
example:
data:
-
id: 5
name: Restaurant
parent_id: 1
parent:
id: 1
name: 'Food & Beverage'
-
id: 6
name: Cafe
parent_id: 1
parent:
id: 1
name: 'Food & Beverage'
links:
first: '@{{$baseUrl}}/api/categories?include=parent&page=1'
last: '@{{$baseUrl}}/api/categories?include=parent&page=1'
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
path: '@{{$baseUrl}}/api/categories'
per_page: 15
to: 2
total: 2
properties:
data:
type: array
example:
-
id: 5
name: Restaurant
parent_id: 1
parent:
id: 1
name: 'Food & Beverage'
-
id: 6
name: Cafe
parent_id: 1
parent:
id: 1
name: 'Food & Beverage'
items:
type: object
properties:
id:
type: integer
example: 5
name:
type: string
example: Restaurant
parent_id:
type: integer
example: 1
parent:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Food & Beverage'
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/api/categories?include=parent&page=1'
last:
type: string
example: '@{{$baseUrl}}/api/categories?include=parent&page=1'
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
path:
type: string
example: '@{{$baseUrl}}/api/categories'
per_page:
type: integer
example: 15
to:
type: integer
example: 2
total:
type: integer
example: 2
tags:
- Categories
/api/credits:
get:
summary: 'List Credits'
operationId: listCredits
description: 'Get a paginated list of credits for the organisation.'
parameters:
-
in: query
name: 'filter[customer_id]'
description: 'Filter by customer ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by customer ID.'
example: 1
-
in: query
name: 'filter[type]'
description: 'Filter by credit type.'
example: 'Top Up'
required: false
schema:
type: string
description: 'Filter by credit type.'
example: 'Top Up'
-
in: query
name: 'filter[space_id]'
description: 'Filter by space ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by space ID.'
example: 1
-
in: query
name: 'filter[amount]'
description: 'Filter by amount with operators.'
example: '>100'
required: false
schema:
type: string
description: 'Filter by amount with operators.'
example: '>100'
-
in: query
name: sort
description: 'Sort field (created_at, amount).'
example: '-created_at'
required: false
schema:
type: string
description: 'Sort field (created_at, amount).'
example: '-created_at'
-
in: query
name: include
description: 'Include relationships (customer, space, reward).'
example: customer
required: false
schema:
type: string
description: 'Include relationships (customer, space, reward).'
example: customer
-
in: query
name: per_page
description: 'Number of records per page.'
example: 15
required: false
schema:
type: integer
description: 'Number of records per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
customer_id: 42
space_id: 1
amount: '50.00'
operation: +
type: 'Top Up'
remarks: 'Birthday top-up'
created_at: '2025-05-06T14:30:01.000000Z'
updated_at: '2025-05-06T14:30:01.000000Z'
links:
first: ...
last: ...
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
per_page: 15
to: 1
total: 1
properties:
data:
type: array
example:
-
id: 1
customer_id: 42
space_id: 1
amount: '50.00'
operation: +
type: 'Top Up'
remarks: 'Birthday top-up'
created_at: '2025-05-06T14:30:01.000000Z'
updated_at: '2025-05-06T14:30:01.000000Z'
items:
type: object
properties:
id:
type: integer
example: 1
customer_id:
type: integer
example: 42
space_id:
type: integer
example: 1
amount:
type: string
example: '50.00'
operation:
type: string
example: +
type:
type: string
example: 'Top Up'
remarks:
type: string
example: 'Birthday top-up'
created_at:
type: string
example: '2025-05-06T14:30:01.000000Z'
updated_at:
type: string
example: '2025-05-06T14:30:01.000000Z'
links:
type: object
properties:
first:
type: string
example: ...
last:
type: string
example: ...
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
per_page:
type: integer
example: 15
to:
type: integer
example: 1
total:
type: integer
example: 1
401:
description: Unauthenticated
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
403:
description: 'Missing scope'
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
tags:
- Credits
post:
summary: 'Create Credit'
operationId: createCredit
description: 'Creates a new credit entry for a customer (top up, deduct, revert, etc.).'
parameters: []
responses:
201:
description: 'Top Up created'
content:
application/json:
schema:
type: object
example:
data:
id: 1
customer_id: 42
space_id: 1
amount: '50.00'
operation: +
type: 'Top Up'
remarks: 'Birthday top-up'
created_at: '2025-05-06T14:30:01.000000Z'
updated_at: '2025-05-06T14:30:01.000000Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
customer_id:
type: integer
example: 42
space_id:
type: integer
example: 1
amount:
type: string
example: '50.00'
operation:
type: string
example: +
type:
type: string
example: 'Top Up'
remarks:
type: string
example: 'Birthday top-up'
created_at:
type: string
example: '2025-05-06T14:30:01.000000Z'
updated_at:
type: string
example: '2025-05-06T14:30:01.000000Z'
403:
description: 'Missing scope'
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
422:
description: 'Validation failed'
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
type:
- 'The selected type is invalid.'
amount:
- 'The amount field is required.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
type:
type: array
example:
- 'The selected type is invalid.'
items:
type: string
amount:
type: array
example:
- 'The amount field is required.'
items:
type: string
tags:
- Credits
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
customer_id:
type: integer
description: 'Customer ID. Required if phone_number not provided.'
example: 42
phone_number:
type: string
description: 'Customer phone number. Required if customer_id not provided.'
example: '+60123456789'
amount:
type: numeric
description: 'Credit amount in dollars.'
example: '50.00'
type:
type: string
description: 'Credit type. One of: `Top Up`, `Deduct`, `Bonus`, `Refund`, `Referral`, `Revert`.'
example: 'Top Up'
space_id:
type: integer
description: 'Space ID.'
example: 1
remarks:
type: string
description: 'Additional remarks. Max 500 characters.'
example: 'Birthday top-up'
custom_properties:
type: object
description: 'Custom properties for this credit entry.'
example: []
properties: { }
source_credit_id:
type: integer
description: 'Required when type is `Revert`. The original TopUp credit to revert.'
example: 10
required:
- amount
- type
'/api/credits/{id}':
get:
summary: 'Get Credit Details'
operationId: getCreditDetails
description: 'Retrieve detailed information about a specific credit.'
parameters:
-
in: query
name: include
description: 'Relationships to include (customer, space, reward).'
example: customer
required: false
schema:
type: string
description: 'Relationships to include (customer, space, reward).'
example: customer
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
customer_id: 42
space_id: 1
amount: '50.00'
operation: +
type: 'Top Up'
remarks: 'Birthday top-up'
created_at: '2025-05-06T14:30:01.000000Z'
updated_at: '2025-05-06T14:30:01.000000Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
customer_id:
type: integer
example: 42
space_id:
type: integer
example: 1
amount:
type: string
example: '50.00'
operation:
type: string
example: +
type:
type: string
example: 'Top Up'
remarks:
type: string
example: 'Birthday top-up'
created_at:
type: string
example: '2025-05-06T14:30:01.000000Z'
updated_at:
type: string
example: '2025-05-06T14:30:01.000000Z'
403:
description: 'Missing scope'
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
404:
description: 'Not found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found.'
properties:
message:
type: string
example: 'Record not found.'
tags:
- Credits
parameters:
-
in: path
name: id
description: 'The ID of the credit.'
example: 3
required: true
schema:
type: integer
-
in: path
name: credit
description: 'The ID of the credit.'
example: 1
required: true
schema:
type: integer
/api/customer_paid_memberships:
get:
summary: 'List Customer Paid Memberships'
operationId: listCustomerPaidMemberships
description: "Returns a paginated list of customer paid membership instances. Records are scoped to the\nauthenticated user's organisation via the model's global scope."
parameters:
-
in: query
name: include
description: 'Comma-separated relationships to include. Available: `customer`, `paidMembershipType`, `transactions`, `payments`, `activePayment`. Use `payments` to retrieve all payment records for reconciliation, or `activePayment` to retrieve only the currently linked payment.'
example: 'paidMembershipType,customer'
required: false
schema:
type: string
description: 'Comma-separated relationships to include. Available: `customer`, `paidMembershipType`, `transactions`, `payments`, `activePayment`. Use `payments` to retrieve all payment records for reconciliation, or `activePayment` to retrieve only the currently linked payment.'
example: 'paidMembershipType,customer'
-
in: query
name: 'filter[customer_id]'
description: 'Filter by customer ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by customer ID.'
example: 1
-
in: query
name: 'filter[paid_membership_type_id]'
description: 'Filter by membership type ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by membership type ID.'
example: 1
-
in: query
name: 'filter[is_active]'
description: 'Filter by active flag.'
example: true
required: false
schema:
type: boolean
description: 'Filter by active flag.'
example: true
-
in: query
name: 'filter[billing_cycle]'
description: 'Filter by billing cycle (`monthly` or `yearly`).'
example: yearly
required: false
schema:
type: string
description: 'Filter by billing cycle (`monthly` or `yearly`).'
example: yearly
-
in: query
name: 'filter[expires_at]'
description: 'Filter by expiry date using comparison operators (`>`, `<`, `=`). ISO 8601 date.'
example: '>2025-04-01'
required: false
schema:
type: string
description: 'Filter by expiry date using comparison operators (`>`, `<`, `=`). ISO 8601 date.'
example: '>2025-04-01'
-
in: query
name: sort
description: 'Sort field and direction. Prefix with `-` for descending. Available: `created_at`, `starts_at`, `expires_at`. Defaults to `-created_at`.'
example: '-expires_at'
required: false
schema:
type: string
description: 'Sort field and direction. Prefix with `-` for descending. Available: `created_at`, `starts_at`, `expires_at`. Defaults to `-created_at`.'
example: '-expires_at'
-
in: query
name: per_page
description: 'Number of records per page. Defaults to 15.'
example: 15
required: false
schema:
type: integer
description: 'Number of records per page. Defaults to 15.'
example: 15
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- 'Customer Paid Memberships'
post:
summary: 'Assign a Paid Membership'
operationId: assignAPaidMembership
description: "Assigns a paid membership of the given type and billing cycle to a customer. Any existing\nactive membership for that customer is automatically deactivated. Delegates lifecycle logic\nto `PaidMembershipService::assignMembership`, which records a `ManualAssign` transaction and\nissues any rewards configured on the membership type.\n\nIf the integrator has already collected payment in their own system, an optional `payment`\nobject may be supplied. When present, a `Payment` row is recorded against the membership\nand a `ManualPayment` transaction is logged. The accepted `payment.method` values (`cash`,\n`card`, `e_wallet`) are deliberately limited to manually-recorded instruments — gateway\nmethods are not accepted via this endpoint. Omit `payment` entirely for free/comp\nassignments."
parameters: []
responses:
201:
description: Created
content:
application/json:
schema:
type: object
example:
data:
id: 1
membership_status: active
membership_type_name: 'VIP Gold'
billing_cycle: monthly
starts_at: '2025-04-01'
expires_at: '2025-05-01'
is_active: true
message: 'Paid membership assigned successfully'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
membership_status:
type: string
example: active
membership_type_name:
type: string
example: 'VIP Gold'
billing_cycle:
type: string
example: monthly
starts_at:
type: string
example: '2025-04-01'
expires_at:
type: string
example: '2025-05-01'
is_active:
type: boolean
example: true
message:
type: string
example: 'Paid membership assigned successfully'
422:
description: 'Validation Error'
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
billing_cycle:
- 'The selected billing cycle is not available for this membership type.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
billing_cycle:
type: array
example:
- 'The selected billing cycle is not available for this membership type.'
items:
type: string
tags:
- 'Customer Paid Memberships'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
customer_id:
type: string
description: 'The ID of the customer to assign the membership to. Must belong to your organisation. The id of an existing record in the customers table.'
example: 1
paid_membership_type_id:
type: string
description: 'The ID of the paid membership type catalog entry. Must belong to your organisation. The id of an existing record in the paid_membership_types table.'
example: 1
billing_cycle:
type: string
description: 'The billing cycle for this membership. Must be one of the cycles configured in the membership type pricing options.'
example: monthly
enum:
- monthly
- yearly
starts_at:
type: string
description: 'Optional start date. Defaults to now if omitted. ISO 8601 (YYYY-MM-DD). Must be a valid date.'
example: '2025-04-01'
nullable: true
remarks:
type: string
description: 'Optional internal note explaining the assignment (e.g. promotion code, manual signup reason). Must not be greater than 500 characters.'
example: 'Manually assigned by support after refund.'
nullable: true
payment:
type: object
description: 'Optional payment record. Use when the integrator has already collected payment in their own system and wants Pixalink to record it alongside the membership. Omit this object entirely for a free/comp assignment.'
example: null
properties:
method:
type: string
description: 'The payment instrument used. Must be one of: `cash`, `card`, `e_wallet`. Payment gateway methods are not accepted here. This field is required when payment is present.'
example: cash
enum:
- cash
- card
- e_wallet
amount:
type: number
description: 'The amount collected, in major currency units (e.g. `99.00` for RM 99.00). Must be greater than zero — omit the `payment` block entirely for free/comp assignments. This field is required when payment is present. Must be at least 0.01.'
example: 99.0
currency:
type: string
description: 'ISO 4217 currency code. Defaults to `MYR` if omitted. Must be 3 characters.'
example: MYR
nullable: true
reference_id:
type: string
description: "The integrator's own transaction identifier, stored on the payment row for later reconciliation. Must not be greater than 255 characters."
example: POS-12345
nullable: true
remarks:
type: string
description: 'Optional note attached to the payment record. Must not be greater than 500 characters.'
example: 'Paid at counter'
nullable: true
required:
- customer_id
- paid_membership_type_id
- billing_cycle
'/api/customer_paid_memberships/{id}':
get:
summary: 'Show Customer Paid Membership'
operationId: showCustomerPaidMembership
description: 'Retrieves a single customer paid membership by ID. Cross-organisation access returns 404.'
parameters:
-
in: query
name: include
description: 'Comma-separated relationships to include. Available: `customer`, `paidMembershipType`, `transactions`, `payments`, `activePayment`. Use `payments` to retrieve all payment records for reconciliation, or `activePayment` to retrieve only the currently linked payment.'
example: payments
required: false
schema:
type: string
description: 'Comma-separated relationships to include. Available: `customer`, `paidMembershipType`, `transactions`, `payments`, `activePayment`. Use `payments` to retrieve all payment records for reconciliation, or `activePayment` to retrieve only the currently linked payment.'
example: payments
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
membership_status: active
membership_type_name: 'VIP Gold'
billing_cycle: monthly
starts_at: '2025-04-01'
expires_at: '2025-05-01'
auto_renew: false
is_active: true
properties:
data:
type: object
properties:
id:
type: integer
example: 1
membership_status:
type: string
example: active
membership_type_name:
type: string
example: 'VIP Gold'
billing_cycle:
type: string
example: monthly
starts_at:
type: string
example: '2025-04-01'
expires_at:
type: string
example: '2025-05-01'
auto_renew:
type: boolean
example: false
is_active:
type: boolean
example: true
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
tags:
- 'Customer Paid Memberships'
put:
summary: 'Update Customer Paid Membership'
operationId: updateCustomerPaidMembership
description: "Either extends the membership (via `extend_days` + `remarks`) or toggles `auto_renew`.\nThe two operations are mutually exclusive — sending both at once returns a 422.\n\nExtending delegates to `PaidMembershipService::extendMembership`, which records a\n`ManualExtend` transaction and sends the customer the configured notification."
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
membership_status: active
membership_type_name: 'VIP Gold'
billing_cycle: monthly
starts_at: '2025-04-01'
expires_at: '2025-05-15'
auto_renew: true
is_active: true
message: 'Paid membership updated successfully'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
membership_status:
type: string
example: active
membership_type_name:
type: string
example: 'VIP Gold'
billing_cycle:
type: string
example: monthly
starts_at:
type: string
example: '2025-04-01'
expires_at:
type: string
example: '2025-05-15'
auto_renew:
type: boolean
example: true
is_active:
type: boolean
example: true
message:
type: string
example: 'Paid membership updated successfully'
422:
description: 'Mutually Exclusive Fields'
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
extend_days:
- 'The extend days field prohibits auto_renew from being present.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
extend_days:
type: array
example:
- 'The extend days field prohibits auto_renew from being present.'
items:
type: string
tags:
- 'Customer Paid Memberships'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
extend_days:
type: integer
description: 'Number of days to extend the membership by. Mutually exclusive with `auto_renew`. Requires `remarks`. This field is required when auto_renew is not present. This field is required when remarks is present. Must be at least 1. Must not be greater than 3650.'
example: 14
nullable: true
remarks:
type: string
description: 'Internal note for the extension. Required when `extend_days` is set. This field is required when extend_days is present. Must not be greater than 500 characters.'
example: 'Goodwill extension after service outage.'
nullable: true
auto_renew:
type: boolean
description: 'Toggle the auto-renew flag for this membership. Mutually exclusive with `extend_days` / `remarks`. This field is required when extend_days is not present.'
example: true
nullable: true
delete:
summary: 'Cancel Customer Paid Membership'
operationId: cancelCustomerPaidMembership
description: "Cancels (deactivates) the customer's paid membership. Sets `is_active` to `false` and\nrecords a `Cancelled` transaction for the audit trail. The row is not hard-deleted —\nhistorical lookups remain intact."
parameters: []
responses:
204:
description: Cancelled
content:
text/plain:
schema:
type: string
example: ''
tags:
- 'Customer Paid Memberships'
parameters:
-
in: path
name: id
description: 'The ID of the customer paid membership.'
example: 1
required: true
schema:
type: integer
-
in: path
name: customer_paid_membership
description: 'The ID of the membership instance.'
example: 1
required: true
schema:
type: integer
/api/customer_rewards:
get:
summary: 'List Customer Rewards'
operationId: listCustomerRewards
description: "Returns a paginated list of customer rewards belonging to the authenticated user's organisation.\nResults can be filtered, sorted and include related data through query parameters."
parameters:
-
in: query
name: include
description: "Comma-separated list of relationships to include:\n- customer\n- reward\n- validatedAtSpace\n- validatedByUser"
example: 'customer,reward,validatedAtSpace,validatedByUser'
required: false
schema:
type: string
description: "Comma-separated list of relationships to include:\n- customer\n- reward\n- validatedAtSpace\n- validatedByUser"
example: 'customer,reward,validatedAtSpace,validatedByUser'
-
in: query
name: 'filter[status]'
description: 'Filter by reward status (Pending, Used, Expired).'
example: Pending
required: false
schema:
type: string
description: 'Filter by reward status (Pending, Used, Expired).'
example: Pending
-
in: query
name: 'filter[code]'
description: 'Filter by exact reward code match.'
example: ABC123
required: false
schema:
type: string
description: 'Filter by exact reward code match.'
example: ABC123
-
in: query
name: 'filter[expired_at]'
description: 'Filter by expiry date with operators (>, <, =).'
example: '>2024-01-01'
required: false
schema:
type: string
description: 'Filter by expiry date with operators (>, <, =).'
example: '>2024-01-01'
-
in: query
name: 'filter[used_at]'
description: 'Filter by redemption date with operators (>, <, =).'
example: '<2024-12-31'
required: false
schema:
type: string
description: 'Filter by redemption date with operators (>, <, =).'
example: '<2024-12-31'
-
in: query
name: 'filter[customer_id]'
description: 'Filter by customer ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by customer ID.'
example: 1
-
in: query
name: 'filter[reward_id]'
description: 'Filter by reward ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by reward ID.'
example: 1
-
in: query
name: 'filter[validated_at_space_id]'
description: 'Filter by validation space ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by validation space ID.'
example: 1
-
in: query
name: 'filter[validated_by_user_id]'
description: 'Filter by validator user ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by validator user ID.'
example: 1
-
in: query
name: sort
description: 'Sort field and direction. Allowed fields: expired_at, used_at, created_at.'
example: '-created_at'
required: false
schema:
type: string
description: 'Sort field and direction. Allowed fields: expired_at, used_at, created_at.'
example: '-created_at'
-
in: query
name: per_page
description: 'Number of records per page.'
example: 15
required: false
schema:
type: integer
description: 'Number of records per page.'
example: 15
responses:
200:
description: ''
content:
text/plain:
schema:
type: string
example: "{\n \"data\":[\n {\n \"id\": 1,\n \"customer_id\": 1,\n \"reward_id\": 1,\n \"status\": \"Pending\",\n \"code\": \"REWARD123\",\n \"expired_at\": \"2024-12-31T23:59:59.000000Z\",\n \"used_at\": null,\n \"created_at\": \"2024-02-11T00:00:00.000000Z\",\n \"updated_at\": \"2024-02-11T00:00:00.000000Z\",\n \"customer\" : {\n \"id\": 1,\n \"name\": \"John Smith\",\n \"email\": \"john.smith@example.com\",\n \"phone_number\": \"+60123456789\",\n \"gender\": 1,\n \"date_of_birth\": \"1990-05-15\",\n \"source\": \"WhatsApp\",\n \"status\": \"Converted\",\n \"current_point\": 2500,\n \"created_at\": \"2024-01-15T08:30:00.000000Z\",\n \"updated_at\": \"2024-02-01T14:22:33.000000Z\",\n \"notes\": \"Prefers to be contacted via WhatsApp\",\n \"is_manually_assign_tier\": 0,\n \"current_credits\": 150,\n \"birthday_month\": 5,\n \"tags\": [\"VIP\", \"Regular Customer\"],\n \"space_id\": 1,\n \"tier_id\": 1,\n \"organisation_id\": 1,\n }\n },\n ],\n \"links\": {\n \"first\": \"@{{$baseUrl}}/customer_rewards?page=1\",\n \"last\": \"@{{$baseUrl}}/customer_rewards?page=5\",\n \"prev\": null,\n \"next\": \"@{{$baseUrl}}/customer_rewards?page=2\"\n },\n \"meta\": {\n \"current_page\": 1,\n \"from\": 1,\n \"last_page\": 5,\n \"links\": [\n {\n \"url\": null,\n \"label\": \"« Previous\",\n \"active\": false\n },\n {\n \"url\": \"@{{$baseUrl}}/customer_rewards?page=1\",\n \"label\": \"1\",\n \"active\": true\n },\n {\n \"url\": \"@{{$baseUrl}}/customer_rewards?page=2\",\n \"label\": \"2\",\n \"active\": false\n },\n {\n \"url\": \"@{{$baseUrl}}/customer_rewards?page=3\",\n \"label\": \"3\",\n \"active\": false\n },\n {\n \"url\": \"@{{$baseUrl}}/customer_rewards?page=2\",\n \"label\": \"Next »\",\n \"active\": false\n }\n ],\n \"path\": \"@{{$baseUrl}}/customer_rewards\",\n \"per_page\": 15,\n \"to\": 15,\n \"total\": 68\n }\n }"
tags:
- 'Customer Rewards'
post:
summary: 'Create Customer Reward'
operationId: createCustomerReward
description: "Assigns a reward to a customer and processes the point redemption. This endpoint performs several validation checks:\n\n1. Verifies that the customer has sufficient points for the reward\n2. Checks if the reward has reached its total availability limit (if configured)\n3. For one-time rewards, ensures the customer hasn't already redeemed it\n4. Validates that both customer and reward belong to the authenticated organisation\n\nUpon successful validation, the system will:\n- Deduct points from the customer's balance\n- Create a customer reward record\n- Generate a unique redemption code\n- If mark_as_redeemed is true, immediately mark the reward as used\n\nNote: The reward's points cost is determined by its configured amount. Ensure the customer\nhas sufficient points before making this request to avoid validation errors."
parameters: []
responses:
201:
description: 'Created successfully'
content:
application/json:
schema:
type: object
example:
data:
id: 1
customer_id: 1
reward_id: 1
status: Pending
code: REWARD123
expired_at: '2024-12-31T23:59:59.000000Z'
used_at: null
created_at: '2024-02-11T00:00:00.000000Z'
updated_at: '2024-02-11T00:00:00.000000Z'
message: 'Customer reward created successfully'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
customer_id:
type: integer
example: 1
reward_id:
type: integer
example: 1
status:
type: string
example: Pending
code:
type: string
example: REWARD123
expired_at:
type: string
example: '2024-12-31T23:59:59.000000Z'
used_at:
type: string
example: null
nullable: true
created_at:
type: string
example: '2024-02-11T00:00:00.000000Z'
updated_at:
type: string
example: '2024-02-11T00:00:00.000000Z'
message:
type: string
example: 'Customer reward created successfully'
422:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Total availability exceeded'
type: object
example:
message: 'The given data was invalid.'
errors:
reward_id:
- 'This reward cannot be redeemed as it has reached its maximum redemption limit.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
reward_id:
type: array
example:
- 'This reward cannot be redeemed as it has reached its maximum redemption limit.'
items:
type: string
-
description: 'One-time reward already redeemed'
type: object
example:
message: 'The given data was invalid.'
errors:
reward_id:
- 'This reward has already been used/redeemed. (Single redemption reward cannot be redeemed twice)'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
reward_id:
type: array
example:
- 'This reward has already been used/redeemed. (Single redemption reward cannot be redeemed twice)'
items:
type: string
-
description: 'Insufficient points'
type: object
example:
message: 'The given data was invalid.'
errors:
reward_id:
- 'Customer does not have enough point to redeem'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
reward_id:
type: array
example:
- 'Customer does not have enough point to redeem'
items:
type: string
-
description: 'Invalid customer or reward ID'
type: object
example:
message: 'The given data was invalid.'
errors:
customer_id:
- 'The selected customer id is invalid.'
reward_id:
- 'The selected reward id is invalid.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
customer_id:
type: array
example:
- 'The selected customer id is invalid.'
items:
type: string
reward_id:
type: array
example:
- 'The selected reward id is invalid.'
items:
type: string
tags:
- 'Customer Rewards'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
customer_id:
type: integer
description: 'The ID of the customer who will receive the reward. Must belong to the authenticated organisation.'
example: 1
reward_id:
type: integer
description: "The ID of the reward to be redeemed. Must exist in one of the organisation's spaces."
example: 1
mark_as_redeemed:
type: boolean
description: 'optional If true, the reward will be marked as used immediately upon creation. Default: false.'
example: false
currency_type:
type: string
description: 'optional The currency type to use for redemption. Must be "point" or "credit". Defaults to "point" if not specified.'
example: point
nullable: true
validated_at_space_id:
type: integer
description: 'optional ID of the space where the reward was validated. Required when mark_as_redeemed is true.'
example: 1
nullable: true
validated_by_user_id:
type: integer
description: 'optional ID of the user who validated the reward. Will default to current user if not specified when mark_as_redeemed is true.'
example: 1
nullable: true
required:
- customer_id
- reward_id
'/api/customer_rewards/{id}':
get:
summary: 'Get Customer Reward Details'
operationId: getCustomerRewardDetails
description: 'Retrieves detailed information about a specific customer reward.'
parameters:
-
in: query
name: include
description: 'Comma-separated list of relationships to include (customer, reward, validatedAtSpace, validatedByUser).'
example: 'customer,reward,validatedAtSpace,validatedByUser'
required: false
schema:
type: string
description: 'Comma-separated list of relationships to include (customer, reward, validatedAtSpace, validatedByUser).'
example: 'customer,reward,validatedAtSpace,validatedByUser'
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
customer_id: 1
reward_id: 1
status: Pending
code: REWARD123
expired_at: '2024-12-31T23:59:59.000000Z'
used_at: null
created_at: '2024-02-11T00:00:00.000000Z'
updated_at: '2024-02-11T00:00:00.000000Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
customer_id:
type: integer
example: 1
reward_id:
type: integer
example: 1
status:
type: string
example: Pending
code:
type: string
example: REWARD123
expired_at:
type: string
example: '2024-12-31T23:59:59.000000Z'
used_at:
type: string
example: null
nullable: true
created_at:
type: string
example: '2024-02-11T00:00:00.000000Z'
updated_at:
type: string
example: '2024-02-11T00:00:00.000000Z'
tags:
- 'Customer Rewards'
put:
summary: 'Update Customer Reward'
operationId: updateCustomerReward
description: "Updates a customer reward's details.
\nCan update status, expiry date, and usage date.
\nCannot modify rewards that are Expired or Used for more than 1 hour.
\nUsed rewards can be updated within 1 hour of being marked as used."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Success - Status Update'
type: object
example:
data:
id: 1
status: Used
expired_at: '2024-12-31T23:59:59.000000Z'
used_at: '2024-02-11T00:00:00.000000Z'
updated_at: '2024-02-11T00:00:00.000000Z'
message: 'Customer reward updated successfully'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
status:
type: string
example: Used
expired_at:
type: string
example: '2024-12-31T23:59:59.000000Z'
used_at:
type: string
example: '2024-02-11T00:00:00.000000Z'
updated_at:
type: string
example: '2024-02-11T00:00:00.000000Z'
message:
type: string
example: 'Customer reward updated successfully'
-
description: 'Success - Update Expiry'
type: object
example:
data:
id: 1
status: Pending
expired_at: '2024-12-31T23:59:59.000000Z'
used_at: null
updated_at: '2024-02-11T00:00:00.000000Z'
message: 'Customer reward updated successfully'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
status:
type: string
example: Pending
expired_at:
type: string
example: '2024-12-31T23:59:59.000000Z'
used_at:
type: string
example: null
nullable: true
updated_at:
type: string
example: '2024-02-11T00:00:00.000000Z'
message:
type: string
example: 'Customer reward updated successfully'
422:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Invalid Status Value'
type: object
example:
message: 'The selected status is invalid.'
errors:
status:
- 'The selected status is invalid.'
properties:
message:
type: string
example: 'The selected status is invalid.'
errors:
type: object
properties:
status:
type: array
example:
- 'The selected status is invalid.'
items:
type: string
-
description: 'Invalid Status Change'
type: object
example:
message: 'Cannot update expired rewards or rewards used more than 1 hour ago'
errors:
status:
- 'Cannot update expired rewards or rewards used more than 1 hour ago.'
properties:
message:
type: string
example: 'Cannot update expired rewards or rewards used more than 1 hour ago'
errors:
type: object
properties:
status:
type: array
example:
- 'Cannot update expired rewards or rewards used more than 1 hour ago.'
items:
type: string
-
description: 'Invalid Used At Without Status'
type: object
example:
message: 'The given data was invalid.'
errors:
used_at:
- 'used_at can only be set when status is Used.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
used_at:
type: array
example:
- 'used_at can only be set when status is Used.'
items:
type: string
-
description: 'Future Used At Date'
type: object
example:
message: 'The given data was invalid.'
errors:
used_at:
- 'The used at must be a date before or equal to now.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
used_at:
type: array
example:
- 'The used at must be a date before or equal to now.'
items:
type: string
-
description: 'Past Expiry Date'
type: object
example:
message: 'The given data was invalid.'
errors:
expired_at:
- 'The expired at must be a date after now.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
expired_at:
type: array
example:
- 'The expired at must be a date after now.'
items:
type: string
-
description: 'Invalid Date Format'
type: object
example:
message: 'The given data was invalid.'
errors:
expired_at:
- 'The expired at is not a valid date.'
used_at:
- 'The used at is not a valid date.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
expired_at:
type: array
example:
- 'The expired at is not a valid date.'
items:
type: string
used_at:
type: array
example:
- 'The used at is not a valid date.'
items:
type: string
tags:
- 'Customer Rewards'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
status:
type: string
description: 'optional The reward status. Cannot update Expired rewards or Used rewards older than 1 hour. Used rewards can be updated within 1 hour.'
example: Used
expired_at:
type: datetime
description: 'optional New expiry date for the reward. Must be a future date.'
example: '2024-12-31T23:59:59Z'
used_at:
type: datetime
description: 'optional Date when the reward was used. Can only be set when status is Used. Must not be in the future.'
example: '2024-02-10T15:30:00Z'
nullable: true
validated_at_space_id:
type: integer
description: 'optional ID of the space where the reward was validated. Auto-set when marking as Used. Can be explicitly set to null when reverting to Pending.'
example: 1
nullable: true
validated_by_user_id:
type: integer
description: 'optional ID of the user who validated the reward. Auto-set to current user when marking as Used. Can be explicitly set to null when reverting to Pending.'
example: 1
nullable: true
delete:
summary: 'Delete Customer Reward'
operationId: deleteCustomerReward
description: 'Revokes/deletes a pending customer reward. Cannot revoke Used or Expired rewards.'
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
message: 'Customer reward revoked successfully'
properties:
message:
type: string
example: 'Customer reward revoked successfully'
422:
description: 'Cannot Revoke'
content:
application/json:
schema:
type: object
example:
message: 'Cannot revoke used or expired rewards'
properties:
message:
type: string
example: 'Cannot revoke used or expired rewards'
tags:
- 'Customer Rewards'
parameters:
-
in: path
name: id
description: 'The ID of the customer reward.'
example: 1
required: true
schema:
type: integer
/api/customers:
get:
summary: 'List Customers'
operationId: listCustomers
description: "Returns a paginated list of customers belonging to the authenticated user's organisation.
\nResults can be filtered, sorted and include related data through query parameters."
parameters:
-
in: query
name: include
description: "Comma-separated list of relationships to include:\n- tier\n- space\n- tags\n- address\n- activePaidMembership.paidMembershipType (includes active paid membership with type details)"
example: 'tier,activePaidMembership.paidMembershipType'
required: false
schema:
type: string
description: "Comma-separated list of relationships to include:\n- tier\n- space\n- tags\n- address\n- activePaidMembership.paidMembershipType (includes active paid membership with type details)"
example: 'tier,activePaidMembership.paidMembershipType'
-
in: query
name: 'filter[phone_number]'
description: 'Filter by phone number (partial match). Searches for phone numbers containing the provided value.'
example: '88888888 (matches +60188888888)'
required: false
schema:
type: string
description: 'Filter by phone number (partial match). Searches for phone numbers containing the provided value.'
example: '88888888 (matches +60188888888)'
-
in: query
name: 'filter[email]'
description: 'Filter by email match.'
example: customer@example.com
required: false
schema:
type: string
description: 'Filter by email match.'
example: customer@example.com
-
in: query
name: 'filter[gender]'
description: "Filter by gender. Must be one of:\n- 0 (Female)\n- 1 (Male)"
example: 1
required: false
schema:
type: integer
description: "Filter by gender. Must be one of:\n- 0 (Female)\n- 1 (Male)"
example: 1
-
in: query
name: 'filter[source]'
description: "Filter by customer source. Must be one of:\n- Email\n- Phone\n- Direct\n- Reservation\n- WhatsApp\n- StoreHub\n- Loyverse\n- Softinn\n- Loyalty"
example: WhatsApp
required: false
schema:
type: string
description: "Filter by customer source. Must be one of:\n- Email\n- Phone\n- Direct\n- Reservation\n- WhatsApp\n- StoreHub\n- Loyverse\n- Softinn\n- Loyalty"
example: WhatsApp
-
in: query
name: 'filter[status]'
description: "Filter by customer status. Must be one of:\n- Lead\n- Open\n- Replied\n- Opportunity\n- Quotation\n- Lost Quotation\n- Interested\n- Converted\n- Do Not Contact\n- Blocked"
example: Lead
required: false
schema:
type: string
description: "Filter by customer status. Must be one of:\n- Lead\n- Open\n- Replied\n- Opportunity\n- Quotation\n- Lost Quotation\n- Interested\n- Converted\n- Do Not Contact\n- Blocked"
example: Lead
-
in: query
name: 'filter[current_point]'
description: 'Filter by points with operators (>, <, =).'
example: '>100'
required: false
schema:
type: string
description: 'Filter by points with operators (>, <, =).'
example: '>100'
-
in: query
name: 'filter[current_credits]'
description: 'Filter by credits with operators (>, <, =).'
example: '>50'
required: false
schema:
type: string
description: 'Filter by credits with operators (>, <, =).'
example: '>50'
-
in: query
name: 'filter[created_at]'
description: "Filter by creation date with operators (>, <, =). You can:\n- Find customers created on a specific date: 2024-01-01\n- Find customers created after a date: >2024-01-01\n- Find customers created before a date: <2024-01-01\n- Find customers created between dates: >2024-01-01,<2024-01-31\nUse ISO 8601 format (YYYY-MM-DD)."
example: '2024-01-01'
required: false
schema:
type: string
description: "Filter by creation date with operators (>, <, =). You can:\n- Find customers created on a specific date: 2024-01-01\n- Find customers created after a date: >2024-01-01\n- Find customers created before a date: <2024-01-01\n- Find customers created between dates: >2024-01-01,<2024-01-31\nUse ISO 8601 format (YYYY-MM-DD)."
example: '2024-01-01'
-
in: query
name: 'filter[updated_at]'
description: 'Filter by last update date with operators (>, <, =). Use ISO 8601 format (YYYY-MM-DD).'
example: '<2024-12-31'
required: false
schema:
type: string
description: 'Filter by last update date with operators (>, <, =). Use ISO 8601 format (YYYY-MM-DD).'
example: '<2024-12-31'
-
in: query
name: 'filter[tier]'
description: 'Filter by tier ID.'
example: '1'
required: false
schema:
type: string
description: 'Filter by tier ID.'
example: '1'
-
in: query
name: 'filter[space]'
description: 'Filter by space ID.'
example: '1'
required: false
schema:
type: string
description: 'Filter by space ID.'
example: '1'
-
in: query
name: 'filter[has_tags]'
description: 'Filter by tag name.'
example: VIP
required: false
schema:
type: string
description: 'Filter by tag name.'
example: VIP
-
in: query
name: 'filter[name]'
description: 'Filter by customer name (partial match).'
example: John
required: false
schema:
type: string
description: 'Filter by customer name (partial match).'
example: John
-
in: query
name: 'filter[birthday_month]'
description: 'Filter by birth month (1-12).'
example: 10
required: false
schema:
type: integer
description: 'Filter by birth month (1-12).'
example: 10
-
in: query
name: sort
description: 'Sort field and direction. Allowed fields: name, created_at, current_point, current_credits.'
example: '-created_at'
required: false
schema:
type: string
description: 'Sort field and direction. Allowed fields: name, created_at, current_point, current_credits.'
example: '-created_at'
-
in: query
name: per_page
description: 'Number of records per page.'
example: 15
required: false
schema:
type: integer
description: 'Number of records per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
name: 'John Smith'
email: john.smith@example.com
phone_number: '+60123456789'
gender: 1
date_of_birth: '1990-05-15'
source: WhatsApp
status: Converted
current_point: 2500
created_at: '2024-01-15T08:30:00.000000Z'
updated_at: '2024-02-01T14:22:33.000000Z'
notes: 'Prefers to be contacted via WhatsApp'
is_manually_assign_tier: 0
current_credits: 150
birthday_month: 5
tags:
- VIP
- 'Regular Customer'
space_id: 1
tier_id: 1
organisation_id: 1
tier:
id: 1
name: 'Gold Member'
sort: 2
min_point: 2000
color: '#FFD700'
is_manual_assign: 1
point_multiplier: 1.5
perks:
birthday_multiplier: 2
tier_configuration_id: 1
space:
id: 1
uuid: f2d75bf6-3cb8-305c-8f70-f71fe697cba3
name: 'Main Branch'
slug: main-branch
matterport_model_id: null
description: 'Our flagship store in the city centre'
visibility: public
email: main@example.com
phone_number: '+60312345678'
website: '@{{$baseUrl}}'
social_media:
facebook: example
instagram: example
organisation_id: 1
category_id: 1
visits: 1250
created_at: '2023-01-01T00:00:00.000000Z'
updated_at: '2024-01-15T08:30:00.000000Z'
deleted_at: null
links:
public_url: '@{{$baseUrl}}/spaces/main-branch'
address:
address_line_1: '123 Customer Street'
address_line_2: 'Unit 4B'
city: 'Kuala Lumpur'
state: 'Federal Territory of Kuala Lumpur'
country: Malaysia
postal_code: '50000'
full_address: '123 Customer Street, Unit 4B, Kuala Lumpur, Federal Territory of Kuala Lumpur, 50000, Malaysia'
paidMembership:
id: 123
membership_status: active
membership_type_name: 'VIP Gold'
billing_cycle: monthly
starts_at: '2024-01-01'
expires_at: '2024-02-01'
is_active: true
links:
first: '@{{$baseUrl}}/customers?page=1'
last: '@{{$baseUrl}}/customers?page=5'
prev: null
next: '@{{$baseUrl}}/customers?page=2'
meta:
current_page: 1
from: 1
last_page: 5
links:
-
url: null
label: '« Previous'
active: false
-
url: '@{{$baseUrl}}/customers?page=1'
label: '1'
active: true
-
url: '@{{$baseUrl}}/customers?page=2'
label: '2'
active: false
-
url: '@{{$baseUrl}}/customers?page=3'
label: '3'
active: false
-
url: '@{{$baseUrl}}/customers?page=2'
label: 'Next »'
active: false
path: '@{{$baseUrl}}/customers'
per_page: 15
to: 15
total: 68
properties:
data:
type: array
example:
-
id: 1
name: 'John Smith'
email: john.smith@example.com
phone_number: '+60123456789'
gender: 1
date_of_birth: '1990-05-15'
source: WhatsApp
status: Converted
current_point: 2500
created_at: '2024-01-15T08:30:00.000000Z'
updated_at: '2024-02-01T14:22:33.000000Z'
notes: 'Prefers to be contacted via WhatsApp'
is_manually_assign_tier: 0
current_credits: 150
birthday_month: 5
tags:
- VIP
- 'Regular Customer'
space_id: 1
tier_id: 1
organisation_id: 1
tier:
id: 1
name: 'Gold Member'
sort: 2
min_point: 2000
color: '#FFD700'
is_manual_assign: 1
point_multiplier: 1.5
perks:
birthday_multiplier: 2
tier_configuration_id: 1
space:
id: 1
uuid: f2d75bf6-3cb8-305c-8f70-f71fe697cba3
name: 'Main Branch'
slug: main-branch
matterport_model_id: null
description: 'Our flagship store in the city centre'
visibility: public
email: main@example.com
phone_number: '+60312345678'
website: '@{{$baseUrl}}'
social_media:
facebook: example
instagram: example
organisation_id: 1
category_id: 1
visits: 1250
created_at: '2023-01-01T00:00:00.000000Z'
updated_at: '2024-01-15T08:30:00.000000Z'
deleted_at: null
links:
public_url: '@{{$baseUrl}}/spaces/main-branch'
address:
address_line_1: '123 Customer Street'
address_line_2: 'Unit 4B'
city: 'Kuala Lumpur'
state: 'Federal Territory of Kuala Lumpur'
country: Malaysia
postal_code: '50000'
full_address: '123 Customer Street, Unit 4B, Kuala Lumpur, Federal Territory of Kuala Lumpur, 50000, Malaysia'
paidMembership:
id: 123
membership_status: active
membership_type_name: 'VIP Gold'
billing_cycle: monthly
starts_at: '2024-01-01'
expires_at: '2024-02-01'
is_active: true
items:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'John Smith'
email:
type: string
example: john.smith@example.com
phone_number:
type: string
example: '+60123456789'
gender:
type: integer
example: 1
date_of_birth:
type: string
example: '1990-05-15'
source:
type: string
example: WhatsApp
status:
type: string
example: Converted
current_point:
type: integer
example: 2500
created_at:
type: string
example: '2024-01-15T08:30:00.000000Z'
updated_at:
type: string
example: '2024-02-01T14:22:33.000000Z'
notes:
type: string
example: 'Prefers to be contacted via WhatsApp'
is_manually_assign_tier:
type: integer
example: 0
current_credits:
type: integer
example: 150
birthday_month:
type: integer
example: 5
tags:
type: array
example:
- VIP
- 'Regular Customer'
items:
type: string
space_id:
type: integer
example: 1
tier_id:
type: integer
example: 1
organisation_id:
type: integer
example: 1
tier:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Gold Member'
sort:
type: integer
example: 2
min_point:
type: integer
example: 2000
color:
type: string
example: '#FFD700'
is_manual_assign:
type: integer
example: 1
point_multiplier:
type: number
example: 1.5
perks:
type: object
properties:
birthday_multiplier:
type: integer
example: 2
tier_configuration_id:
type: integer
example: 1
space:
type: object
properties:
id:
type: integer
example: 1
uuid:
type: string
example: f2d75bf6-3cb8-305c-8f70-f71fe697cba3
name:
type: string
example: 'Main Branch'
slug:
type: string
example: main-branch
matterport_model_id:
type: string
example: null
nullable: true
description:
type: string
example: 'Our flagship store in the city centre'
visibility:
type: string
example: public
email:
type: string
example: main@example.com
phone_number:
type: string
example: '+60312345678'
website:
type: string
example: '@{{$baseUrl}}'
social_media:
type: object
properties:
facebook:
type: string
example: example
instagram:
type: string
example: example
organisation_id:
type: integer
example: 1
category_id:
type: integer
example: 1
visits:
type: integer
example: 1250
created_at:
type: string
example: '2023-01-01T00:00:00.000000Z'
updated_at:
type: string
example: '2024-01-15T08:30:00.000000Z'
deleted_at:
type: string
example: null
nullable: true
links:
type: object
properties:
public_url:
type: string
example: '@{{$baseUrl}}/spaces/main-branch'
address:
type: object
properties:
address_line_1:
type: string
example: '123 Customer Street'
address_line_2:
type: string
example: 'Unit 4B'
city:
type: string
example: 'Kuala Lumpur'
state:
type: string
example: 'Federal Territory of Kuala Lumpur'
country:
type: string
example: Malaysia
postal_code:
type: string
example: '50000'
full_address:
type: string
example: '123 Customer Street, Unit 4B, Kuala Lumpur, Federal Territory of Kuala Lumpur, 50000, Malaysia'
paidMembership:
type: object
properties:
id:
type: integer
example: 123
membership_status:
type: string
example: active
membership_type_name:
type: string
example: 'VIP Gold'
billing_cycle:
type: string
example: monthly
starts_at:
type: string
example: '2024-01-01'
expires_at:
type: string
example: '2024-02-01'
is_active:
type: boolean
example: true
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/customers?page=1'
last:
type: string
example: '@{{$baseUrl}}/customers?page=5'
prev:
type: string
example: null
nullable: true
next:
type: string
example: '@{{$baseUrl}}/customers?page=2'
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 5
links:
type: array
example:
-
url: null
label: '« Previous'
active: false
-
url: '@{{$baseUrl}}/customers?page=1'
label: '1'
active: true
-
url: '@{{$baseUrl}}/customers?page=2'
label: '2'
active: false
-
url: '@{{$baseUrl}}/customers?page=3'
label: '3'
active: false
-
url: '@{{$baseUrl}}/customers?page=2'
label: 'Next »'
active: false
items:
type: object
properties:
url:
type: string
example: null
nullable: true
label:
type: string
example: '« Previous'
active:
type: boolean
example: false
path:
type: string
example: '@{{$baseUrl}}/customers'
per_page:
type: integer
example: 15
to:
type: integer
example: 15
total:
type: integer
example: 68
tags:
- Customers
post:
summary: 'Create Customer'
operationId: createCustomer
description: "Creates a new customer in the system.
\nThe customer will be associated with the authenticated user's organisation.
\nSupports tag assignment."
parameters: []
responses:
201:
description: 'Created successfully'
content:
text/plain:
schema:
type: string
example: "{\n \"data\": {\n \"id\": 1,\n \"name\": \"John Doe\",\n \"email\": \"john@example.com\",\n \"phone_number\": \"+60123456789\",\n \"gender\": 1,\n \"date_of_birth\": \"1990-01-01\",\n \"source\": \"WhatsApp\",\n \"status\": \"Lead\",\n \"current_point\": 0,\n \"notes\": \"Prefers evening appointments\",\n \"custom_properties\": null,\n \"is_manually_assign_tier\": 0,\n \"current_credits\": 0,\n \"birthday_month\": 1,\n \"space_id\": 1,\n \"organisation_id\": 1,\n \"created_at\": \"2025-02-01T00:00:00.000000Z\",\n \"updated_at\": \"2025-02-01T00:00:00.000000Z\",\n \"address\": {\n \"address_line_1\": \"No 39, Jalan Desa 1/1\",\n \"address_line_2\": \"Taman Desa\",\n \"city\": \"Petaling Jaya\",\n \"state\": \"Selangor\",\n \"country\": \"Malaysia\",\n \"postal_code\": \"58100\",\n \"full_address\": \"No 39, Jalan Desa 1/1, Taman Desa, Petaling Jaya, Selangor, 58100, Malaysia\",\n }\n },\n \"message\": \"Customer created successfully\"\n}"
422:
description: 'Validation failed'
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
email:
- 'The email address is already taken.'
phone_number:
- 'The phone number is already taken.'
source:
- 'The selected source is invalid.'
status:
- 'The selected status is invalid.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
email:
type: array
example:
- 'The email address is already taken.'
items:
type: string
phone_number:
type: array
example:
- 'The phone number is already taken.'
items:
type: string
source:
type: array
example:
- 'The selected source is invalid.'
items:
type: string
status:
type: array
example:
- 'The selected status is invalid.'
items:
type: string
tags:
- Customers
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'The full name of the customer. Must not be greater than 255 characters.'
example: 'John Smith'
email:
type: string
description: 'The email address of the customer. Must be a valid RFC compliant email. Must be a valid email address.'
example: john.smith@example.com
nullable: true
phone_number:
type: string
description: 'The phone number of the customer in E.164 format.'
example: '+60123456789'
gender:
type: string
description: 'The gender of the customer.'
example: 1
enum:
- 0
- 1
nullable: true
date_of_birth:
type: string
description: 'The date of birth of the customer. Must be a valid date. Must be a date before 2008-05-21. Must be a date after 1914-05-21.'
example: '1990-01-01'
nullable: true
space_id:
type: string
description: 'The ID of the space this customer belongs to. Must belong to your organisation. The id of an existing record in the spaces table.'
example: 1
nullable: true
source:
type: string
description: 'The source where the customer was acquired from.'
example: WhatsApp
enum:
- Email
- Phone
- Direct
- Reservation
- WhatsApp
- StoreHub
- Loyverse
- Softinn
- GeniusPos
- WooCommerce
- Shopify
- Loyalty
- Zeoniq
- Bukku
- IvendPos
- LemonPos
- PosPal
- QnE
status:
type: string
description: 'The status of the customer.'
example: Lead
enum:
- Lead
- Open
- Replied
- Opportunity
- Quotation
- 'Lost Quotation'
- Interested
- Converted
- 'Do Not Contact'
- Blocked
tier_id:
type: string
description: 'The ID of the tier assigned to this customer. Must be a valid tier from your organisation.'
example: 1
nullable: true
is_manually_assign_tier:
type: boolean
description: 'Whether the tier was manually assigned to the customer.'
example: true
tags:
type: array
description: ''
example:
- aliquid
items:
type: string
notes:
type: string
description: 'Additional notes about the customer. Must not be greater than 65535 characters.'
example: 'Prefers to be contacted via WhatsApp'
nullable: true
address:
type: object
description: ''
example: []
properties:
address_line_1:
type: string
description: "The first line of the customer's address. Must not be greater than 255 characters."
example: '123 Main Street'
nullable: true
address_line_2:
type: string
description: "The second line of the customer's address (optional). Must not be greater than 255 characters."
example: 'Unit 4B'
nullable: true
country:
type: string
description: 'The country of residence. This field is required when address.address_line_1 is present.'
example: Malaysia
enum:
- Malaysia
- Singapore
state:
type: string
description: 'The state of residence. Must exist in our database. This field is required when address.address_line_1 is present. The name of an existing record in the states table.'
example: Selangor
city:
type: string
description: 'The city of residence. Must exist in our database. This field is required when address.address_line_1 is present. The name of an existing record in the cities table.'
example: 'Petaling Jaya'
postal_code:
type: string
description: 'The postal code. Must be exactly 5 digits. This field is required when address.address_line_1 is present. Must be 5 digits.'
example: '46150'
required:
- name
- phone_number
- source
- status
'/api/customers/{id}':
get:
summary: 'Show Customer Details'
operationId: showCustomerDetails
description: 'Retrieves detailed information about a specific customer, including any requested relationships.'
parameters:
-
in: query
name: include
description: "Comma-separated list of relationships to include. Available relationships:\n- tier\n- space\n- address\n- tags\n- activePaidMembership.paidMembershipType (includes active paid membership with type details)"
example: 'tier,activePaidMembership.paidMembershipType'
required: false
schema:
type: string
description: "Comma-separated list of relationships to include. Available relationships:\n- tier\n- space\n- address\n- tags\n- activePaidMembership.paidMembershipType (includes active paid membership with type details)"
example: 'tier,activePaidMembership.paidMembershipType'
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
name: 'John Doe'
email: john@example.com
phone_number: '+60123456789'
gender: 1
date_of_birth: '1990-01-01'
source: WhatsApp
status: Lead
current_point: 0
notes: 'Prefers evening appointments'
custom_properties: null
is_manually_assign_tier: 0
current_credits: 0
birthday_month: 1
space_id: 1
organisation_id: 1
created_at: '2025-02-01T00:00:00.000000Z'
updated_at: '2025-02-01T00:00:00.000000Z'
tier_id: 1
tier:
id: 1
name: Bronze
points_required: 0
paidMembership:
id: 123
membership_status: active
membership_type_name: 'VIP Gold'
billing_cycle: monthly
starts_at: '2024-01-01'
expires_at: '2024-02-01'
is_active: true
properties:
data:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'John Doe'
email:
type: string
example: john@example.com
phone_number:
type: string
example: '+60123456789'
gender:
type: integer
example: 1
date_of_birth:
type: string
example: '1990-01-01'
source:
type: string
example: WhatsApp
status:
type: string
example: Lead
current_point:
type: integer
example: 0
notes:
type: string
example: 'Prefers evening appointments'
custom_properties:
type: string
example: null
nullable: true
is_manually_assign_tier:
type: integer
example: 0
current_credits:
type: integer
example: 0
birthday_month:
type: integer
example: 1
space_id:
type: integer
example: 1
organisation_id:
type: integer
example: 1
created_at:
type: string
example: '2025-02-01T00:00:00.000000Z'
updated_at:
type: string
example: '2025-02-01T00:00:00.000000Z'
tier_id:
type: integer
example: 1
tier:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: Bronze
points_required:
type: integer
example: 0
paidMembership:
type: object
properties:
id:
type: integer
example: 123
membership_status:
type: string
example: active
membership_type_name:
type: string
example: 'VIP Gold'
billing_cycle:
type: string
example: monthly
starts_at:
type: string
example: '2024-01-01'
expires_at:
type: string
example: '2024-02-01'
is_active:
type: boolean
example: true
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
tags:
- Customers
put:
summary: 'Update Customer'
operationId: updateCustomer
description: "Updates an existing customer's information.
All fields are optional.
\nExisting tags will be synced with the provided tags array if included."
parameters: []
responses:
200:
description: Success
content:
text/plain:
schema:
type: string
example: "{\n \"data\": {\n \"id\": 1,\n \"name\": \"John Doe\",\n \"email\": \"john@example.com\",\n \"phone_number\": \"+60123456789\",\n \"gender\": 1,\n \"date_of_birth\": \"1990-01-01\",\n \"source\": \"WhatsApp\",\n \"status\": \"Lead\",\n \"current_point\": 0,\n \"notes\": \"Prefers evening appointments\",\n \"custom_properties\": null,\n \"is_manually_assign_tier\": 0,\n \"current_credits\": 0,\n \"birthday_month\": 1,\n \"space_id\": 1,\n \"organisation_id\": 1,\n \"created_at\": \"2025-02-01T00:00:00.000000Z\",\n \"updated_at\": \"2025-02-01T00:00:00.000000Z\",\n \"address\": {\n \"address_line_1\": \"No 39, Jalan Desa 1/1\",\n \"address_line_2\": \"Taman Desa\",\n \"city\": \"Petaling Jaya\",\n \"state\": \"Selangor\",\n \"country\": \"Malaysia\",\n \"postal_code\": \"58100\",\n \"full_address\": \"No 39, Jalan Desa 1/1, Taman Desa, Petaling Jaya, Selangor, 58100, Malaysia\",\n }\n },\n \"message\": \"Customer updated successfully\"\n}"
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Customer not found'
properties:
message:
type: string
example: 'Customer not found'
422:
description: 'Validation Error'
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
email:
- 'The email address is already taken.'
phone_number:
- 'The phone number is already taken.'
source:
- 'The selected source is invalid.'
status:
- 'The selected status is invalid.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
email:
type: array
example:
- 'The email address is already taken.'
items:
type: string
phone_number:
type: array
example:
- 'The phone number is already taken.'
items:
type: string
source:
type: array
example:
- 'The selected source is invalid.'
items:
type: string
status:
type: array
example:
- 'The selected status is invalid.'
items:
type: string
tags:
- Customers
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: "optional The customer's full name."
example: 'John Doe'
email:
type: string
description: 'optional A valid email address.'
example: john@example.com
nullable: true
phone_number:
type: string
description: 'optional Phone number with country code.'
example: '+60123456789'
gender:
type: integer|null
description: "optional Gender (0: Female, 1: Male). If present, must be one of:\n- 0 (Female)\n- 1 (Male)"
example: '1'
nullable: true
date_of_birth:
type: date
description: "optional The customer's birthdate (Y-m-d format)."
example: '1990-01-01'
nullable: true
space_id:
type: integer
description: 'optional The ID of the space this customer belongs to.'
example: 1
nullable: true
source:
type: string
description: "optional Source of the customer. Must be one of:\n- Email\n- Phone\n- Direct\n- Reservation\n- WhatsApp\n- StoreHub\n- Loyverse\n- Softinn\n- Loyalty"
example: WhatsApp
status:
type: string
description: "optional Customer's status. Must be one of:\n- Lead\n- Open\n- Replied\n- Opportunity\n- Quotation\n- Lost Quotation\n- Interested\n- Converted\n- Do Not Contact\n- Blocked"
example: Lead
tier_id:
type: string
description: 'The ID of the tier assigned to this customer. Must be a valid tier from your organisation.'
example: 1
nullable: true
is_manually_assign_tier:
type: boolean
description: 'Whether the tier was manually assigned to the customer.'
example: true
tags:
type: array
description: 'optional Array of tag IDs to sync with the customer. Will remove any existing tags not in the array.'
example:
- VIP
- 'Frequent Visitor'
items:
type: string
notes:
type: string
description: 'optional Additional notes about the customer.'
example: 'Prefers evening appointments'
nullable: true
address:
type: object
description: ''
example: []
properties:
address_line_1:
type: string
description: 'optional The first line of the address.'
example: 'No 39, Jalan Desa 1/1'
nullable: true
address_line_2:
type: string
description: 'optional The second line of the address.'
example: 'Taman Desa'
nullable: true
country:
type: string
description: 'optional|required_with:address.address_line_1 The country (Malaysia or Singapore).'
example: Malaysia
state:
type: string
description: 'optional|required_with:address.address_line_1 The state name.'
example: Selangor
city:
type: string
description: 'optional|required_with:address.address_line_1 The city name.'
example: 'Petaling Jaya'
postal_code:
type: string
description: 'optional|required_with:address.address_line_1 5-digit postal code.'
example: '58100'
delete:
summary: 'Delete Customer'
operationId: deleteCustomer
description: "Queues a customer for deletion.
\nThe deletion process runs asynchronously.
\nAll related data will be deleted including rewards, transactions, and media (if any)."
parameters: []
responses:
202:
description: Accepted
content:
application/json:
schema:
type: object
example:
message: 'Customer deletion has been queued and will be processed shortly'
properties:
message:
type: string
example: 'Customer deletion has been queued and will be processed shortly'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
tags:
- Customers
parameters:
-
in: path
name: id
description: 'The ID of the customer to delete.'
example: 1
required: true
schema:
type: integer
/api/customers/bulk-import:
post:
summary: 'Bulk Import Customers'
operationId: bulkImportCustomers
description: "Import multiple customers asynchronously.
\nThe operation is queued and processed in chunks of 100 records.
\nLimited to 1,000 customers per request and rate limited to 10 requests per minute."
parameters: []
responses:
202:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Bulk customer import has been queued and will be processed shortly'
properties:
message:
type: string
example: 'Bulk customer import has been queued and will be processed shortly'
422:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
customers:
- 'The customers must not have more than 1000 items.'
'customers.*.email':
- 'The customers.0.email has already been taken.'
'customers.*.phone_number':
- 'The customers.0.phone_number must be a valid phone number.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
customers:
type: array
example:
- 'The customers must not have more than 1000 items.'
items:
type: string
'customers.*.email':
type: array
example:
- 'The customers.0.email has already been taken.'
items:
type: string
'customers.*.phone_number':
type: array
example:
- 'The customers.0.phone_number must be a valid phone number.'
items:
type: string
429:
description: 'Too Many Requests'
content:
application/json:
schema:
type: object
example:
message: 'Too Many Attempts.'
properties:
message:
type: string
example: 'Too Many Attempts.'
tags:
- Customers
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
customers:
type: array
description: 'Array of customers to import (max 1,000). Must not have more than 1000 items.'
example:
-
name: 'John Doe'
email: john@example.com
phone_number: '+60123456789'
gender: 1
date_of_birth: '1990-01-01'
source: WhatsApp
status: Lead
notes: 'VIP customer'
tags:
- VIP
- WhatsApp
items:
type: object
properties:
name:
type: string
description: 'Full name of the customer. Must not be greater than 255 characters.'
example: 'John Doe'
email:
type: string
description: 'Email address (must be unique per organisation). Must be a valid email address.'
example: john@example.com
nullable: true
phone_number:
type: string
description: 'Phone number with country code (must be unique per organisation).'
example: '+60123456789'
gender:
type: string
description: 'Gender (0: Female, 1: Male).'
example: '1'
enum:
- 0
- 1
nullable: true
date_of_birth:
type: string
description: 'Date of birth in Y-m-d format. Must be a valid date.'
example: '1990-01-01'
nullable: true
source:
type: string
description: 'Customer source. Must be one of: Email, Phone, Direct, Reservation, WhatsApp, StoreHub, Loyverse, Softinn, WooCommerce, Shopify, Loyalty, Zeoniq, Bukku, IvendPos, LemonPos, PosPal.'
example: WhatsApp
enum:
- Email
- Phone
- Direct
- Reservation
- WhatsApp
- StoreHub
- Loyverse
- Softinn
- GeniusPos
- WooCommerce
- Shopify
- Loyalty
- Zeoniq
- Bukku
- IvendPos
- LemonPos
- PosPal
- QnE
nullable: true
status:
type: string
description: 'Customer status. Must be one of: Lead, Open, Replied, Opportunity, Quotation, Lost Quotation, Interested, Converted, Do Not Contact, Blocked.'
example: Lead
enum:
- Lead
- Open
- Replied
- Opportunity
- Quotation
- 'Lost Quotation'
- Interested
- Converted
- 'Do Not Contact'
- Blocked
nullable: true
notes:
type: string
description: 'Additional notes about the customer.'
example: 'VIP customer'
nullable: true
tags:
type: array
description: ''
example:
- animi
items:
type: string
required:
- name
- phone_number
- tags
space_id:
type: string
description: 'The ID of the space to assign all customers to. The id of an existing record in the spaces table.'
example: 1
nullable: true
required:
- customers
/api/organisations:
get:
summary: 'List Organisations'
operationId: listOrganisations
description: "Returns a paginated list of organisations. Vendors see only their own organisation.\nSuper admins with \"View Any Organisation\" permission see all organisations."
parameters:
-
in: query
name: 'filter[name]'
description: 'Filter by organisation name (partial match).'
example: Acme
required: false
schema:
type: string
description: 'Filter by organisation name (partial match).'
example: Acme
-
in: query
name: sort
description: 'Sort field. Allowed: id, name, created_at.'
example: '-id'
required: false
schema:
type: string
description: 'Sort field. Allowed: id, name, created_at.'
example: '-id'
-
in: query
name: per_page
description: 'Records per page.'
example: 15
required: false
schema:
type: integer
description: 'Records per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
name: 'Acme Corp'
friendly_name: Acme
email: hello@acme.com
phone_number: '+60123456789'
pos_provider: null
ecommerce_provider: null
features:
loyalty: true
credits: false
tiering: false
ecommerce_integration: false
created_at: '2024-01-01T00:00:00.000000Z'
updated_at: '2024-01-01T00:00:00.000000Z'
links:
first: ...
last: ...
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
per_page: 15
to: 1
total: 1
properties:
data:
type: array
example:
-
id: 1
name: 'Acme Corp'
friendly_name: Acme
email: hello@acme.com
phone_number: '+60123456789'
pos_provider: null
ecommerce_provider: null
features:
loyalty: true
credits: false
tiering: false
ecommerce_integration: false
created_at: '2024-01-01T00:00:00.000000Z'
updated_at: '2024-01-01T00:00:00.000000Z'
items:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Acme Corp'
friendly_name:
type: string
example: Acme
email:
type: string
example: hello@acme.com
phone_number:
type: string
example: '+60123456789'
pos_provider:
type: string
example: null
nullable: true
ecommerce_provider:
type: string
example: null
nullable: true
features:
type: object
properties:
loyalty:
type: boolean
example: true
credits:
type: boolean
example: false
tiering:
type: boolean
example: false
ecommerce_integration:
type: boolean
example: false
created_at:
type: string
example: '2024-01-01T00:00:00.000000Z'
updated_at:
type: string
example: '2024-01-01T00:00:00.000000Z'
links:
type: object
properties:
first:
type: string
example: ...
last:
type: string
example: ...
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
per_page:
type: integer
example: 15
to:
type: integer
example: 1
total:
type: integer
example: 1
tags:
- Organisations
'/api/organisations/{id}':
get:
summary: 'Show Organisation'
operationId: showOrganisation
description: ''
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
name: 'Acme Corp'
features:
loyalty: true
credits: false
tiering: false
ecommerce_integration: false
properties:
data:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Acme Corp'
features:
type: object
properties:
loyalty:
type: boolean
example: true
credits:
type: boolean
example: false
tiering:
type: boolean
example: false
ecommerce_integration:
type: boolean
example: false
403:
description: Forbidden
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
tags:
- Organisations
put:
summary: 'Update Organisation'
operationId: updateOrganisation
description: ''
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
name: 'Updated Name'
message: 'Organisation updated successfully'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Updated Name'
message:
type: string
example: 'Organisation updated successfully'
422:
description: 'Validation Error'
content:
application/json:
schema:
type: object
example:
message: ...
errors: { }
properties:
message:
type: string
example: ...
errors:
type: object
properties: { }
tags:
- Organisations
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'The name of the organisation. Must not be greater than 255 characters.'
example: 'Acme Corp'
friendly_name:
type: string
description: 'A short display name for the organisation. Must not be greater than 255 characters.'
example: Acme
nullable: true
email:
type: string
description: 'Contact email address for the organisation. Must be a valid email address.'
example: hello@acme.com
phone_number:
type: string
description: 'Contact phone number for the organisation. Must not be greater than 50 characters.'
example: '+60123456789'
parameters:
-
in: path
name: id
description: 'The organisation ID.'
example: 1
required: true
schema:
type: integer
'/api/organisations/{organisation_id}/subscriptions':
get:
summary: 'List Organisation Subscriptions'
operationId: listOrganisationSubscriptions
description: "Returns a paginated list of Stripe subscriptions for the specified organisation.\nOnly the organisation's own members can access this endpoint."
parameters:
-
in: query
name: per_page
description: 'Records per page.'
example: 15
required: false
schema:
type: integer
description: 'Records per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: Success
type: object
example:
data:
-
id: 1
type: default
stripe_id: sub_1ABC
stripe_status: active
stripe_price: price_1ABC
quantity: 1
trial_ends_at: null
ends_at: null
created_at: '2024-01-01T00:00:00.000000Z'
updated_at: '2024-01-01T00:00:00.000000Z'
links:
first: ...
last: ...
prev: null
next: null
meta:
current_page: 1
total: 1
properties:
data:
type: array
example:
-
id: 1
type: default
stripe_id: sub_1ABC
stripe_status: active
stripe_price: price_1ABC
quantity: 1
trial_ends_at: null
ends_at: null
created_at: '2024-01-01T00:00:00.000000Z'
updated_at: '2024-01-01T00:00:00.000000Z'
items:
type: object
properties:
id:
type: integer
example: 1
type:
type: string
example: default
stripe_id:
type: string
example: sub_1ABC
stripe_status:
type: string
example: active
stripe_price:
type: string
example: price_1ABC
quantity:
type: integer
example: 1
trial_ends_at:
type: string
example: null
nullable: true
ends_at:
type: string
example: null
nullable: true
created_at:
type: string
example: '2024-01-01T00:00:00.000000Z'
updated_at:
type: string
example: '2024-01-01T00:00:00.000000Z'
links:
type: object
properties:
first:
type: string
example: ...
last:
type: string
example: ...
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
total:
type: integer
example: 1
-
description: ''
type: object
example:
data:
-
id: 1
type: default
stripe_id: sub_1ABC
stripe_status: active
stripe_price: price_1ABC
quantity: 1
trial_ends_at: null
ends_at: null
created_at: '2024-01-01T00:00:00.000000Z'
updated_at: '2024-01-01T00:00:00.000000Z'
links:
first: ...
last: ...
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
per_page: 15
to: 1
total: 1
properties:
data:
type: array
example:
-
id: 1
type: default
stripe_id: sub_1ABC
stripe_status: active
stripe_price: price_1ABC
quantity: 1
trial_ends_at: null
ends_at: null
created_at: '2024-01-01T00:00:00.000000Z'
updated_at: '2024-01-01T00:00:00.000000Z'
items:
type: object
properties:
id:
type: integer
example: 1
type:
type: string
example: default
stripe_id:
type: string
example: sub_1ABC
stripe_status:
type: string
example: active
stripe_price:
type: string
example: price_1ABC
quantity:
type: integer
example: 1
trial_ends_at:
type: string
example: null
nullable: true
ends_at:
type: string
example: null
nullable: true
created_at:
type: string
example: '2024-01-01T00:00:00.000000Z'
updated_at:
type: string
example: '2024-01-01T00:00:00.000000Z'
links:
type: object
properties:
first:
type: string
example: ...
last:
type: string
example: ...
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
per_page:
type: integer
example: 15
to:
type: integer
example: 1
total:
type: integer
example: 1
403:
description: Forbidden
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
tags:
- Organisations
parameters:
-
in: path
name: organisation_id
description: 'The organisation ID.'
example: 1
required: true
schema:
type: integer
/api/posCredit:
get:
summary: 'List POS credits'
operationId: listPOSCredits
description: ''
parameters:
-
in: query
name: 'filter[status]'
description: 'Filter by status (`pending`, `adopted`).'
example: adopted
required: false
schema:
type: string
description: 'Filter by status (`pending`, `adopted`).'
example: adopted
-
in: query
name: 'filter[space_id]'
description: 'Filter by space ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by space ID.'
example: 1
-
in: query
name: 'filter[ref_id]'
description: 'Filter by ref_id.'
example: REF-123
required: false
schema:
type: string
description: 'Filter by ref_id.'
example: REF-123
-
in: query
name: 'filter[transaction_timestamp]'
description: 'Filter by date (YYYY-MM-DD).'
example: '2025-05-06'
required: false
schema:
type: string
description: 'Filter by date (YYYY-MM-DD).'
example: '2025-05-06'
-
in: query
name: 'filter[amount]'
description: 'Filter by amount with operators.'
example: '>50'
required: false
schema:
type: string
description: 'Filter by amount with operators.'
example: '>50'
-
in: query
name: sort
description: 'Sort field. Allowed: transaction_timestamp, amount, created_at.'
example: '-created_at'
required: false
schema:
type: string
description: 'Sort field. Allowed: transaction_timestamp, amount, created_at.'
example: '-created_at'
-
in: query
name: include
description: 'Relationships to include (space).'
example: space
required: false
schema:
type: string
description: 'Relationships to include (space).'
example: space
-
in: query
name: per_page
description: 'Records per page.'
example: 15
required: false
schema:
type: integer
description: 'Records per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
ref_id: CREDIT-12345
transaction_timestamp: '2025-05-06T14:30:00.000000Z'
amount: '50.00'
status: adopted
pos_provider: eats365
organisation_id: 1
space_id: 1
credit_id: 101
void_credit_id: null
payload:
terminal: T001
created_at: '2025-05-06T14:30:01.000000Z'
updated_at: '2025-05-06T14:30:01.000000Z'
links:
first: ...
last: ...
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
per_page: 15
to: 1
total: 1
properties:
data:
type: array
example:
-
id: 1
ref_id: CREDIT-12345
transaction_timestamp: '2025-05-06T14:30:00.000000Z'
amount: '50.00'
status: adopted
pos_provider: eats365
organisation_id: 1
space_id: 1
credit_id: 101
void_credit_id: null
payload:
terminal: T001
created_at: '2025-05-06T14:30:01.000000Z'
updated_at: '2025-05-06T14:30:01.000000Z'
items:
type: object
properties:
id:
type: integer
example: 1
ref_id:
type: string
example: CREDIT-12345
transaction_timestamp:
type: string
example: '2025-05-06T14:30:00.000000Z'
amount:
type: string
example: '50.00'
status:
type: string
example: adopted
pos_provider:
type: string
example: eats365
organisation_id:
type: integer
example: 1
space_id:
type: integer
example: 1
credit_id:
type: integer
example: 101
void_credit_id:
type: string
example: null
nullable: true
payload:
type: object
properties:
terminal:
type: string
example: T001
created_at:
type: string
example: '2025-05-06T14:30:01.000000Z'
updated_at:
type: string
example: '2025-05-06T14:30:01.000000Z'
links:
type: object
properties:
first:
type: string
example: ...
last:
type: string
example: ...
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
per_page:
type: integer
example: 15
to:
type: integer
example: 1
total:
type: integer
example: 1
403:
description: 'POS integration not enabled'
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
tags:
- 'POS Credits'
post:
summary: 'Create POS Credit'
operationId: createPOSCredit
description: "Ingests a POS credit record. If a customer is identified (via customer_id or phone_number),\na credit wallet entry is created and the POS credit status is set to **adopted**.\nWithout customer identification the record is stored as **pending** for later adoption.\n\n- Positive amounts create a `Top Up` credit.\n- Negative amounts create a `Refund` credit."
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Adopted (with customer)'
type: object
example:
data:
id: 1
ref_id: CREDIT-12345
transaction_timestamp: '2025-05-06T14:30:00.000000Z'
amount: '50.00'
status: adopted
pos_provider: eats365
organisation_id: 1
space_id: 1
credit_id: 101
void_credit_id: null
payload:
terminal: T001
created_at: '2025-05-06T14:30:01.000000Z'
updated_at: '2025-05-06T14:30:01.000000Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
ref_id:
type: string
example: CREDIT-12345
transaction_timestamp:
type: string
example: '2025-05-06T14:30:00.000000Z'
amount:
type: string
example: '50.00'
status:
type: string
example: adopted
pos_provider:
type: string
example: eats365
organisation_id:
type: integer
example: 1
space_id:
type: integer
example: 1
credit_id:
type: integer
example: 101
void_credit_id:
type: string
example: null
nullable: true
payload:
type: object
properties:
terminal:
type: string
example: T001
created_at:
type: string
example: '2025-05-06T14:30:01.000000Z'
updated_at:
type: string
example: '2025-05-06T14:30:01.000000Z'
-
description: 'Pending (no customer)'
type: object
example:
data:
id: 2
ref_id: CREDIT-99999
status: pending
credit_id: null
void_credit_id: null
properties:
data:
type: object
properties:
id:
type: integer
example: 2
ref_id:
type: string
example: CREDIT-99999
status:
type: string
example: pending
credit_id:
type: string
example: null
nullable: true
void_credit_id:
type: string
example: null
nullable: true
403:
description: 'POS integration not enabled'
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
422:
description: 'Duplicate ref_id'
content:
application/json:
schema:
type: object
example:
message: 'The ref id has already been taken.'
errors:
ref_id:
- 'The ref id has already been taken.'
properties:
message:
type: string
example: 'The ref id has already been taken.'
errors:
type: object
properties:
ref_id:
type: array
example:
- 'The ref id has already been taken.'
items:
type: string
tags:
- 'POS Credits'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
ref_id:
type: string
description: 'A unique reference ID for this POS credit transaction. Must be unique within the organisation.'
example: REF-20250506-001
transaction_timestamp:
type: string
description: 'The date and time when the transaction occurred at the POS terminal. Must be a valid date.'
example: '2025-05-06T14:30:00Z'
amount:
type: integer
description: 'Transaction amount in cents. Positive for top-ups, negative for refunds.'
example: 5000
payload:
type: string
description: 'Raw payload from the POS system. Can be a JSON string or object.'
example:
receipt_no: RCP-001
terminal_id: T01
space_id:
type: integer
description: 'The ID of the space (outlet) where this credit transaction occurred. Must belong to the authenticated organisation. The id of an existing record in the spaces table.'
example: 1
customer_id:
type: integer
description: 'The ID of the customer to link this credit to. Optional — credit will be auto-adopted if provided. The id of an existing record in the customers table.'
example: 42
phone_number:
type: string
description: 'Customer phone number as fallback for identification. Will be normalized to E.164 format.'
example: '+60123456789'
remarks:
type: string
description: 'Optional remarks for this POS credit transaction. Max 500 characters. Must not be greater than 500 characters.'
example: 'Top-up via Eats365 POS'
required:
- ref_id
- transaction_timestamp
- amount
- payload
- space_id
'/api/posCredit/{posCredit_id}/void':
post:
summary: 'Void POS Credit'
operationId: voidPOSCredit
description: "Voids an adopted POS credit record by creating a deduct entry in the customer's credit wallet.\n\n**Idempotency:** Voiding an already-voided POS credit returns success without creating a duplicate entry."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Voided successfully'
type: object
example:
message: 'POS credit voided successfully.'
data:
id: 202
customer_id: 42
amount: 50
operation: '-'
type: Deduct
remarks: 'Voided POS credit #1'
created_at: '2025-05-06T15:00:00.000000Z'
properties:
message:
type: string
example: 'POS credit voided successfully.'
data:
type: object
properties:
id:
type: integer
example: 202
customer_id:
type: integer
example: 42
amount:
type: integer
example: 50
operation:
type: string
example: '-'
type:
type: string
example: Deduct
remarks:
type: string
example: 'Voided POS credit #1'
created_at:
type: string
example: '2025-05-06T15:00:00.000000Z'
-
description: 'Already voided (idempotent)'
type: object
example:
message: 'This transaction has already been voided.'
data: null
properties:
message:
type: string
example: 'This transaction has already been voided.'
data:
type: string
example: null
nullable: true
-
description: 'Not adopted, nothing to void'
type: object
example:
message: 'Transaction not adopted, nothing to void.'
data: null
properties:
message:
type: string
example: 'Transaction not adopted, nothing to void.'
data:
type: string
example: null
nullable: true
403:
description: Unauthorized
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
404:
description: 'POS credit not found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found.'
properties:
message:
type: string
example: 'Record not found.'
422:
description: 'customer_id required'
content:
application/json:
schema:
type: object
example:
message: 'A customer ID is required to void this POS credit.'
properties:
message:
type: string
example: 'A customer ID is required to void this POS credit.'
tags:
- 'POS Credits'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
customer_id:
type: integer
description: 'The ID of the customer. Required only when the POS credit has no linked customer. Must belong to the authenticated organisation. The id of an existing record in the customers table.'
example: 42
remarks:
type: string
description: 'Optional remarks for this void operation. Max 500 characters. Must not be greater than 500 characters.'
example: 'Voided per customer request'
parameters:
-
in: path
name: posCredit_id
description: 'The ID of the posCredit.'
example: 1
required: true
schema:
type: integer
-
in: path
name: posCredit
description: 'The ID of the POS credit to void.'
example: 1
required: true
schema:
type: integer
/api/posTransaction:
get:
summary: 'List POS transactions'
operationId: listPOSTransactions
description: "Returns a paginated list of POS transactions for the authenticated organisation.\nResults can be filtered, sorted and paginated."
parameters:
-
in: query
name: 'filter[status]'
description: 'Filter by transaction status.'
example: adopted
required: false
schema:
type: string
description: 'Filter by transaction status.'
example: adopted
-
in: query
name: 'filter[space_id]'
description: 'Filter by space UUID.'
example: '1'
required: false
schema:
type: string
description: 'Filter by space UUID.'
example: '1'
-
in: query
name: 'filter[transaction_time]'
description: 'Filter by transaction date (Y-m-d format).'
example: '2025-05-06'
required: false
schema:
type: string
description: 'Filter by transaction date (Y-m-d format).'
example: '2025-05-06'
-
in: query
name: 'filter[amount_min]'
description: 'Filter by minimum amount.'
example: 50.0
required: false
schema:
type: number
description: 'Filter by minimum amount.'
example: 50.0
-
in: query
name: 'filter[amount_max]'
description: 'Filter by maximum amount.'
example: 500.0
required: false
schema:
type: number
description: 'Filter by maximum amount.'
example: 500.0
-
in: query
name: 'filter[ref_id]'
description: 'Filter by reference ID.'
example: TRX-123
required: false
schema:
type: string
description: 'Filter by reference ID.'
example: TRX-123
-
in: query
name: sort
description: 'Sort by column (prefixed with - for descending). Available options: transaction_time, amount, created_at.'
example: '-transaction_time'
required: false
schema:
type: string
description: 'Sort by column (prefixed with - for descending). Available options: transaction_time, amount, created_at.'
example: '-transaction_time'
-
in: query
name: include
description: 'Include related resources. Available options: space.'
example: space
required: false
schema:
type: string
description: 'Include related resources. Available options: space.'
example: space
-
in: query
name: page
description: 'Page number.'
example: 1
required: false
schema:
type: integer
description: 'Page number.'
example: 1
-
in: query
name: per_page
description: 'Items per page (max 100).'
example: 15
required: false
schema:
type: integer
description: 'Items per page (max 100).'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
ref_id: TRX-12345678
transaction_time: '2025-05-06T14:30:00Z'
amount: 150.0
status: adopted
pos_provider: provider_name
organisation_id: 1
created_at: '2025-05-06T14:35:00Z'
updated_at: '2025-05-06T14:35:00Z'
space:
id: '1'
name: 'Main Store'
slug: main-store
links:
first: '@{{$baseUrl}}/pos-transactions?page=1'
last: '@{{$baseUrl}}/pos-transactions?page=5'
prev: null
next: '@{{$baseUrl}}/pos-transactions?page=2'
meta:
current_page: 1
from: 1
last_page: 5
path: '@{{$baseUrl}}/pos-transactions'
per_page: 15
to: 15
total: 75
properties:
data:
type: array
example:
-
id: 1
ref_id: TRX-12345678
transaction_time: '2025-05-06T14:30:00Z'
amount: 150
status: adopted
pos_provider: provider_name
organisation_id: 1
created_at: '2025-05-06T14:35:00Z'
updated_at: '2025-05-06T14:35:00Z'
space:
id: '1'
name: 'Main Store'
slug: main-store
items:
type: object
properties:
id:
type: integer
example: 1
ref_id:
type: string
example: TRX-12345678
transaction_time:
type: string
example: '2025-05-06T14:30:00Z'
amount:
type: number
example: 150.0
status:
type: string
example: adopted
pos_provider:
type: string
example: provider_name
organisation_id:
type: integer
example: 1
created_at:
type: string
example: '2025-05-06T14:35:00Z'
updated_at:
type: string
example: '2025-05-06T14:35:00Z'
space:
type: object
properties:
id:
type: string
example: '1'
name:
type: string
example: 'Main Store'
slug:
type: string
example: main-store
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/pos-transactions?page=1'
last:
type: string
example: '@{{$baseUrl}}/pos-transactions?page=5'
prev:
type: string
example: null
nullable: true
next:
type: string
example: '@{{$baseUrl}}/pos-transactions?page=2'
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 5
path:
type: string
example: '@{{$baseUrl}}/pos-transactions'
per_page:
type: integer
example: 15
to:
type: integer
example: 15
total:
type: integer
example: 75
tags:
- 'POS Transaction'
post:
summary: 'Create a new POS transaction'
operationId: createANewPOSTransaction
description: "This endpoint creates a new POS transaction in the system. If a customer identifier (customer_id or phone_number)\nis provided, the transaction will be automatically adopted and a corresponding transaction record will be created for the customer.\n\n**Partial Refunds:** To process a partial refund, send a negative amount. Optionally provide the original_ref_id\nof the transaction being refunded. The refund will deduct proportional points from the customer's balance.\n\n**Blind Refunds:** If no original_ref_id is provided with a negative amount (blind refund), points will be\ndeducted using the formula: refund_amount × tier_multiplier (or 1:1 if customer has no tier)."
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
oneOf:
-
description: Success
type: object
example:
data:
id: 1
ref_id: TRX-12345678
transaction_time: '2025-05-06T14:30:00Z'
amount: 150.0
status: pending
pos_provider: provider_name
space_id: '1'
organisation_id: 1
created_at: '2025-05-06T14:35:00Z'
updated_at: '2025-05-06T14:35:00Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
ref_id:
type: string
example: TRX-12345678
transaction_time:
type: string
example: '2025-05-06T14:30:00Z'
amount:
type: number
example: 150.0
status:
type: string
example: pending
pos_provider:
type: string
example: provider_name
space_id:
type: string
example: '1'
organisation_id:
type: integer
example: 1
created_at:
type: string
example: '2025-05-06T14:35:00Z'
updated_at:
type: string
example: '2025-05-06T14:35:00Z'
-
description: 'With customer adoption'
type: object
example:
data:
id: 1
ref_id: TRX-12345678
transaction_time: '2025-05-06T14:30:00Z'
amount: 150.0
status: adopted
pos_provider: provider_name
space_id: '1'
organisation_id: 1
created_at: '2025-05-06T14:35:00Z'
updated_at: '2025-05-06T14:35:00Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
ref_id:
type: string
example: TRX-12345678
transaction_time:
type: string
example: '2025-05-06T14:30:00Z'
amount:
type: number
example: 150.0
status:
type: string
example: adopted
pos_provider:
type: string
example: provider_name
space_id:
type: string
example: '1'
organisation_id:
type: integer
example: 1
created_at:
type: string
example: '2025-05-06T14:35:00Z'
updated_at:
type: string
example: '2025-05-06T14:35:00Z'
-
description: 'Partial refund'
type: object
example:
data:
id: 2
ref_id: TRX-REFUND-001
transaction_time: '2025-05-06T15:00:00Z'
amount: -50.0
status: adopted
pos_provider: provider_name
space_id: '1'
organisation_id: 1
is_refund: true
original_ref_id: TRX-12345678
refund_details:
original_amount: 150.0
refund_amount: 50.0
points_deducted: 50
created_at: '2025-05-06T15:00:00Z'
updated_at: '2025-05-06T15:00:00Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 2
ref_id:
type: string
example: TRX-REFUND-001
transaction_time:
type: string
example: '2025-05-06T15:00:00Z'
amount:
type: number
example: -50.0
status:
type: string
example: adopted
pos_provider:
type: string
example: provider_name
space_id:
type: string
example: '1'
organisation_id:
type: integer
example: 1
is_refund:
type: boolean
example: true
original_ref_id:
type: string
example: TRX-12345678
refund_details:
type: object
properties:
original_amount:
type: number
example: 150.0
refund_amount:
type: number
example: 50.0
points_deducted:
type: integer
example: 50
created_at:
type: string
example: '2025-05-06T15:00:00Z'
updated_at:
type: string
example: '2025-05-06T15:00:00Z'
-
description: 'Blind refund (no original_ref_id)'
type: object
example:
data:
id: 3
ref_id: TRX-BLIND-REFUND-001
transaction_time: '2025-05-06T15:00:00Z'
amount: -50.0
status: adopted
pos_provider: provider_name
space_id: '1'
organisation_id: 1
is_refund: true
original_ref_id: null
refund_details:
original_amount: null
refund_amount: 50.0
points_deducted: 75
created_at: '2025-05-06T15:00:00Z'
updated_at: '2025-05-06T15:00:00Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 3
ref_id:
type: string
example: TRX-BLIND-REFUND-001
transaction_time:
type: string
example: '2025-05-06T15:00:00Z'
amount:
type: number
example: -50.0
status:
type: string
example: adopted
pos_provider:
type: string
example: provider_name
space_id:
type: string
example: '1'
organisation_id:
type: integer
example: 1
is_refund:
type: boolean
example: true
original_ref_id:
type: string
example: null
nullable: true
refund_details:
type: object
properties:
original_amount:
type: string
example: null
nullable: true
refund_amount:
type: number
example: 50.0
points_deducted:
type: integer
example: 75
created_at:
type: string
example: '2025-05-06T15:00:00Z'
updated_at:
type: string
example: '2025-05-06T15:00:00Z'
403:
description: 'POS integration not enabled'
content:
application/json:
schema:
type: object
example:
message: 'POS integration not enabled for this organisation'
properties:
message:
type: string
example: 'POS integration not enabled for this organisation'
422:
description: 'Validation error'
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
ref_id:
- 'The ref id has already been taken.'
amount:
- 'The refund amount exceeds the refundable balance.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
ref_id:
type: array
example:
- 'The ref id has already been taken.'
items:
type: string
amount:
type: array
example:
- 'The refund amount exceeds the refundable balance.'
items:
type: string
tags:
- 'POS Transaction'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
space_id:
type: string
description: 'The UUID of the space where the transaction occurred. Must belong to the authenticated organisation.'
example: '1'
nullable: true
ref_id:
type: string
description: 'A unique reference ID for the transaction. Must be unique across all POS transactions.'
example: TRX-12345678
transaction_time:
type: datetime
description: 'The date and time when the transaction occurred. Must be a valid date format.'
example: '2025-05-06T14:30:00Z'
amount:
type: integer
description: 'The transaction amount in cents. Positive for purchases, negative for partial refunds.'
example: 15000
payload:
type: json
description: 'Additional transaction details in JSON format.'
example: '{"items": [{"name": "Product A", "quantity": 2, "price": 7500}], "payment_method": "credit_card", "terminal_id": "T001"}'
customer_id:
type: integer
description: 'The ID of the customer associated with this transaction. Must exist in the customers table.'
example: 42
nullable: true
phone_number:
type: string
description: "The customer's phone number in E.164 format."
example: '+60123456789'
nullable: true
original_ref_id:
type: string
description: 'The ref_id of the original transaction being refunded. Optional for refunds - if provided, points are deducted proportionally; if not provided (blind refund), points = refund_amount × tier_multiplier.'
example: TRX-ORIGINAL-001
nullable: true
remarks:
type: string
description: 'Free-form text describing the purchase items or refund reason. Max 500 characters.'
example: '2x Coffee, 1x Sandwich'
nullable: true
required:
- ref_id
- transaction_time
- amount
- payload
'/api/posTransaction/{posTransaction_id}/void':
post:
summary: 'Void POS Transaction'
operationId: voidPOSTransaction
description: "Voids an adopted POS transaction and deducts the exact points that were earned.\nThis generic endpoint works for all POS providers.\n\nThe void operation uses the linked transaction_id to deduct the exact amount of points\nthat were earned, accounting for tier multipliers. For example:\n- Customer with 2x Gold tier earns 200 points on $100 purchase\n- Voiding deducts exactly 200 points (not 100)\n\n**Idempotency:** Attempting to void the same transaction multiple times will return success\nbut only create one void transaction."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: ''
type: object
example:
message: 'POS transaction voided successfully.'
data:
id: 789
customer_id: 456
amount: 200
operation: '-'
remarks: 'Transaction voided from POS System (Ref: REF-12345)'
created_at: '2025-10-25T14:30:00Z'
properties:
message:
type: string
example: 'POS transaction voided successfully.'
data:
type: object
properties:
id:
type: integer
example: 789
customer_id:
type: integer
example: 456
amount:
type: integer
example: 200
operation:
type: string
example: '-'
remarks:
type: string
example: 'Transaction voided from POS System (Ref: REF-12345)'
created_at:
type: string
example: '2025-10-25T14:30:00Z'
-
description: ''
type: object
example:
message: 'Transaction not adopted, nothing to void.'
data: null
properties:
message:
type: string
example: 'Transaction not adopted, nothing to void.'
data:
type: string
example: null
nullable: true
-
description: ''
type: object
example:
message: 'This transaction has already been voided.'
data: null
properties:
message:
type: string
example: 'This transaction has already been voided.'
data:
type: string
example: null
nullable: true
403:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
404:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'POS Transaction not found.'
properties:
message:
type: string
example: 'POS Transaction not found.'
422:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
customer_id:
- 'The selected customer does not exist in your organisation.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
customer_id:
type: array
example:
- 'The selected customer does not exist in your organisation.'
items:
type: string
tags:
- 'POS Transaction'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
customer_id:
type: integer
description: 'The ID of the customer who earned the points.'
example: 456
remarks:
type: string
description: 'Optional custom remarks for the void transaction.'
example: '"Transaction voided - customer request"'
nullable: true
required:
- customer_id
parameters:
-
in: path
name: posTransaction_id
description: 'The ID of the posTransaction.'
example: 1140884
required: true
schema:
type: integer
-
in: path
name: posTransaction
description: 'The ID of the POS transaction to void.'
example: 123
required: true
schema:
type: integer
/api/paid_membership_types:
get:
summary: 'List Paid Membership Types'
operationId: listPaidMembershipTypes
description: "Returns a paginated list of paid membership type catalog entries belonging to the\nauthenticated user's organisation. Supports filtering and sorting via query parameters."
parameters:
-
in: query
name: 'filter[is_active]'
description: 'Filter by active status.'
example: true
required: false
schema:
type: boolean
description: 'Filter by active status.'
example: true
-
in: query
name: 'filter[name]'
description: 'Filter by membership type name (partial match).'
example: Gold
required: false
schema:
type: string
description: 'Filter by membership type name (partial match).'
example: Gold
-
in: query
name: sort
description: 'Sort field and direction. Prefix with `-` for descending. Available fields: `sort`, `name`, `created_at`. Defaults to `sort`.'
example: '-created_at'
required: false
schema:
type: string
description: 'Sort field and direction. Prefix with `-` for descending. Available fields: `sort`, `name`, `created_at`. Defaults to `sort`.'
example: '-created_at'
-
in: query
name: per_page
description: 'Number of records per page. Defaults to 15.'
example: 15
required: false
schema:
type: integer
description: 'Number of records per page. Defaults to 15.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
organisation_id: 1
name: 'VIP Gold'
description: 'Priority support and birthday rewards.'
pricing_options:
-
cycle: monthly
price: 3500
-
cycle: yearly
price: 35000
benefits:
-
icon: heroicon-o-star
heading: 'Priority Support'
description: 'Get priority customer support'
sort: 1
is_active: true
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
links:
first: ...
last: ...
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
path: ...
per_page: 15
to: 1
total: 1
properties:
data:
type: array
example:
-
id: 1
organisation_id: 1
name: 'VIP Gold'
description: 'Priority support and birthday rewards.'
pricing_options:
-
cycle: monthly
price: 3500
-
cycle: yearly
price: 35000
benefits:
-
icon: heroicon-o-star
heading: 'Priority Support'
description: 'Get priority customer support'
sort: 1
is_active: true
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
items:
type: object
properties:
id:
type: integer
example: 1
organisation_id:
type: integer
example: 1
name:
type: string
example: 'VIP Gold'
description:
type: string
example: 'Priority support and birthday rewards.'
pricing_options:
type: array
example:
-
cycle: monthly
price: 3500
-
cycle: yearly
price: 35000
items:
type: object
properties:
cycle:
type: string
example: monthly
price:
type: integer
example: 3500
benefits:
type: array
example:
-
icon: heroicon-o-star
heading: 'Priority Support'
description: 'Get priority customer support'
items:
type: object
properties:
icon:
type: string
example: heroicon-o-star
heading:
type: string
example: 'Priority Support'
description:
type: string
example: 'Get priority customer support'
sort:
type: integer
example: 1
is_active:
type: boolean
example: true
created_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
updated_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
links:
type: object
properties:
first:
type: string
example: ...
last:
type: string
example: ...
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
path:
type: string
example: ...
per_page:
type: integer
example: 15
to:
type: integer
example: 1
total:
type: integer
example: 1
tags:
- 'Paid Membership Types'
'/api/paid_membership_types/{id}':
get:
summary: 'Show Paid Membership Type'
operationId: showPaidMembershipType
description: 'Retrieves a single paid membership type by ID. Cross-organisation access returns 404.'
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
organisation_id: 1
name: 'VIP Gold'
description: 'Priority support and birthday rewards.'
pricing_options:
-
cycle: monthly
price: 3500
benefits: []
sort: 1
is_active: true
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
organisation_id:
type: integer
example: 1
name:
type: string
example: 'VIP Gold'
description:
type: string
example: 'Priority support and birthday rewards.'
pricing_options:
type: array
example:
-
cycle: monthly
price: 3500
items:
type: object
properties:
cycle:
type: string
example: monthly
price:
type: integer
example: 3500
benefits:
type: array
example: []
sort:
type: integer
example: 1
is_active:
type: boolean
example: true
created_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
updated_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
tags:
- 'Paid Membership Types'
parameters:
-
in: path
name: id
description: 'The ID of the paid membership type.'
example: 1
required: true
schema:
type: integer
-
in: path
name: paid_membership_type
description: 'The ID of the paid membership type.'
example: 1
required: true
schema:
type: integer
/api/payments:
get:
summary: 'List Payments'
operationId: listPayments
description: "Get a paginated list of payments and refunds belonging to the authenticated\nuser's organisation. Results can be filtered, sorted and expanded with related\ndata through query parameters."
parameters:
-
in: query
name: 'filter[type]'
description: 'Filter by record type. Available values: `payment`, `refund`.'
example: payment
required: false
schema:
type: string
description: 'Filter by record type. Available values: `payment`, `refund`.'
example: payment
-
in: query
name: 'filter[status]'
description: 'Filter by payment status code (e.g. 0 = pending, 1 = success, 2 = failed).'
example: 1
required: false
schema:
type: integer
description: 'Filter by payment status code (e.g. 0 = pending, 1 = success, 2 = failed).'
example: 1
-
in: query
name: 'filter[method]'
description: 'Filter by payment method (e.g. `commerce_pay`, `manual`, `credit`).'
example: commerce_pay
required: false
schema:
type: string
description: 'Filter by payment method (e.g. `commerce_pay`, `manual`, `credit`).'
example: commerce_pay
-
in: query
name: 'filter[payable_type]'
description: 'Filter by the fully-qualified class of what was paid for.'
example: App\Models\Reservation
required: false
schema:
type: string
description: 'Filter by the fully-qualified class of what was paid for.'
example: App\Models\Reservation
-
in: query
name: 'filter[created_at]'
description: 'Filter by creation date with operators.'
example: '>2024-01-01'
required: false
schema:
type: string
description: 'Filter by creation date with operators.'
example: '>2024-01-01'
-
in: query
name: 'filter[completed_at]'
description: 'Filter by completion date with operators.'
example: '>2024-01-01'
required: false
schema:
type: string
description: 'Filter by completion date with operators.'
example: '>2024-01-01'
-
in: query
name: sort
description: 'Sort field. Allowed: created_at, completed_at, amount. Use - for descending.'
example: '-created_at'
required: false
schema:
type: string
description: 'Sort field. Allowed: created_at, completed_at, amount. Use - for descending.'
example: '-created_at'
-
in: query
name: include
description: 'Comma-separated relationships to include. Allowed: refunds, refundingPayment.'
example: refunds
required: false
schema:
type: string
description: 'Comma-separated relationships to include. Allowed: refunds, refundingPayment.'
example: refunds
-
in: query
name: per_page
description: 'Number of records per page.'
example: 15
required: false
schema:
type: integer
description: 'Number of records per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'List of payments'
type: object
example:
data:
-
id: 1
type: payment
method: commerce_pay
reference_id: TXN-1234567890
amount: 150.0
currency_code: MYR
status: 1
is_manual: false
payment_type: null
source: null
attempted_at: '2025-01-01T00:00:00+00:00'
completed_at: '2025-01-01T00:01:00+00:00'
failed_at: null
failure_reason: null
created_at: '2025-01-01T00:00:00+00:00'
links:
first: ...
last: ...
prev: null
next: null
meta:
current_page: 1
per_page: 15
total: 1
properties:
data:
type: array
example:
-
id: 1
type: payment
method: commerce_pay
reference_id: TXN-1234567890
amount: 150
currency_code: MYR
status: 1
is_manual: false
payment_type: null
source: null
attempted_at: '2025-01-01T00:00:00+00:00'
completed_at: '2025-01-01T00:01:00+00:00'
failed_at: null
failure_reason: null
created_at: '2025-01-01T00:00:00+00:00'
items:
type: object
properties:
id:
type: integer
example: 1
type:
type: string
example: payment
method:
type: string
example: commerce_pay
reference_id:
type: string
example: TXN-1234567890
amount:
type: number
example: 150.0
currency_code:
type: string
example: MYR
status:
type: integer
example: 1
is_manual:
type: boolean
example: false
payment_type:
type: string
example: null
nullable: true
source:
type: string
example: null
nullable: true
attempted_at:
type: string
example: '2025-01-01T00:00:00+00:00'
completed_at:
type: string
example: '2025-01-01T00:01:00+00:00'
failed_at:
type: string
example: null
nullable: true
failure_reason:
type: string
example: null
nullable: true
created_at:
type: string
example: '2025-01-01T00:00:00+00:00'
links:
type: object
properties:
first:
type: string
example: ...
last:
type: string
example: ...
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
per_page:
type: integer
example: 15
total:
type: integer
example: 1
-
description: 'No payments found'
type: object
example:
data: []
links:
first: ...
last: ...
prev: null
next: null
meta:
current_page: 1
per_page: 15
total: 0
properties:
data:
type: array
example: []
links:
type: object
properties:
first:
type: string
example: ...
last:
type: string
example: ...
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
per_page:
type: integer
example: 15
total:
type: integer
example: 0
401:
description: Unauthenticated
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
403:
description: Unauthorized
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
tags:
- Payments
'/api/payments/{id}':
get:
summary: 'Show Payment'
operationId: showPayment
description: 'Retrieve detailed information about a specific payment or refund.'
parameters:
-
in: query
name: include
description: 'Comma-separated relationships to include. Allowed: refunds, refundingPayment.'
example: refunds
required: false
schema:
type: string
description: 'Comma-separated relationships to include. Allowed: refunds, refundingPayment.'
example: refunds
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
type: payment
method: commerce_pay
reference_id: TXN-1234567890
amount: 150.0
currency_code: MYR
status: 1
is_manual: false
payment_type: null
source: null
attempted_at: '2025-01-01T00:00:00+00:00'
completed_at: '2025-01-01T00:01:00+00:00'
failed_at: null
failure_reason: null
created_at: '2025-01-01T00:00:00+00:00'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
type:
type: string
example: payment
method:
type: string
example: commerce_pay
reference_id:
type: string
example: TXN-1234567890
amount:
type: number
example: 150.0
currency_code:
type: string
example: MYR
status:
type: integer
example: 1
is_manual:
type: boolean
example: false
payment_type:
type: string
example: null
nullable: true
source:
type: string
example: null
nullable: true
attempted_at:
type: string
example: '2025-01-01T00:00:00+00:00'
completed_at:
type: string
example: '2025-01-01T00:01:00+00:00'
failed_at:
type: string
example: null
nullable: true
failure_reason:
type: string
example: null
nullable: true
created_at:
type: string
example: '2025-01-01T00:00:00+00:00'
401:
description: Unauthenticated
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
403:
description: Unauthorized
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found.'
properties:
message:
type: string
example: 'Record not found.'
tags:
- Payments
parameters:
-
in: path
name: id
description: 'The ID of the payment.'
example: 1
required: true
schema:
type: integer
-
in: path
name: payment
description: 'The ID of the payment.'
example: 1
required: true
schema:
type: integer
/api/plans:
get:
summary: 'List Plans'
operationId: listPlans
description: 'Returns a paginated list of active subscription plans, including their prices.'
parameters:
-
in: query
name: 'filter[name]'
description: 'Filter by plan name (partial match).'
example: Premium
required: false
schema:
type: string
description: 'Filter by plan name (partial match).'
example: Premium
-
in: query
name: sort
description: 'Sort field. Allowed: id, name, created_at.'
example: '-id'
required: false
schema:
type: string
description: 'Sort field. Allowed: id, name, created_at.'
example: '-id'
-
in: query
name: per_page
description: 'Records per page.'
example: 15
required: false
schema:
type: integer
description: 'Records per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
name: Premium
description: 'Full access to all features'
is_active: true
features:
loyalty_enabled: true
credits_enabled: true
tiering_enabled: true
max_spaces: 3
max_customers: 10000
prices:
-
id: 1
currency: MYR
billing_period: monthly
amount: 350
stripe_price_id: price_1ABC
is_active: true
description: null
created_at: '2024-01-01T00:00:00.000000Z'
updated_at: '2024-01-01T00:00:00.000000Z'
links:
first: ...
last: ...
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
per_page: 15
to: 1
total: 1
properties:
data:
type: array
example:
-
id: 1
name: Premium
description: 'Full access to all features'
is_active: true
features:
loyalty_enabled: true
credits_enabled: true
tiering_enabled: true
max_spaces: 3
max_customers: 10000
prices:
-
id: 1
currency: MYR
billing_period: monthly
amount: 350
stripe_price_id: price_1ABC
is_active: true
description: null
created_at: '2024-01-01T00:00:00.000000Z'
updated_at: '2024-01-01T00:00:00.000000Z'
items:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: Premium
description:
type: string
example: 'Full access to all features'
is_active:
type: boolean
example: true
features:
type: object
properties:
loyalty_enabled:
type: boolean
example: true
credits_enabled:
type: boolean
example: true
tiering_enabled:
type: boolean
example: true
max_spaces:
type: integer
example: 3
max_customers:
type: integer
example: 10000
prices:
type: array
example:
-
id: 1
currency: MYR
billing_period: monthly
amount: 350
stripe_price_id: price_1ABC
is_active: true
description: null
items:
type: object
properties:
id:
type: integer
example: 1
currency:
type: string
example: MYR
billing_period:
type: string
example: monthly
amount:
type: integer
example: 350
stripe_price_id:
type: string
example: price_1ABC
is_active:
type: boolean
example: true
description:
type: string
example: null
nullable: true
created_at:
type: string
example: '2024-01-01T00:00:00.000000Z'
updated_at:
type: string
example: '2024-01-01T00:00:00.000000Z'
links:
type: object
properties:
first:
type: string
example: ...
last:
type: string
example: ...
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
per_page:
type: integer
example: 15
to:
type: integer
example: 1
total:
type: integer
example: 1
tags:
- Plans
'/api/plans/{id}':
get:
summary: 'Show Plan'
operationId: showPlan
description: ''
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
name: Premium
features:
loyalty_enabled: true
prices: []
properties:
data:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: Premium
features:
type: object
properties:
loyalty_enabled:
type: boolean
example: true
prices:
type: array
example: []
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'No query results for model [Plan] 1'
properties:
message:
type: string
example: 'No query results for model [Plan] 1'
tags:
- Plans
parameters:
-
in: path
name: id
description: 'The plan ID.'
example: 1
required: true
schema:
type: integer
/api/customer_rewards/validate:
post:
summary: 'Validate Reward Code'
operationId: validateRewardCode
description: "Validates a 6-digit reward redemption code and returns validation status along with complete reward details.\nThis endpoint is designed for cashiers and POS systems to verify reward codes before processing redemptions.\n\nThe validation process checks:\n- Code format (must be exactly 6 digits)\n- Code existence in the system\n- Code expiration status\n- Whether the code has already been used\n- Organisation-level multi-tenancy\n\nThe response includes the full reward object with `properties` field containing POS integration settings.\n**For iVend POS:** Check `reward.properties.ivend.enabled` to determine if the reward can be processed.\nUse `reward.properties.ivend.amount` to apply the discount amount."
parameters: []
responses:
200:
description: 'Valid Code'
content:
application/json:
schema:
type: object
example:
valid: true
message: 'Code is valid'
data:
id: 101
status: Pending
code: '123456'
expired_at: '2024-12-31T23:59:59.000000Z'
used_at: null
customer_id: 42
reward_id: 5
customer:
id: 42
name: 'John Doe'
phone_number: '+601234567890'
email: john@example.com
current_point: 2500
current_credits: 150
reward:
id: 5
name: 'Free Coffee'
description: 'Complimentary coffee of your choice'
terms: 'Valid at all outlets. One redemption per visit.'
is_active: true
start_at: null
end_at: null
valid_days: 30
amount: 100
point_is_active: true
credit_is_active: false
credit_amount: null
credit_earn_point: false
available_currency_types:
- point
allow_customer_self_reward: true
is_one_time_reward: false
is_direct_link_accessible: false
is_redeemable: true
is_custom_valid_days: true
is_limit_total_availability: false
total_availability: null
total_redeemed: 15
cover: gift
automations: []
notification_settings: { }
properties:
ivend:
enabled: true
type: amount
amount: 5
created_at: '2024-01-15T08:30:00.000000Z'
updated_at: '2024-02-10T10:45:00.000000Z'
properties:
valid:
type: boolean
example: true
message:
type: string
example: 'Code is valid'
data:
type: object
properties:
id:
type: integer
example: 101
status:
type: string
example: Pending
code:
type: string
example: '123456'
expired_at:
type: string
example: '2024-12-31T23:59:59.000000Z'
used_at:
type: string
example: null
nullable: true
customer_id:
type: integer
example: 42
reward_id:
type: integer
example: 5
customer:
type: object
properties:
id:
type: integer
example: 42
name:
type: string
example: 'John Doe'
phone_number:
type: string
example: '+601234567890'
email:
type: string
example: john@example.com
current_point:
type: integer
example: 2500
current_credits:
type: integer
example: 150
reward:
type: object
properties:
id:
type: integer
example: 5
name:
type: string
example: 'Free Coffee'
description:
type: string
example: 'Complimentary coffee of your choice'
terms:
type: string
example: 'Valid at all outlets. One redemption per visit.'
is_active:
type: boolean
example: true
start_at:
type: string
example: null
nullable: true
end_at:
type: string
example: null
nullable: true
valid_days:
type: integer
example: 30
amount:
type: integer
example: 100
point_is_active:
type: boolean
example: true
credit_is_active:
type: boolean
example: false
credit_amount:
type: string
example: null
nullable: true
credit_earn_point:
type: boolean
example: false
available_currency_types:
type: array
example:
- point
items:
type: string
allow_customer_self_reward:
type: boolean
example: true
is_one_time_reward:
type: boolean
example: false
is_direct_link_accessible:
type: boolean
example: false
is_redeemable:
type: boolean
example: true
is_custom_valid_days:
type: boolean
example: true
is_limit_total_availability:
type: boolean
example: false
total_availability:
type: string
example: null
nullable: true
total_redeemed:
type: integer
example: 15
cover:
type: string
example: gift
automations:
type: array
example: []
notification_settings:
type: object
properties: { }
properties:
type: object
properties:
ivend:
type: object
properties:
enabled:
type: boolean
example: true
type:
type: string
example: amount
amount:
type: integer
example: 5
created_at:
type: string
example: '2024-01-15T08:30:00.000000Z'
updated_at:
type: string
example: '2024-02-10T10:45:00.000000Z'
404:
description: 'Organisation Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Organisation not found'
properties:
message:
type: string
example: 'Organisation not found'
422:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Code Expired'
type: object
example:
valid: false
message: 'Code has expired'
error_code: CODE_EXPIRED
properties:
valid:
type: boolean
example: false
message:
type: string
example: 'Code has expired'
error_code:
type: string
example: CODE_EXPIRED
-
description: 'Code Already Used'
type: object
example:
valid: false
message: 'Code has already been used'
error_code: CODE_ALREADY_USED
properties:
valid:
type: boolean
example: false
message:
type: string
example: 'Code has already been used'
error_code:
type: string
example: CODE_ALREADY_USED
-
description: 'Invalid Code'
type: object
example:
valid: false
message: 'Invalid redemption code'
error_code: CODE_NOT_FOUND
properties:
valid:
type: boolean
example: false
message:
type: string
example: 'Invalid redemption code'
error_code:
type: string
example: CODE_NOT_FOUND
-
description: 'Invalid Format'
type: object
example:
valid: false
message: 'Code must be exactly 6 digits'
error_code: INVALID_FORMAT
properties:
valid:
type: boolean
example: false
message:
type: string
example: 'Code must be exactly 6 digits'
error_code:
type: string
example: INVALID_FORMAT
-
description: 'Validation Error'
type: object
example:
message: 'The given data was invalid.'
errors:
code:
- 'Code must be exactly 6 digits.'
organisation_id:
- 'Organisation ID is required.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
code:
type: array
example:
- 'Code must be exactly 6 digits.'
items:
type: string
organisation_id:
type: array
example:
- 'Organisation ID is required.'
items:
type: string
tags:
- 'Reward Validation'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
code:
type: string
description: 'The 6-digit reward redemption code to validate. Must be exactly 6 digits.'
example: '123456'
organisation_id:
type: integer
description: 'The ID of the organisation the validation is being performed for. Must be a valid organisation ID.'
example: 123
required:
- code
- organisation_id
/api/rewards:
get:
summary: 'List Rewards'
operationId: listRewards
description: "Returns a paginated list of rewards belonging to the authenticated user's organisation spaces.
\nSupports filtering, sorting and relationship inclusion through query parameters."
parameters:
-
in: query
name: include
description: "Comma-separated list of relationships to include. Available relationships:\n- space\n- customerRewards\n- rewardVouchers\n- inviterReferralItem\n- inviteeReferralItem\n- rewardBundleItems\n- pendingRewardVouchers"
example: 'space,customerRewards'
required: false
schema:
type: string
description: "Comma-separated list of relationships to include. Available relationships:\n- space\n- customerRewards\n- rewardVouchers\n- inviterReferralItem\n- inviteeReferralItem\n- rewardBundleItems\n- pendingRewardVouchers"
example: 'space,customerRewards'
-
in: query
name: 'filter[space_id]'
description: 'Filter by space ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by space ID.'
example: 1
-
in: query
name: 'filter[is_active]'
description: 'Filter by active status.'
example: true
required: false
schema:
type: boolean
description: 'Filter by active status.'
example: true
-
in: query
name: 'filter[is_redeemable]'
description: 'Filter by redeemable status.'
example: true
required: false
schema:
type: boolean
description: 'Filter by redeemable status.'
example: true
-
in: query
name: 'filter[is_one_time_reward]'
description: 'Filter by one-time reward status.'
example: false
required: false
schema:
type: boolean
description: 'Filter by one-time reward status.'
example: false
-
in: query
name: 'filter[allow_customer_self_reward]'
description: 'Filter by self-reward permission.'
example: true
required: false
schema:
type: boolean
description: 'Filter by self-reward permission.'
example: true
-
in: query
name: 'filter[point_is_active]'
description: 'Filter by point redemption availability.'
example: true
required: false
schema:
type: boolean
description: 'Filter by point redemption availability.'
example: true
-
in: query
name: 'filter[credit_is_active]'
description: 'Filter by credit redemption availability.'
example: true
required: false
schema:
type: boolean
description: 'Filter by credit redemption availability.'
example: true
-
in: query
name: 'filter[available]'
description: 'Filter by availability (considers active status and date range).'
example: true
required: false
schema:
type: boolean
description: 'Filter by availability (considers active status and date range).'
example: true
-
in: query
name: 'filter[name]'
description: 'Filter by reward name (partial match).'
example: Birthday
required: false
schema:
type: string
description: 'Filter by reward name (partial match).'
example: Birthday
-
in: query
name: sort
description: "Sort field and direction. Note: Prefix with `-` for descending order.\nAvailable sort fields:\n- name\n- start_at\n- end_at\n- created_at\n- amount"
example: '-created_at'
required: false
schema:
type: string
description: "Sort field and direction. Note: Prefix with `-` for descending order.\nAvailable sort fields:\n- name\n- start_at\n- end_at\n- created_at\n- amount"
example: '-created_at'
-
in: query
name: per_page
description: 'Number of records per page. Defaults to 15.'
example: 15
required: false
schema:
type: integer
description: 'Number of records per page. Defaults to 15.'
example: 15
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
name: 'Birthday Reward'
description: 'Special reward for customer birthdays'
terms: 'Valid for 30 days from issue'
is_active: true
start_at: '2025-01-01T00:00:00.000000Z'
end_at: '2025-12-31T23:59:59.000000Z'
valid_days: 30
amount: 1000
allow_customer_self_reward: false
is_one_time_reward: true
is_direct_link_accessible: false
is_redeemable: true
is_custom_valid_days: true
is_limit_total_availability: true
total_availability: 100
total_redeemed: 0
cover: gift
automations: []
notification_settings:
new_reward:
is_enabled: true
message: "You've received a birthday reward!"
properties:
ivend:
enabled: true
type: amount
amount: 10
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
links:
first: '@{{$baseUrl}}/api/rewards?page=1'
last: '@{{$baseUrl}}/api/rewards?page=1'
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
path: '@{{$baseUrl}}/api/rewards'
per_page: 15
to: 1
total: 1
properties:
data:
type: array
example:
-
id: 1
name: 'Birthday Reward'
description: 'Special reward for customer birthdays'
terms: 'Valid for 30 days from issue'
is_active: true
start_at: '2025-01-01T00:00:00.000000Z'
end_at: '2025-12-31T23:59:59.000000Z'
valid_days: 30
amount: 1000
allow_customer_self_reward: false
is_one_time_reward: true
is_direct_link_accessible: false
is_redeemable: true
is_custom_valid_days: true
is_limit_total_availability: true
total_availability: 100
total_redeemed: 0
cover: gift
automations: []
notification_settings:
new_reward:
is_enabled: true
message: "You've received a birthday reward!"
properties:
ivend:
enabled: true
type: amount
amount: 10
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
items:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Birthday Reward'
description:
type: string
example: 'Special reward for customer birthdays'
terms:
type: string
example: 'Valid for 30 days from issue'
is_active:
type: boolean
example: true
start_at:
type: string
example: '2025-01-01T00:00:00.000000Z'
end_at:
type: string
example: '2025-12-31T23:59:59.000000Z'
valid_days:
type: integer
example: 30
amount:
type: integer
example: 1000
allow_customer_self_reward:
type: boolean
example: false
is_one_time_reward:
type: boolean
example: true
is_direct_link_accessible:
type: boolean
example: false
is_redeemable:
type: boolean
example: true
is_custom_valid_days:
type: boolean
example: true
is_limit_total_availability:
type: boolean
example: true
total_availability:
type: integer
example: 100
total_redeemed:
type: integer
example: 0
cover:
type: string
example: gift
automations:
type: array
example: []
notification_settings:
type: object
properties:
new_reward:
type: object
properties:
is_enabled:
type: boolean
example: true
message:
type: string
example: "You've received a birthday reward!"
properties:
type: object
properties:
ivend:
type: object
properties:
enabled:
type: boolean
example: true
type:
type: string
example: amount
amount:
type: integer
example: 10
created_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
updated_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
required:
- cover
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/api/rewards?page=1'
last:
type: string
example: '@{{$baseUrl}}/api/rewards?page=1'
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
path:
type: string
example: '@{{$baseUrl}}/api/rewards'
per_page:
type: integer
example: 15
to:
type: integer
example: 1
total:
type: integer
example: 1
tags:
- Rewards
post:
summary: 'Create Reward'
operationId: createReward
description: "Creates a new reward in the system.
\nThe reward will be associated with the specified space in the authenticated user's organisation."
parameters: []
responses:
201:
description: 'Created successfully'
content:
application/json:
schema:
type: object
example:
data:
id: 1
name: 'Birthday Reward'
description: 'Special reward for customer birthdays'
terms: 'Valid for 30 days from issue'
is_active: true
start_at: '2025-01-01T00:00:00.000000Z'
end_at: '2025-12-31T23:59:59.000000Z'
valid_days: 30
amount: 1000
allow_customer_self_reward: false
is_one_time_reward: true
is_direct_link_accessible: false
is_redeemable: true
is_custom_valid_days: true
is_limit_total_availability: true
total_availability: 100
total_redeemed: 0
cover: gift
automations: []
notification_settings:
new_reward:
is_enabled: true
message: "You've received a birthday reward!"
properties:
ivend:
enabled: true
type: amount
amount: 10
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
message: 'Reward created successfully'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Birthday Reward'
description:
type: string
example: 'Special reward for customer birthdays'
terms:
type: string
example: 'Valid for 30 days from issue'
is_active:
type: boolean
example: true
start_at:
type: string
example: '2025-01-01T00:00:00.000000Z'
end_at:
type: string
example: '2025-12-31T23:59:59.000000Z'
valid_days:
type: integer
example: 30
amount:
type: integer
example: 1000
allow_customer_self_reward:
type: boolean
example: false
is_one_time_reward:
type: boolean
example: true
is_direct_link_accessible:
type: boolean
example: false
is_redeemable:
type: boolean
example: true
is_custom_valid_days:
type: boolean
example: true
is_limit_total_availability:
type: boolean
example: true
total_availability:
type: integer
example: 100
total_redeemed:
type: integer
example: 0
cover:
type: string
example: gift
automations:
type: array
example: []
notification_settings:
type: object
properties:
new_reward:
type: object
properties:
is_enabled:
type: boolean
example: true
message:
type: string
example: "You've received a birthday reward!"
properties:
type: object
properties:
ivend:
type: object
properties:
enabled:
type: boolean
example: true
type:
type: string
example: amount
amount:
type: integer
example: 10
created_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
updated_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
message:
type: string
example: 'Reward created successfully'
422:
description: 'Validation Error'
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
name:
- 'The name field is required.'
space_id:
- 'The selected space id is invalid.'
amount:
- 'The amount must be at least 0.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
name:
type: array
example:
- 'The name field is required.'
items:
type: string
space_id:
type: array
example:
- 'The selected space id is invalid.'
items:
type: string
amount:
type: array
example:
- 'The amount must be at least 0.'
items:
type: string
tags:
- Rewards
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
name:
type: string
description: "The reward's name."
example: 'Birthday Reward'
description:
type: string
description: 'Description of the reward.'
example: 'Special reward for customer birthdays'
terms:
type: string
description: 'Terms and conditions for the reward.'
example: 'Valid for 30 days from issue'
space_id:
type: integer
description: "The ID of the space this reward belongs to. Must be from user's organisation."
example: 1
is_active:
type: boolean
description: 'Whether the reward is active. Default: true.'
example: true
start_at:
type: datetime
description: 'nullable Start date of the reward availability.'
example: '2025-01-01'
nullable: true
end_at:
type: datetime
description: 'nullable required_without:valid_days End date of the reward availability.'
example: '2025-12-31'
nullable: true
is_redeemable:
type: boolean
description: 'Whether the reward can be redeemed. Default: true.'
example: true
is_custom_valid_days:
type: boolean
description: 'Whether to use custom validity period.'
example: true
valid_days:
type: integer
description: 'nullable required_without:end_at Number of days the reward is valid after issuance.'
example: 30
nullable: true
amount:
type: integer
description: 'Value/points for the reward.'
example: 1000
point_is_active:
type: boolean
description: 'Whether point-based redemption is enabled for this reward.'
example: true
credit_is_active:
type: boolean
description: 'Whether credit-based redemption is enabled for this reward.'
example: true
credit_amount:
type: numeric
description: 'Amount of credits required to redeem this reward (in currency units).'
example: '10.50'
nullable: true
credit_earn_point:
type: boolean
description: 'Whether customers earn points when redeeming this reward with credits.'
example: false
allow_customer_self_reward:
type: boolean
description: 'Whether customers can claim the reward themselves.'
example: true
is_one_time_reward:
type: boolean
description: 'Whether the reward can only be claimed once per customer.'
example: true
is_direct_link_accessible:
type: boolean
description: 'Whether the reward can be accessed via direct link.'
example: false
is_limit_total_availability:
type: boolean
description: 'Whether to limit total number of rewards.'
example: true
total_availability:
type: integer
description: 'required_if:is_limit_total_availability,true Maximum number of rewards available.'
example: 100
cover:
type: string
description: 'Cover preset type. Must be one of: discount, gift, voucher, custom.'
example: gift
custom_cover:
type: string
format: binary
description: 'required_if:cover.preset,custom Square image file (1:1 ratio).'
automations:
type: array
description: 'optional Automation settings for the reward.'
example: null
items:
type: string
nullable: true
notification_settings:
type: object
description: 'optional Notification settings for various reward events.'
example: []
properties:
new_reward:
type: object
description: 'Notification settings for new rewards.'
example: []
properties:
is_enabled:
type: boolean
description: 'Whether to send notifications for new rewards.'
example: false
message:
type: string
description: "Template message for new reward notification.\nAvailable variables:\n- @{{NAME}} Customer's name\n- @{{SPACE_NAME}} Space name\n- @{{REWARD_NAME}} Reward name\n- @{{REWARD_POINTS}} Points required for reward\n- @{{REWARD_EXPIRED}} Reward expiration date\n- @{{REWARD_REDEEM}} Reward redemption link/code"
example: 'Hi @{{NAME}}, you have received a new reward from @{{SPACE_NAME}}!'
expired_reward:
type: object
description: ''
example:
is_enabled: true
message: 'Hi @{{NAME}}, your reward has expired.'
properties:
is_enabled:
type: boolean
description: ''
example: true
message:
type: string
description: "Template message for expired reward notification.\nAvailable variables:\n- @{{NAME}} Customer's name\n- @{{SPACE_NAME}} Space name\n- @{{REWARD_NAME}} Reward name\n- @{{REWARD_EXPIRED}} Reward expiration date"
example: 'Hi @{{NAME}}, your reward from @{{SPACE_NAME}} has expired.'
expiring_reward:
type: object
description: ''
example:
is_enabled: true
message: 'Hi @{{NAME}}, your reward will expire soon!'
send_notification_before: '7'
properties:
is_enabled:
type: boolean
description: ''
example: true
message:
type: string
description: "Template message for expiring reward notification.\nAvailable variables:\n- @{{NAME}} Customer's name\n- @{{SPACE_NAME}} Space name\n- @{{REWARD_NAME}} Reward name\n- @{{REWARD_POINTS}} Points required for reward\n- @{{REWARD_EXPIRED}} Reward expiration date\n- @{{REWARD_REDEEM}} Reward redemption link/code"
example: 'Hi @{{NAME}}, your reward from @{{SPACE_NAME}} is expiring soon.'
send_notification_before:
type: string
description: "Days before expiry to send notification. Must be one of:\n `10` - 10 days before\n `7` - 7 days before\n `5` - 5 days before\n `3` - 3 days before\n `2` - 2 days before\n `1` - 1 day before"
example: facere
nullable: true
required:
- name
- description
- terms
- space_id
- amount
- cover
'/api/rewards/{id}':
get:
summary: 'Show Reward Details'
operationId: showRewardDetails
description: 'Retrieves detailed information about a specific reward, including any requested relationships.'
parameters:
-
in: query
name: include
description: "Comma-separated list of relationships to include. Available relationships:\n- space\n- customerRewards\n- rewardVouchers\n- inviterReferralItem\n- inviteeReferralItem\n- rewardBundleItems\n- pendingRewardVouchers"
example: 'space,customerRewards'
required: false
schema:
type: string
description: "Comma-separated list of relationships to include. Available relationships:\n- space\n- customerRewards\n- rewardVouchers\n- inviterReferralItem\n- inviteeReferralItem\n- rewardBundleItems\n- pendingRewardVouchers"
example: 'space,customerRewards'
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
name: 'Birthday Reward'
description: 'Special reward for customer birthdays'
terms: 'Valid for 30 days from issue'
is_active: true
start_at: '2025-01-01T00:00:00.000000Z'
end_at: '2025-12-31T23:59:59.000000Z'
valid_days: 30
amount: 1000
allow_customer_self_reward: false
is_one_time_reward: true
is_direct_link_accessible: false
is_redeemable: true
is_custom_valid_days: true
is_limit_total_availability: true
total_availability: 100
total_redeemed: 0
cover: gift
automations: []
notification_settings:
new_reward:
is_enabled: true
message: "You've received a birthday reward!"
properties:
ivend:
enabled: true
type: amount
amount: 10
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Birthday Reward'
description:
type: string
example: 'Special reward for customer birthdays'
terms:
type: string
example: 'Valid for 30 days from issue'
is_active:
type: boolean
example: true
start_at:
type: string
example: '2025-01-01T00:00:00.000000Z'
end_at:
type: string
example: '2025-12-31T23:59:59.000000Z'
valid_days:
type: integer
example: 30
amount:
type: integer
example: 1000
allow_customer_self_reward:
type: boolean
example: false
is_one_time_reward:
type: boolean
example: true
is_direct_link_accessible:
type: boolean
example: false
is_redeemable:
type: boolean
example: true
is_custom_valid_days:
type: boolean
example: true
is_limit_total_availability:
type: boolean
example: true
total_availability:
type: integer
example: 100
total_redeemed:
type: integer
example: 0
cover:
type: string
example: gift
automations:
type: array
example: []
notification_settings:
type: object
properties:
new_reward:
type: object
properties:
is_enabled:
type: boolean
example: true
message:
type: string
example: "You've received a birthday reward!"
properties:
type: object
properties:
ivend:
type: object
properties:
enabled:
type: boolean
example: true
type:
type: string
example: amount
amount:
type: integer
example: 10
created_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
updated_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
tags:
- Rewards
put:
summary: 'Update Reward'
operationId: updateReward
description: "Updates an existing reward's information.
\nAll fields are optional."
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
name: 'Birthday Reward'
description: 'Special reward for customer birthdays'
terms: 'Valid for 30 days from issue'
is_active: true
start_at: '2025-01-01T00:00:00.000000Z'
end_at: '2025-12-31T23:59:59.000000Z'
valid_days: 30
amount: 1000
allow_customer_self_reward: false
is_one_time_reward: true
is_direct_link_accessible: false
is_redeemable: true
is_custom_valid_days: true
is_limit_total_availability: true
total_availability: 100
total_redeemed: 0
cover: gift
automations: []
notification_settings:
new_reward:
is_enabled: true
message: "You've received a birthday reward!"
properties:
ivend:
enabled: true
type: amount
amount: 10
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
message: 'Reward updated successfully'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Birthday Reward'
description:
type: string
example: 'Special reward for customer birthdays'
terms:
type: string
example: 'Valid for 30 days from issue'
is_active:
type: boolean
example: true
start_at:
type: string
example: '2025-01-01T00:00:00.000000Z'
end_at:
type: string
example: '2025-12-31T23:59:59.000000Z'
valid_days:
type: integer
example: 30
amount:
type: integer
example: 1000
allow_customer_self_reward:
type: boolean
example: false
is_one_time_reward:
type: boolean
example: true
is_direct_link_accessible:
type: boolean
example: false
is_redeemable:
type: boolean
example: true
is_custom_valid_days:
type: boolean
example: true
is_limit_total_availability:
type: boolean
example: true
total_availability:
type: integer
example: 100
total_redeemed:
type: integer
example: 0
cover:
type: string
example: gift
automations:
type: array
example: []
notification_settings:
type: object
properties:
new_reward:
type: object
properties:
is_enabled:
type: boolean
example: true
message:
type: string
example: "You've received a birthday reward!"
properties:
type: object
properties:
ivend:
type: object
properties:
enabled:
type: boolean
example: true
type:
type: string
example: amount
amount:
type: integer
example: 10
created_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
updated_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
message:
type: string
example: 'Reward updated successfully'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
422:
description: 'Validation Error'
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
name:
- 'The name field is required.'
space_id:
- 'The selected space id is invalid.'
amount:
- 'The amount must be at least 0.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
name:
type: array
example:
- 'The name field is required.'
items:
type: string
space_id:
type: array
example:
- 'The selected space id is invalid.'
items:
type: string
amount:
type: array
example:
- 'The amount must be at least 0.'
items:
type: string
tags:
- Rewards
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
name:
type: string
description: "The reward's name."
example: 'Birthday Reward'
description:
type: string
description: 'Description of the reward.'
example: 'Special reward for customer birthdays'
terms:
type: string
description: 'Terms and conditions for the reward.'
example: 'Valid for 30 days from issue'
space_id:
type: integer
description: "The ID of the space this reward belongs to. Must be from user's organisation."
example: 1
is_active:
type: boolean
description: 'Whether the reward is active.'
example: true
start_at:
type: datetime
description: 'nullable Start date of the reward availability.'
example: '2025-01-01'
nullable: true
end_at:
type: datetime
description: 'nullable required_without:valid_days End date of the reward availability.'
example: '2025-12-31'
nullable: true
is_redeemable:
type: boolean
description: 'Whether the reward can be redeemed.'
example: true
is_custom_valid_days:
type: boolean
description: 'Whether to use custom validity period.'
example: true
valid_days:
type: integer
description: 'nullable required_without:end_at Number of days the reward is valid after issuance.'
example: 30
nullable: true
amount:
type: integer
description: 'Value/points for the reward.'
example: 1000
point_is_active:
type: boolean
description: 'Whether point-based redemption is enabled for this reward.'
example: true
credit_is_active:
type: boolean
description: 'Whether credit-based redemption is enabled for this reward.'
example: true
credit_amount:
type: numeric
description: 'Amount of credits required to redeem this reward (in currency units).'
example: '10.50'
nullable: true
credit_earn_point:
type: boolean
description: 'Whether customers earn points when redeeming this reward with credits.'
example: false
allow_customer_self_reward:
type: boolean
description: 'Whether customers can claim the reward themselves.'
example: true
is_one_time_reward:
type: boolean
description: 'Whether the reward can only be claimed once per customer.'
example: true
is_direct_link_accessible:
type: boolean
description: 'Whether the reward can be accessed via direct link.'
example: false
is_limit_total_availability:
type: boolean
description: 'Whether to limit total number of rewards.'
example: true
total_availability:
type: integer
description: 'required_if:is_limit_total_availability,true Maximum number of rewards available.'
example: 100
cover:
type: string
description: 'Cover preset type. Must be one of: discount, gift, voucher, custom.'
example: gift
custom_cover:
type: string
format: binary
description: 'Square image file (1:1 ratio).'
automations:
type: array
description: 'Automation settings for the reward.'
example: null
items:
type: string
nullable: true
notification_settings:
type: object
description: 'Notification settings for various reward events.'
example: []
properties:
new_reward:
type: object
description: 'Notification settings for new rewards.'
example: []
properties:
is_enabled:
type: boolean
description: 'Whether to send notifications for new rewards.'
example: false
message:
type: string
description: 'required_if:notification_settings.new_reward.is_enabled,true Message for new reward notification.'
example: laboriosam
expired_reward:
type: object
description: ''
example:
is_enabled: true
message: 'Hi @{{NAME}}, your reward has expired.'
properties:
is_enabled:
type: boolean
description: ''
example: true
message:
type: string
description: 'This field is required when notification_settings.expired_reward.is_enabled is true.'
example: 'Hi @{{NAME}}, your reward has expired.'
expiring_reward:
type: object
description: ''
example:
is_enabled: true
message: 'Hi @{{NAME}}, your reward will expire soon!'
send_notification_before: '7'
properties:
is_enabled:
type: boolean
description: ''
example: true
message:
type: string
description: 'This field is required when notification_settings.expiring_reward.is_enabled is true.'
example: 'Hi @{{NAME}}, your reward will expire soon!'
send_notification_before:
type: string
description: 'This field is required when notification_settings.expiring_reward.is_enabled is true.'
example: '7'
enum:
- 10
- 7
- 5
- 3
- 2
- 1
nullable: true
required:
- cover
delete:
summary: 'Delete Reward'
operationId: deleteReward
description: "Deletes a reward from the system.
\nOnly rewards that haven't been assigned to any customers can be deleted."
parameters: []
responses:
204:
description: 'No Content'
content:
application/json:
schema:
type: object
example: { }
properties: { }
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
422:
description: 'Cannot Delete'
content:
application/json:
schema:
type: object
example:
message: 'Cannot delete reward that has been assigned to customers'
properties:
message:
type: string
example: 'Cannot delete reward that has been assigned to customers'
tags:
- Rewards
parameters:
-
in: path
name: id
description: 'The ID of the reward.'
example: 1
required: true
schema:
type: integer
'/api/rewards/{reward_id}/vouchers':
get:
summary: 'List reward vouchers'
operationId: listRewardVouchers
description: 'Get a paginated list of vouchers for a specific reward.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
code: REWARD123
status: available
expired_at: '2024-12-31T23:59:59Z'
given_at: null
used_at: null
customer_reward:
customer:
id: 1
name: 'John Doe'
phone_number: '+1234567890'
links:
first: '@{{$baseUrl}}/api/rewards/@{{reward_id}}/vouchers?page=1'
last: '@{{$baseUrl}}/api/rewards/@{{reward_id}}/vouchers?page=1'
prev: null
next: null
meta:
current_page: 1
last_page: 1
per_page: 15
total: 1
properties:
data:
type: array
example:
-
id: 1
code: REWARD123
status: available
expired_at: '2024-12-31T23:59:59Z'
given_at: null
used_at: null
customer_reward:
customer:
id: 1
name: 'John Doe'
phone_number: '+1234567890'
items:
type: object
properties:
id:
type: integer
example: 1
code:
type: string
example: REWARD123
status:
type: string
example: available
expired_at:
type: string
example: '2024-12-31T23:59:59Z'
given_at:
type: string
example: null
nullable: true
used_at:
type: string
example: null
nullable: true
customer_reward:
type: object
properties:
customer:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'John Doe'
phone_number:
type: string
example: '+1234567890'
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/api/rewards/@{{reward_id}}/vouchers?page=1'
last:
type: string
example: '@{{$baseUrl}}/api/rewards/@{{reward_id}}/vouchers?page=1'
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
last_page:
type: integer
example: 1
per_page:
type: integer
example: 15
total:
type: integer
example: 1
404:
description: 'Record not found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found.'
properties:
message:
type: string
example: 'Record not found.'
tags:
- Rewards
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
status:
type: string
description: 'Filter vouchers by status.'
example: Active
enum:
- Pending
- Reserved
- Active
- Expired
- Used
nullable: true
search:
type: string
description: 'Search vouchers by code or customer information.'
example: REWARD123
nullable: true
page:
type: integer
description: 'Page number for pagination. Must be at least 1.'
example: 1
nullable: true
per_page:
type: integer
description: 'Number of items per page (max: 100). Must be at least 1. Must not be greater than 100.'
example: 15
nullable: true
post:
summary: 'Create reward voucher'
operationId: createRewardVoucher
description: "Creates a reward voucher for a specific reward. The voucher is initially created\nin a pending status, which means it has been generated but not yet assigned.\nThe voucher will remain in this state until it is given to a recipient\nor further processed."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
id: 1
code: REWARD123
status: available
expired_at: '2024-12-31T23:59:59Z'
given_at: null
used_at: null
properties:
id:
type: integer
example: 1
description: 'The unique identifier of the reward voucher.'
code:
type: string
example: REWARD123
description: 'The unique code for the voucher.'
status:
type: string
example: available
description: 'The current status of the voucher.'
expired_at:
type: string
example: '2024-12-31T23:59:59Z'
description: 'datetime|null The expiration date and time of the voucher.'
given_at:
type: string
example: null
description: 'datetime|null The date and time when the voucher was given to a customer.'
used_at:
type: string
example: null
description: 'datetime|null The date and time when the voucher was used.'
tags:
- Rewards
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
code:
type: string
description: 'Unique voucher code for the reward. Must not be greater than 255 characters.'
example: REWARD123XYZ
expired_at:
type: string
description: 'The date and time when the voucher expires. Must be a valid date. Must be a date after now.'
example: '2024-12-31T23:59:59Z'
required:
- code
- expired_at
parameters:
-
in: path
name: reward_id
description: 'The ID of the reward.'
example: 10
required: true
schema:
type: integer
'/api/rewards/{reward_id}/vouchers/{id}':
get:
summary: 'Show reward voucher details'
operationId: showRewardVoucherDetails
description: "Retrieve detailed information about a specific reward voucher.\nThis endpoint allows fetching a single voucher with optional related resources."
parameters:
-
in: query
name: include
description: "Optional related resources to include.\nPossible values: customerReward, customerReward.customer, reward"
example: 'customerReward,reward'
required: false
schema:
type: string
description: "Optional related resources to include.\nPossible values: customerReward, customerReward.customer, reward"
example: 'customerReward,reward'
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
id: 1
code: REWARD123
status: available
expired_at: '2024-12-31T23:59:59Z'
given_at: null
used_at: null
customer_reward:
id: 12
status: pending
reward:
id: 1
name: 'Loyalty Discount'
description: '10% off next purchase'
properties:
id:
type: integer
example: 1
description: 'The unique identifier of the reward voucher.'
code:
type: string
example: REWARD123
description: 'The unique code for the voucher.'
status:
type: string
example: available
description: "The current status of the voucher.\n Possible values: available, used, expired, pending."
expired_at:
type: string
example: '2024-12-31T23:59:59Z'
description: 'datetime|null The expiration date and time of the voucher.'
given_at:
type: string
example: null
description: 'datetime|null The date and time when the voucher was given to a customer.'
used_at:
type: string
example: null
description: 'datetime|null The date and time when the voucher was used.'
customer_reward:
type: object
properties:
id:
type: integer
example: 12
status:
type: string
example: pending
description: 'Details of the customer reward associated with this voucher.'
reward:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Loyalty Discount'
description:
type: string
example: '10% off next purchase'
description: 'Details of the reward associated with this voucher.'
404:
description: 'Record not found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found.'
properties:
message:
type: string
example: 'Record not found.'
tags:
- Rewards
put:
summary: 'Update reward voucher status'
operationId: updateRewardVoucherStatus
description: 'Update the status of a specific voucher.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
id: 1
code: REWARD123
status: used
expired_at: '2024-12-31T23:59:59Z'
given_at: '2024-02-09T10:00:00Z'
used_at: '2024-02-09T12:00:00Z'
properties:
id:
type: integer
example: 1
description: 'The unique identifier of the reward voucher.'
code:
type: string
example: REWARD123
description: 'The unique code for the voucher.'
status:
type: string
example: used
description: 'The current status of the voucher.'
expired_at:
type: string
example: '2024-12-31T23:59:59Z'
description: 'datetime|null The expiration date and time of the voucher.'
given_at:
type: string
example: '2024-02-09T10:00:00Z'
description: 'datetime|null The date and time when the voucher was given to a customer.'
used_at:
type: string
example: '2024-02-09T12:00:00Z'
description: 'datetime|null The date and time when the voucher was used.'
404:
description: 'Record not found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found.'
properties:
message:
type: string
example: 'Record not found.'
tags:
- Rewards
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
status:
type: string
description: 'The new status for the voucher.'
example: used
enum:
- Used
- Expired
required:
- status
delete:
summary: 'Delete reward voucher'
operationId: deleteRewardVoucher
description: 'Delete a specific voucher.'
parameters: []
responses:
204:
description: ''
content:
application/json:
schema:
type: object
example: { }
properties: { }
404:
description: 'Record not found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found.'
properties:
message:
type: string
example: 'Record not found.'
tags:
- Rewards
parameters:
-
in: path
name: reward_id
description: 'The unique identifier of the reward.'
example: 1
required: true
schema:
type: integer
-
in: path
name: id
description: 'The unique identifier of the voucher.'
example: 42
required: true
schema:
type: integer
'/api/rewards/{reward_id}/vouchers/bulk-update':
post:
summary: 'Bulk Update Reward Vouchers'
operationId: bulkUpdateRewardVouchers
description: "Updates the status of multiple reward vouchers asynchronously. The operation is queued as a background job\nand processed in chunks of 100 records. This endpoint is rate limited to 10 requests per minute."
parameters: []
responses:
202:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Bulk voucher update has been queued and will be processed shortly'
updated_count: 3
properties:
message:
type: string
example: 'Bulk voucher update has been queued and will be processed shortly'
updated_count:
type: integer
example: 3
404:
description: 'Record not found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found.'
properties:
message:
type: string
example: 'Record not found.'
422:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Validation failed'
errors:
voucher_codes:
- 'Either voucher_codes or voucher_ids must be provided.'
properties:
message:
type: string
example: 'Validation failed'
errors:
type: object
properties:
voucher_codes:
type: array
example:
- 'Either voucher_codes or voucher_ids must be provided.'
items:
type: string
429:
description: 'Too Many Requests'
content:
application/json:
schema:
type: object
example:
message: 'Too Many Attempts.'
properties:
message:
type: string
example: 'Too Many Attempts.'
tags:
- Rewards
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
voucher_codes:
type: array
description: 'A valid voucher code string. This field is required when voucher_codes is present. The code of an existing record in the reward_vouchers table.'
example:
- REWARD123
items:
type: string
voucher_ids:
type: array
description: 'A valid voucher ID integer. This field is required when voucher_ids is present. The id of an existing record in the reward_vouchers table.'
example:
- 1
items:
type: integer
status:
type: string
description: 'The new status to be applied to the selected vouchers.'
example: Used
enum:
- Used
- Expired
required:
- status
parameters:
-
in: path
name: reward_id
description: 'The ID of the reward these vouchers belong to.'
example: 1
required: true
schema:
type: integer
'/api/rewards/{reward_id}/vouchers/bulk-import':
post:
summary: 'Bulk import vouchers'
operationId: bulkImportVouchers
description: "Import multiple vouchers for a reward asynchronously. The operation is queued and processed in chunks of 100 records.\nLimited to 10,000 vouchers per request and rate limited to 10 requests per minute."
parameters: []
responses:
202:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Bulk voucher import has been queued and will be processed shortly'
properties:
message:
type: string
example: 'Bulk voucher import has been queued and will be processed shortly'
404:
description: 'Record not found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found.'
properties:
message:
type: string
example: 'Record not found.'
422:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
vouchers:
- 'The vouchers must not have more than 10000 items.'
'vouchers.*.code':
- 'The vouchers.0.expired_at has already been taken.'
'vouchers.*.expired_at':
- 'The vouchers.0.expired_at must be a date after now.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
vouchers:
type: array
example:
- 'The vouchers must not have more than 10000 items.'
items:
type: string
'vouchers.*.code':
type: array
example:
- 'The vouchers.0.expired_at has already been taken.'
items:
type: string
'vouchers.*.expired_at':
type: array
example:
- 'The vouchers.0.expired_at must be a date after now.'
items:
type: string
429:
description: 'Too Many Requests'
content:
application/json:
schema:
type: object
example:
message: 'Too Many Attempts.'
properties:
message:
type: string
example: 'Too Many Attempts.'
tags:
- Rewards
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
vouchers:
type: array
description: 'Array of vouchers to import (max 10,000).'
example:
-
code: REWARD123
expired_at: '2024-12-31T23:59:59Z'
items:
type: string
auto_reassign:
type: boolean
description: 'Whether to automatically trigger voucher reassignment to pending customer rewards after import completes. Defaults to false.'
example: false
required:
- vouchers
parameters:
-
in: path
name: reward_id
description: 'The ID of the reward.'
example: 2
required: true
schema:
type: integer
/api/roles:
get:
summary: 'List Roles'
operationId: listRoles
description: 'Returns a paginated list of all available roles.'
parameters:
-
in: query
name: 'filter[name]'
description: 'Filter by role name (partial match).'
example: Admin
required: false
schema:
type: string
description: 'Filter by role name (partial match).'
example: Admin
-
in: query
name: sort
description: 'Sort field. Allowed: id, name, created_at.'
example: '-id'
required: false
schema:
type: string
description: 'Sort field. Allowed: id, name, created_at.'
example: '-id'
-
in: query
name: per_page
description: 'Records per page.'
example: 15
required: false
schema:
type: integer
description: 'Records per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
name: 'Super Admin'
guard_name: web
created_at: '2024-01-01T00:00:00.000000Z'
updated_at: '2024-01-01T00:00:00.000000Z'
links:
first: ...
last: ...
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
per_page: 15
to: 1
total: 1
properties:
data:
type: array
example:
-
id: 1
name: 'Super Admin'
guard_name: web
created_at: '2024-01-01T00:00:00.000000Z'
updated_at: '2024-01-01T00:00:00.000000Z'
items:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Super Admin'
guard_name:
type: string
example: web
created_at:
type: string
example: '2024-01-01T00:00:00.000000Z'
updated_at:
type: string
example: '2024-01-01T00:00:00.000000Z'
links:
type: object
properties:
first:
type: string
example: ...
last:
type: string
example: ...
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
per_page:
type: integer
example: 15
to:
type: integer
example: 1
total:
type: integer
example: 1
tags:
- Roles
'/api/roles/{id}':
get:
summary: 'Show Role'
operationId: showRole
description: ''
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
name: Admin
guard_name: web
properties:
data:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: Admin
guard_name:
type: string
example: web
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'No query results for model [Role] 1'
properties:
message:
type: string
example: 'No query results for model [Role] 1'
tags:
- Roles
parameters:
-
in: path
name: id
description: 'The role ID.'
example: 1
required: true
schema:
type: integer
/api/spaces:
get:
summary: 'List Spaces'
operationId: listSpaces
description: "Returns a paginated list of spaces belonging to the authenticated user's organisation.
\nSupports filtering, sorting and relationship inclusion through query parameters.
"
parameters:
-
in: query
name: include
description: "Comma-separated list of relationships to include.\nAvailable relationships:\n- category (Space category details)\n- address (Physical address details)"
example: 'category,address'
required: false
schema:
type: string
description: "Comma-separated list of relationships to include.\nAvailable relationships:\n- category (Space category details)\n- address (Physical address details)"
example: 'category,address'
-
in: query
name: 'filter[visibility]'
description: "Filter by visibility status.\nAvailable options:\n- public (Listed and viewable by anyone)\n- private (Not listed, viewable with link)\n- hidden (Not listed, cannot be searched)\n- draft (Not listed, cannot be searched)"
example: public
required: false
schema:
type: string
description: "Filter by visibility status.\nAvailable options:\n- public (Listed and viewable by anyone)\n- private (Not listed, viewable with link)\n- hidden (Not listed, cannot be searched)\n- draft (Not listed, cannot be searched)"
example: public
-
in: query
name: 'filter[category_id]'
description: 'Filter by category ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by category ID.'
example: 1
-
in: query
name: 'filter[name]'
description: 'Filter by space name (partial match).'
example: Coworking
required: false
schema:
type: string
description: 'Filter by space name (partial match).'
example: Coworking
-
in: query
name: 'filter[description]'
description: 'Filter by space description (partial match).'
example: modern
required: false
schema:
type: string
description: 'Filter by space description (partial match).'
example: modern
-
in: query
name: 'filter[email]'
description: 'Filter by space email (partial match).'
example: space@example.com
required: false
schema:
type: string
description: 'Filter by space email (partial match).'
example: space@example.com
-
in: query
name: 'filter[organisation_id]'
description: 'Filter by organisation ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by organisation ID.'
example: 1
-
in: query
name: 'filter[has_posts]'
description: 'Filter spaces that have posts.'
example: true
required: false
schema:
type: boolean
description: 'Filter spaces that have posts.'
example: true
-
in: query
name: sort
description: "Sort field and direction.\nAvailable fields:\n- name (Sort by space name)\n- created_at (Sort by creation date)\n- updated_at (Sort by last update)\n- visits (Sort by number of visits)\nDefault: -created_at"
example: '-visits'
required: false
schema:
type: string
description: "Sort field and direction.\nAvailable fields:\n- name (Sort by space name)\n- created_at (Sort by creation date)\n- updated_at (Sort by last update)\n- visits (Sort by number of visits)\nDefault: -created_at"
example: '-visits'
-
in: query
name: per_page
description: 'Number of records per page. Default: 15.'
example: 15
required: false
schema:
type: integer
description: 'Number of records per page. Default: 15.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
uuid: f2d75bf6-3cb8-305c-8f70-f71fe697cba3
name: 'Modern Coworking Space'
slug: modern-coworking-space
matterport_model_id: null
description: 'A modern coworking space in the heart of the city'
visibility: public
email: space@example.com
phone_number: '+60123456789'
website: '@{{$baseUrl}}/spaces/modern-coworking'
social_media:
-
name: Twitter
username: '@modernspace'
-
name: Facebook
username: modernspacekl
organisation_id: 1
category_id: 2
visits: 1250
created_at: '2024-01-15T08:00:00Z'
updated_at: '2024-02-08T14:30:00Z'
deleted_at: null
address:
id: 1
address_line_1: '123 Jalan Sultan Ismail'
address_line_2: 'Level 23'
postal_code: '50250'
city: 'Kuala Lumpur'
state: 'Wilayah Persekutuan'
country: Malaysia
category:
id: 2
name: 'Coworking Space'
slug: coworking-space
links:
public_url: '@{{$baseUrl}}/spaces/modern-coworking-space'
-
id: 2
name: 'Creative Studio'
slug: creative-studio
matterport_model_id: abc123xyz
description: 'A creative studio space perfect for photographers and content creators'
visibility: public
email: studio@example.com
phone_number: '+60123456790'
website: '@{{$baseUrl}}/spaces/creative-studio'
social_media:
-
name: Instagram
username: '@creativestudiomy'
organisation_id: 1
category_id: 3
visits: 890
created_at: '2024-01-20T10:00:00Z'
updated_at: '2024-02-07T16:45:00Z'
deleted_at: null
address:
id: 2
address_line_1: '45 Jalan Telawi'
address_line_2: null
postal_code: '59100'
city: Bangsar
state: 'Kuala Lumpur'
country: Malaysia
category:
id: 3
name: 'Studio Space'
slug: studio-space
links:
public_url: '@{{$baseUrl}}/spaces/creative-studio'
links:
first: '@{{$baseUrl}}/api/v1/spaces?page=1'
last: '@{{$baseUrl}}/api/v1/spaces?page=5'
prev: null
next: '@{{$baseUrl}}/api/v1/spaces?page=2'
meta:
current_page: 1
from: 1
last_page: 5
path: '@{{$baseUrl}}/api/v1/spaces'
per_page: 15
to: 15
total: 68
properties:
data:
type: array
example:
-
id: 1
uuid: f2d75bf6-3cb8-305c-8f70-f71fe697cba3
name: 'Modern Coworking Space'
slug: modern-coworking-space
matterport_model_id: null
description: 'A modern coworking space in the heart of the city'
visibility: public
email: space@example.com
phone_number: '+60123456789'
website: '@{{$baseUrl}}/spaces/modern-coworking'
social_media:
-
name: Twitter
username: '@modernspace'
-
name: Facebook
username: modernspacekl
organisation_id: 1
category_id: 2
visits: 1250
created_at: '2024-01-15T08:00:00Z'
updated_at: '2024-02-08T14:30:00Z'
deleted_at: null
address:
id: 1
address_line_1: '123 Jalan Sultan Ismail'
address_line_2: 'Level 23'
postal_code: '50250'
city: 'Kuala Lumpur'
state: 'Wilayah Persekutuan'
country: Malaysia
category:
id: 2
name: 'Coworking Space'
slug: coworking-space
links:
public_url: '@{{$baseUrl}}/spaces/modern-coworking-space'
-
id: 2
name: 'Creative Studio'
slug: creative-studio
matterport_model_id: abc123xyz
description: 'A creative studio space perfect for photographers and content creators'
visibility: public
email: studio@example.com
phone_number: '+60123456790'
website: '@{{$baseUrl}}/spaces/creative-studio'
social_media:
-
name: Instagram
username: '@creativestudiomy'
organisation_id: 1
category_id: 3
visits: 890
created_at: '2024-01-20T10:00:00Z'
updated_at: '2024-02-07T16:45:00Z'
deleted_at: null
address:
id: 2
address_line_1: '45 Jalan Telawi'
address_line_2: null
postal_code: '59100'
city: Bangsar
state: 'Kuala Lumpur'
country: Malaysia
category:
id: 3
name: 'Studio Space'
slug: studio-space
links:
public_url: '@{{$baseUrl}}/spaces/creative-studio'
items:
type: object
properties:
id:
type: integer
example: 1
uuid:
type: string
example: f2d75bf6-3cb8-305c-8f70-f71fe697cba3
name:
type: string
example: 'Modern Coworking Space'
slug:
type: string
example: modern-coworking-space
matterport_model_id:
type: string
example: null
nullable: true
description:
type: string
example: 'A modern coworking space in the heart of the city'
visibility:
type: string
example: public
email:
type: string
example: space@example.com
phone_number:
type: string
example: '+60123456789'
website:
type: string
example: '@{{$baseUrl}}/spaces/modern-coworking'
social_media:
type: array
example:
-
name: Twitter
username: '@modernspace'
-
name: Facebook
username: modernspacekl
items:
type: object
properties:
name:
type: string
example: Twitter
username:
type: string
example: '@modernspace'
organisation_id:
type: integer
example: 1
category_id:
type: integer
example: 2
visits:
type: integer
example: 1250
created_at:
type: string
example: '2024-01-15T08:00:00Z'
updated_at:
type: string
example: '2024-02-08T14:30:00Z'
deleted_at:
type: string
example: null
nullable: true
address:
type: object
properties:
id:
type: integer
example: 1
address_line_1:
type: string
example: '123 Jalan Sultan Ismail'
address_line_2:
type: string
example: 'Level 23'
postal_code:
type: string
example: '50250'
city:
type: string
example: 'Kuala Lumpur'
state:
type: string
example: 'Wilayah Persekutuan'
country:
type: string
example: Malaysia
category:
type: object
properties:
id:
type: integer
example: 2
name:
type: string
example: 'Coworking Space'
slug:
type: string
example: coworking-space
links:
type: object
properties:
public_url:
type: string
example: '@{{$baseUrl}}/spaces/modern-coworking-space'
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/api/v1/spaces?page=1'
last:
type: string
example: '@{{$baseUrl}}/api/v1/spaces?page=5'
prev:
type: string
example: null
nullable: true
next:
type: string
example: '@{{$baseUrl}}/api/v1/spaces?page=2'
description: "Navigation URLs:\n- public_url: string (Full URL to public space page)"
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 5
path:
type: string
example: '@{{$baseUrl}}/api/v1/spaces'
per_page:
type: integer
example: 15
to:
type: integer
example: 15
total:
type: integer
example: 68
tags:
- Spaces
post:
summary: 'Create Space'
operationId: createSpace
description: "Creates a new space in the system.
\nThe space will be associated with the authenticated user's organisation.
"
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
data:
id: 1
uuid: f2d75bf6-3cb8-305c-8f70-f71fe697cba3
name: 'Modern Coworking Space'
slug: modern-coworking-space
matterport_model_id: null
description: 'A modern coworking space in the heart of the city offering flexible workspace solutions for professionals and teams'
visibility: public
email: space@example.com
phone_number: '+60123456789'
website: 'https://pixalink.io/spaces/modern-coworking'
social_media:
-
name: Twitter
username: '@modernspace'
-
name: Facebook
username: modernspacekl
-
name: Instagram
username: '@modernspace.kl'
organisation_id: 1
category_id: 2
visits: 0
created_at: '2024-02-08T15:00:00Z'
updated_at: '2024-02-08T15:00:00Z'
deleted_at: null
address:
id: 1
address_line_1: '123 Jalan Sultan Ismail'
address_line_2: 'Level 23'
postal_code: '50250'
city: 'Kuala Lumpur'
state: 'Wilayah Persekutuan'
country: Malaysia
links:
public_url: '@{{$baseUrl}}/spaces/modern-coworking-space'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
uuid:
type: string
example: f2d75bf6-3cb8-305c-8f70-f71fe697cba3
name:
type: string
example: 'Modern Coworking Space'
slug:
type: string
example: modern-coworking-space
matterport_model_id:
type: string
example: null
nullable: true
description:
type: string
example: 'A modern coworking space in the heart of the city offering flexible workspace solutions for professionals and teams'
visibility:
type: string
example: public
email:
type: string
example: space@example.com
phone_number:
type: string
example: '+60123456789'
website:
type: string
example: 'https://pixalink.io/spaces/modern-coworking'
social_media:
type: array
example:
-
name: Twitter
username: '@modernspace'
-
name: Facebook
username: modernspacekl
-
name: Instagram
username: '@modernspace.kl'
items:
type: object
properties:
name:
type: string
example: Twitter
username:
type: string
example: '@modernspace'
organisation_id:
type: integer
example: 1
category_id:
type: integer
example: 2
visits:
type: integer
example: 0
created_at:
type: string
example: '2024-02-08T15:00:00Z'
updated_at:
type: string
example: '2024-02-08T15:00:00Z'
deleted_at:
type: string
example: null
nullable: true
address:
type: object
properties:
id:
type: integer
example: 1
address_line_1:
type: string
example: '123 Jalan Sultan Ismail'
address_line_2:
type: string
example: 'Level 23'
postal_code:
type: string
example: '50250'
city:
type: string
example: 'Kuala Lumpur'
state:
type: string
example: 'Wilayah Persekutuan'
country:
type: string
example: Malaysia
links:
type: object
properties:
public_url:
type: string
example: '@{{$baseUrl}}/spaces/modern-coworking-space'
422:
description: 'Validation Error'
content:
text/plain:
schema:
type: string
example: "{\n \"message\": \"The given data was invalid.\",\n \"errors\": {\n \"name\": [\n \"The name field is required.\"\n ],\n \"description\": [\n \"The description field is required.\"\n ],\n \"visibility\": [\n \"The visibility field is required.\",\n \"The selected visibility is invalid.\"\n ],\n \"category_id\": [\n \"The category id field is required.\",\n \"The selected category id is invalid.\"\n ],\n \"address.address_line_1\": [\n \"The address line 1 field is required.\"\n ],\n \"address.postal_code\": [\n \"The postal code must be 5 digits.\"\n ],\n \"address.country\": [\n \"The selected country is invalid.\"\n ],\n \"phone_number\": [\n \"The phone number format is invalid.\"\n ]\n }"
tags:
- Spaces
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'The name of the space.'
example: 'Modern Coworking Space'
description:
type: string
description: 'The description of the space (max: 65535 characters).'
example: 'A modern coworking space in the heart of the city'
visibility:
type: string
description: "The visibility of the space. Must be one of:\n- Public (Listed and viewable by anyone)\n- Private (Not listed, viewable with link)\n- Hidden (Not listed, cannot be searched)\n- Draft (Not listed, cannot be searched)"
example: Public
email:
type: string
description: 'optional A valid email address.'
example: space@example.com
nullable: true
phone_number:
type: string
description: 'The contact phone number in E.164 format (e.g. +60125452222). Starting with + followed by country code and number with no spaces or special characters.'
example: '+60123456789'
website:
type: string
description: 'optional A valid URL.'
example: '@{{$baseUrl}}'
nullable: true
category_id:
type: integer
description: 'The ID of a subcategory.'
example: 1
social_media:
type: array
description: 'optional Array of social media details.'
example:
- qui
items:
type: string
nullable: true
address:
type: array
description: "The space's address."
example:
- aliquam
items:
type: string
tags:
type: array
description: 'optional Array of tag names.'
example:
- Coworking
- 24/7
items:
type: string
classification:
type: array
description: 'optional Array of classification tags.'
example:
- Premium
- Featured
items:
type: string
media:
type: object
description: 'optional Media files for the space.'
example: null
properties:
preview:
type: string
format: binary
description: 'optional Preview image/video (400x225, max 10MB). Supports png, jpeg, ico.'
featured_image:
type: string
format: binary
description: 'optional Featured image for SEO (max 10MB). Supports images only.'
gallery:
type: array
description: 'optional Array of gallery images (1920x1080, max 10MB each). Min 1 required if sent.'
example: null
items:
type: string
logo:
type: string
format: binary
description: 'optional Company logo (1:1 ratio, max 5MB). Supports images only.'
required:
- name
- description
- visibility
- category_id
- address
'/api/spaces/{id}':
get:
summary: 'Show Space Details'
operationId: showSpaceDetails
description: 'Retrieves detailed information about a specific space, including any requested relationships.'
parameters:
-
in: query
name: include
description: "Comma-separated list of relationships to include. Available relationships:\n- category\n- address\n- posts\n- widgets\n- activeWidgets\n- activeCalendars"
example: 'category,address'
required: false
schema:
type: string
description: "Comma-separated list of relationships to include. Available relationships:\n- category\n- address\n- posts\n- widgets\n- activeWidgets\n- activeCalendars"
example: 'category,address'
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
uuid: f2d75bf6-3cb8-305c-8f70-f71fe697cba3
name: 'Modern Coworking Space'
slug: modern-coworking-space
matterport_model_id: null
description: 'A modern coworking space in the heart of the city'
visibility: public
email: space@example.com
phone_number: '+60123456789'
website: '@{{$baseUrl}}'
social_media:
-
name: Twitter
username: '@modernspace'
tags:
- Coworking
- 24/7
classification:
- Premium
- Featured
category_id: 1
organisation_id: 1
visits: 150
created_at: '2024-02-02T12:00:00Z'
updated_at: '2024-02-02T12:00:00Z'
address:
id: 1
address_line_1: '123 Main Street'
address_line_2: 'Suite 45'
postal_code: '50450'
city: 'Petaling Jaya'
state: Selangor
country: Malaysia
media:
preview:
url: '@{{$baseUrl}}/media/preview.jpg'
type: image
featured_image:
url: '@{{$baseUrl}}/media/featured.jpg'
gallery:
-
url: '@{{$baseUrl}}/media/gallery1.jpg'
logo:
url: '@{{$baseUrl}}/media/logo.jpg'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
uuid:
type: string
example: f2d75bf6-3cb8-305c-8f70-f71fe697cba3
name:
type: string
example: 'Modern Coworking Space'
slug:
type: string
example: modern-coworking-space
matterport_model_id:
type: string
example: null
nullable: true
description:
type: string
example: 'A modern coworking space in the heart of the city'
visibility:
type: string
example: public
email:
type: string
example: space@example.com
phone_number:
type: string
example: '+60123456789'
website:
type: string
example: '@{{$baseUrl}}'
social_media:
type: array
example:
-
name: Twitter
username: '@modernspace'
items:
type: object
properties:
name:
type: string
example: Twitter
username:
type: string
example: '@modernspace'
tags:
type: array
example:
- Coworking
- 24/7
items:
type: string
classification:
type: array
example:
- Premium
- Featured
items:
type: string
category_id:
type: integer
example: 1
organisation_id:
type: integer
example: 1
visits:
type: integer
example: 150
created_at:
type: string
example: '2024-02-02T12:00:00Z'
updated_at:
type: string
example: '2024-02-02T12:00:00Z'
address:
type: object
properties:
id:
type: integer
example: 1
address_line_1:
type: string
example: '123 Main Street'
address_line_2:
type: string
example: 'Suite 45'
postal_code:
type: string
example: '50450'
city:
type: string
example: 'Petaling Jaya'
state:
type: string
example: Selangor
country:
type: string
example: Malaysia
media:
type: object
properties:
preview:
type: object
properties:
url:
type: string
example: '@{{$baseUrl}}/media/preview.jpg'
type:
type: string
example: image
featured_image:
type: object
properties:
url:
type: string
example: '@{{$baseUrl}}/media/featured.jpg'
gallery:
type: array
example:
-
url: '@{{$baseUrl}}/media/gallery1.jpg'
items:
type: object
properties:
url:
type: string
example: '@{{$baseUrl}}/media/gallery1.jpg'
logo:
type: object
properties:
url:
type: string
example: '@{{$baseUrl}}/media/logo.jpg'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
tags:
- Spaces
put:
summary: 'Update Space'
operationId: updateSpace
description: "Updates an existing space's information.
All fields are optional.
\nOmitted fields will retain their current values."
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
uuid: f2d75bf6-3cb8-305c-8f70-f71fe697cba3
name: 'Modern Coworking Space'
slug: modern-coworking-space
matterport_model_id: null
description: 'A modern coworking space in the heart of the city'
visibility: public
email: space@example.com
phone_number: '+60123456789'
website: '@{{$baseUrl}}'
social_media:
-
name: Twitter
username: '@modernspace'
tags:
- Coworking
- 24/7
classification:
- Premium
- Featured
category_id: 1
organisation_id: 1
visits: 150
created_at: '2024-02-02T12:00:00Z'
updated_at: '2024-02-02T12:00:00Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
uuid:
type: string
example: f2d75bf6-3cb8-305c-8f70-f71fe697cba3
name:
type: string
example: 'Modern Coworking Space'
slug:
type: string
example: modern-coworking-space
matterport_model_id:
type: string
example: null
nullable: true
description:
type: string
example: 'A modern coworking space in the heart of the city'
visibility:
type: string
example: public
email:
type: string
example: space@example.com
phone_number:
type: string
example: '+60123456789'
website:
type: string
example: '@{{$baseUrl}}'
social_media:
type: array
example:
-
name: Twitter
username: '@modernspace'
items:
type: object
properties:
name:
type: string
example: Twitter
username:
type: string
example: '@modernspace'
tags:
type: array
example:
- Coworking
- 24/7
items:
type: string
classification:
type: array
example:
- Premium
- Featured
items:
type: string
category_id:
type: integer
example: 1
organisation_id:
type: integer
example: 1
visits:
type: integer
example: 150
created_at:
type: string
example: '2024-02-02T12:00:00Z'
updated_at:
type: string
example: '2024-02-02T12:00:00Z'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
422:
description: 'Validation Error'
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
email:
- 'The email must be a valid email address.'
phone_number:
- 'The phone number must be a valid phone number.'
visibility:
- 'The selected visibility is invalid.'
address.country:
- 'The selected country is invalid. Only Malaysia and Singapore are allowed.'
media.preview:
- 'The preview must be an image file of type: png, jpg, jpeg, ico.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
email:
type: array
example:
- 'The email must be a valid email address.'
items:
type: string
phone_number:
type: array
example:
- 'The phone number must be a valid phone number.'
items:
type: string
visibility:
type: array
example:
- 'The selected visibility is invalid.'
items:
type: string
address.country:
type: array
example:
- 'The selected country is invalid. Only Malaysia and Singapore are allowed.'
items:
type: string
media.preview:
type: array
example:
- 'The preview must be an image file of type: png, jpg, jpeg, ico.'
items:
type: string
tags:
- Spaces
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'optional The name of the space.'
example: 'Modern Coworking Space'
description:
type: string
description: 'optional The description of the space (max: 65535 characters).'
example: 'A modern coworking space in the heart of the city'
visibility:
type: string
description: "optional The visibility status. Must be one of:\n- Public (Listed and viewable by anyone)\n- Private (Not listed, viewable with link)\n- Hidden (Not listed, cannot be searched)\n- Draft (Not listed, cannot be searched)"
example: Public
email:
type: string
description: 'optional A valid email address.'
example: space@example.com
nullable: true
phone_number:
type: string
description: 'The contact phone number in E.164 format (e.g. +60125452222). Starting with + followed by country code and number with no spaces or special characters.'
example: ipsum
website:
type: string
description: 'optional A valid URL.'
example: '@{{$baseUrl}}'
nullable: true
category_id:
type: integer
description: 'optional The ID of a subcategory.'
example: 1
social_media:
type: array
description: 'optional Array of social media details. Will replace existing entries.'
example:
- amet
items:
type: string
nullable: true
address:
type: array
description: "optional The space's address details to update."
example:
- quasi
items:
type: string
tags:
type: array
description: 'optional Array of tag names to update.'
example:
- Coworking
- 24/7
items:
type: string
classification:
type: array
description: 'optional Array of classification tags to update.'
example:
- Premium
- Featured
items:
type: string
media:
type: object
description: 'optional Media files to update.'
example: null
properties:
preview:
type: string
format: binary
description: 'optional Preview image/video (400x225, max 10MB). Supports png, jpeg, ico.'
featured_image:
type: string
format: binary
description: 'optional Featured image for SEO (max 10MB). Supports images only.'
gallery:
type: array
description: 'optional Array of gallery images (1920x1080, max 10MB each). Min 1 required if sent.'
example: null
items:
type: string
logo:
type: string
format: binary
description: 'optional Company logo (1:1 ratio, max 5MB). Supports images only.'
delete:
summary: 'Delete Space'
operationId: deleteSpace
description: "Deletes a space and all its associated data including:\n- Address\n- Media (preview, gallery, logo)\n- Tags and classifications\n- Widgets\n- Posts\n- POS integration settings"
parameters: []
responses:
204:
description: 'No Content'
content:
application/json:
schema:
type: object
example: { }
properties: { }
403:
description: 'Not Authorized'
content:
application/json:
schema:
type: object
example:
message: 'You are not authorized to delete this space'
properties:
message:
type: string
example: 'You are not authorized to delete this space'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
tags:
- Spaces
parameters:
-
in: path
name: id
description: 'The ID of the space.'
example: 1
required: true
schema:
type: integer
/api/transactions:
get:
summary: 'List Transactions'
operationId: listTransactions
description: 'Get a paginated list of transactions for the organisation.'
parameters:
-
in: query
name: 'filter[customer_id]'
description: 'Filter by customer ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by customer ID.'
example: 1
-
in: query
name: 'filter[type]'
description: "Filter by transaction type. Available types:\n- Purchase: Points earned from purchases\n- Review: Points earned from reviews\n- Referral: Points earned from referrals\n- Point Reward Redeem: Points spent on reward redemption"
example: Purchase
required: false
schema:
type: string
description: "Filter by transaction type. Available types:\n- Purchase: Points earned from purchases\n- Review: Points earned from reviews\n- Referral: Points earned from referrals\n- Point Reward Redeem: Points spent on reward redemption"
example: Purchase
-
in: query
name: 'filter[space_id]'
description: 'Filter by space ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by space ID.'
example: 1
-
in: query
name: 'filter[amount]'
description: 'Filter by amount with operators (>, <, =).'
example: '>100'
required: false
schema:
type: string
description: 'Filter by amount with operators (>, <, =).'
example: '>100'
-
in: query
name: 'filter[created_at]'
description: 'Filter by date with operators.'
example: '>2024-01-01'
required: false
schema:
type: string
description: 'Filter by date with operators.'
example: '>2024-01-01'
-
in: query
name: 'filter[updated_at]'
description: 'Filter by updated date with operators.'
example: '>2024-01-01'
required: false
schema:
type: string
description: 'Filter by updated date with operators.'
example: '>2024-01-01'
-
in: query
name: sort
description: 'Sort field (created_at, amount). Use -field for descending.'
example: '-created_at'
required: false
schema:
type: string
description: 'Sort field (created_at, amount). Use -field for descending.'
example: '-created_at'
-
in: query
name: include
description: 'Include relationships (customer, reward, space, customerRewards).'
example: 'customer,reward,customerRewards'
required: false
schema:
type: string
description: 'Include relationships (customer, reward, space, customerRewards).'
example: 'customer,reward,customerRewards'
-
in: query
name: per_page
description: 'Number of records per page.'
example: 15
required: false
schema:
type: integer
description: 'Number of records per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'List of transactions'
type: object
example:
data:
-
id: 1
amount: 1000
valid_amount: 1000
operation: +
type: Purchase
remarks: 'Purchase at Store #123'
customer_id: 1
space_id: 1
reward_id: null
custom_properties:
purchase_amount: 1000
receipt_number: R12345
expired_at: '2024-02-10T23:59:59.000000Z'
created_at: '2024-02-09T10:00:00.000000Z'
updated_at: '2024-02-09T10:00:00.000000Z'
customer:
id: 1
name: 'John Smith'
email: john.smith@example.com
phone_number: '+60123456789'
current_point: 2500
current_credits: 150
-
id: 2
amount: 500
valid_amount: 500
operation: '-'
type: Point_Reward_Redeem
remarks: 'Reward: Free Coffee'
customer_id: 1
space_id: 1
reward_id: 5
custom_properties: null
expired_at: null
created_at: '2024-02-08T15:30:00.000000Z'
updated_at: '2024-02-08T15:30:00.000000Z'
reward:
id: 5
name: 'Free Coffee'
amount: 500
description: 'Redeem a free coffee at any of our locations'
customerRewards:
-
id: 1
status: Used
code: null
expired_at: '2024-02-08T15:30:00.000000Z'
used_at: '2024-02-08T15:30:00.000000Z'
customer_id: 1
reward_id: 5
links:
first: '@{{$baseUrl}}/api/v1/transactions?page=1'
last: '@{{$baseUrl}}/api/v1/transactions?page=5'
prev: null
next: '@{{$baseUrl}}/api/v1/transactions?page=2'
meta:
current_page: 1
from: 1
last_page: 5
links:
-
url: null
label: '« Previous'
active: false
-
url: '@{{$baseUrl}}/api/v1/transactions?page=1'
label: '1'
active: true
-
url: '@{{$baseUrl}}/api/v1/transactions?page=2'
label: '2'
active: false
path: '@{{$baseUrl}}/api/v1/transactions'
per_page: 15
to: 15
total: 68
properties:
data:
type: array
example:
-
id: 1
amount: 1000
valid_amount: 1000
operation: +
type: Purchase
remarks: 'Purchase at Store #123'
customer_id: 1
space_id: 1
reward_id: null
custom_properties:
purchase_amount: 1000
receipt_number: R12345
expired_at: '2024-02-10T23:59:59.000000Z'
created_at: '2024-02-09T10:00:00.000000Z'
updated_at: '2024-02-09T10:00:00.000000Z'
customer:
id: 1
name: 'John Smith'
email: john.smith@example.com
phone_number: '+60123456789'
current_point: 2500
current_credits: 150
-
id: 2
amount: 500
valid_amount: 500
operation: '-'
type: Point_Reward_Redeem
remarks: 'Reward: Free Coffee'
customer_id: 1
space_id: 1
reward_id: 5
custom_properties: null
expired_at: null
created_at: '2024-02-08T15:30:00.000000Z'
updated_at: '2024-02-08T15:30:00.000000Z'
reward:
id: 5
name: 'Free Coffee'
amount: 500
description: 'Redeem a free coffee at any of our locations'
customerRewards:
-
id: 1
status: Used
code: null
expired_at: '2024-02-08T15:30:00.000000Z'
used_at: '2024-02-08T15:30:00.000000Z'
customer_id: 1
reward_id: 5
items:
type: object
properties:
id:
type: integer
example: 1
amount:
type: integer
example: 1000
valid_amount:
type: integer
example: 1000
operation:
type: string
example: +
type:
type: string
example: Purchase
remarks:
type: string
example: 'Purchase at Store #123'
customer_id:
type: integer
example: 1
space_id:
type: integer
example: 1
reward_id:
type: string
example: null
nullable: true
custom_properties:
type: object
properties:
purchase_amount:
type: integer
example: 1000
receipt_number:
type: string
example: R12345
expired_at:
type: string
example: '2024-02-10T23:59:59.000000Z'
created_at:
type: string
example: '2024-02-09T10:00:00.000000Z'
updated_at:
type: string
example: '2024-02-09T10:00:00.000000Z'
customer:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'John Smith'
email:
type: string
example: john.smith@example.com
phone_number:
type: string
example: '+60123456789'
current_point:
type: integer
example: 2500
current_credits:
type: integer
example: 150
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/api/v1/transactions?page=1'
last:
type: string
example: '@{{$baseUrl}}/api/v1/transactions?page=5'
prev:
type: string
example: null
nullable: true
next:
type: string
example: '@{{$baseUrl}}/api/v1/transactions?page=2'
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 5
links:
type: array
example:
-
url: null
label: '« Previous'
active: false
-
url: '@{{$baseUrl}}/api/v1/transactions?page=1'
label: '1'
active: true
-
url: '@{{$baseUrl}}/api/v1/transactions?page=2'
label: '2'
active: false
items:
type: object
properties:
url:
type: string
example: null
nullable: true
label:
type: string
example: '« Previous'
active:
type: boolean
example: false
path:
type: string
example: '@{{$baseUrl}}/api/v1/transactions'
per_page:
type: integer
example: 15
to:
type: integer
example: 15
total:
type: integer
example: 68
-
description: 'No transactions found'
type: object
example:
data: []
links:
first: '@{{$baseUrl}}/api/v1/transactions?page=1'
last: '@{{$baseUrl}}/api/v1/transactions?page=1'
prev: null
next: null
meta:
current_page: 1
from: null
last_page: 1
links:
-
url: null
label: '« Previous'
active: false
-
url: '@{{$baseUrl}}/api/v1/transactions?page=1'
label: '1'
active: true
path: '@{{$baseUrl}}/api/v1/transactions'
per_page: 15
to: null
total: 0
properties:
data:
type: array
example: []
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/api/v1/transactions?page=1'
last:
type: string
example: '@{{$baseUrl}}/api/v1/transactions?page=1'
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: string
example: null
nullable: true
last_page:
type: integer
example: 1
links:
type: array
example:
-
url: null
label: '« Previous'
active: false
-
url: '@{{$baseUrl}}/api/v1/transactions?page=1'
label: '1'
active: true
items:
type: object
properties:
url:
type: string
example: null
nullable: true
label:
type: string
example: '« Previous'
active:
type: boolean
example: false
path:
type: string
example: '@{{$baseUrl}}/api/v1/transactions'
per_page:
type: integer
example: 15
to:
type: string
example: null
nullable: true
total:
type: integer
example: 0
tags:
- Transactions
post:
summary: 'Create Transaction'
operationId: createTransaction
description: "Creates a new transaction record for tracking customer points/rewards. If no existing customer is found,\na new customer will be automatically created with the provided phone number.\nKey features:\n- Points are multiplied by the customer's tier multiplier if applicable\n- Points may expire based on organisation settings\n- Handles referral tracking and rewards\n- Sends notifications if enabled in organisation settings\n- Can mark linked customer rewards as used during transaction"
parameters: []
responses:
201:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
amount: 1000
valid_amount: 1000
operation: +
type: Purchase
remarks: 'Purchase at Store #123'
customer_id: 1
space_id: 1
expired_at: '2024-02-10T23:59:59.000000Z'
created_at: '2024-02-09T10:00:00.000000Z'
updated_at: '2024-02-09T10:00:00.000000Z'
customer_rewards:
-
id: 1
customer_id: 1
reward_id: 5
status: Used
code: REWARD123
expired_at: '2024-12-31T23:59:59.000000Z'
used_at: '2024-02-09T10:00:00.000000Z'
-
id: 2
customer_id: 1
reward_id: 6
status: Used
code: REWARD456
expired_at: '2024-12-31T23:59:59.000000Z'
used_at: '2024-02-09T10:00:00.000000Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
amount:
type: integer
example: 1000
valid_amount:
type: integer
example: 1000
operation:
type: string
example: +
type:
type: string
example: Purchase
remarks:
type: string
example: 'Purchase at Store #123'
customer_id:
type: integer
example: 1
space_id:
type: integer
example: 1
expired_at:
type: string
example: '2024-02-10T23:59:59.000000Z'
created_at:
type: string
example: '2024-02-09T10:00:00.000000Z'
updated_at:
type: string
example: '2024-02-09T10:00:00.000000Z'
customer_rewards:
type: array
example:
-
id: 1
customer_id: 1
reward_id: 5
status: Used
code: REWARD123
expired_at: '2024-12-31T23:59:59.000000Z'
used_at: '2024-02-09T10:00:00.000000Z'
-
id: 2
customer_id: 1
reward_id: 6
status: Used
code: REWARD456
expired_at: '2024-12-31T23:59:59.000000Z'
used_at: '2024-02-09T10:00:00.000000Z'
items:
type: object
properties:
id:
type: integer
example: 1
customer_id:
type: integer
example: 1
reward_id:
type: integer
example: 5
status:
type: string
example: Used
code:
type: string
example: REWARD123
expired_at:
type: string
example: '2024-12-31T23:59:59.000000Z'
used_at:
type: string
example: '2024-02-09T10:00:00.000000Z'
422:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Validation Error'
type: object
example:
message: 'The given data was invalid.'
errors:
customer_id:
- 'The customer id field is required when phone number is not present.'
phone_number:
- 'The phone number field is required when customer id is not present.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
customer_id:
type: array
example:
- 'The customer id field is required when phone number is not present.'
items:
type: string
phone_number:
type: array
example:
- 'The phone number field is required when customer id is not present.'
items:
type: string
-
description: 'Invalid Customer Rewards'
type: object
example:
message: 'The given data was invalid.'
errors:
customer_reward_ids.0:
- 'One or more of the selected customer rewards are invalid, used, or expired.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
customer_reward_ids.0:
type: array
example:
- 'One or more of the selected customer rewards are invalid, used, or expired.'
items:
type: string
tags:
- Transactions
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
customer_id:
type: integer
description: "Customer's ID. Required if phone_number not provided."
example: 1
phone_number:
type: string
description: "Customer's phone number. Required if customer_id not provided."
example: '+60123456789'
amount:
type: integer
description: 'Transaction amount/points.'
example: 1000
space_id:
type: integer
description: 'optional Space ID where transaction occurred.'
example: 1
remarks:
type: string
description: 'optional Additional remarks.'
example: 'Purchase at Store #123'
custom_properties:
type: object
description: 'optional Custom fields for the transaction.'
example: []
properties: { }
customer_reward_ids:
type: array
description: 'optional An array of customer reward IDs to be marked as used in this transaction. Only pending rewards that have not expired can be used.'
example:
- 1
- 2
- 3
items:
type: string
required:
- amount
'/api/transactions/{id}':
get:
summary: 'Get Transaction Details'
operationId: getTransactionDetails
description: 'Retrieve detailed information about a specific transaction.'
parameters:
-
in: query
name: include
description: 'Relationships to include (customer, reward, space, customerRewards).'
example: 'customer,reward,customerRewards'
required: false
schema:
type: string
description: 'Relationships to include (customer, reward, space, customerRewards).'
example: 'customer,reward,customerRewards'
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
amount: 1000
valid_amount: 1000
operation: +
type: Purchase
remarks: 'Purchase at Store #123'
customer_id: 1
space_id: 1
reward_id: null
expired_at: '2024-12-31T23:59:59.000000Z'
created_at: '2024-02-09T10:00:00.000000Z'
updated_at: '2024-02-09T10:00:00.000000Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
amount:
type: integer
example: 1000
valid_amount:
type: integer
example: 1000
operation:
type: string
example: +
type:
type: string
example: Purchase
remarks:
type: string
example: 'Purchase at Store #123'
customer_id:
type: integer
example: 1
space_id:
type: integer
example: 1
reward_id:
type: string
example: null
nullable: true
expired_at:
type: string
example: '2024-12-31T23:59:59.000000Z'
created_at:
type: string
example: '2024-02-09T10:00:00.000000Z'
updated_at:
type: string
example: '2024-02-09T10:00:00.000000Z'
404:
description: 'Not found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found.'
properties:
message:
type: string
example: 'Record not found.'
tags:
- Transactions
parameters:
-
in: path
name: id
description: 'The ID of the transaction.'
example: 1
required: true
schema:
type: integer
-
in: path
name: transaction
description: 'The ID of the transaction.'
example: 1
required: true
schema:
type: integer
/api/users:
get:
summary: 'List Users'
operationId: listUsers
description: "Returns a paginated list of users belonging to the authenticated user's organisation."
parameters:
-
in: query
name: 'filter[name]'
description: 'Filter by user name (partial match).'
example: Alice
required: false
schema:
type: string
description: 'Filter by user name (partial match).'
example: Alice
-
in: query
name: 'filter[email]'
description: 'Filter by email (partial match).'
example: alice@example.com
required: false
schema:
type: string
description: 'Filter by email (partial match).'
example: alice@example.com
-
in: query
name: sort
description: 'Sort field. Allowed: id, name, created_at.'
example: '-id'
required: false
schema:
type: string
description: 'Sort field. Allowed: id, name, created_at.'
example: '-id'
-
in: query
name: per_page
description: 'Records per page.'
example: 15
required: false
schema:
type: integer
description: 'Records per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
name: 'Alice Tan'
email: alice@acme.com
organisation_id: 1
roles:
- 'Super Admin'
last_login_at: '2024-06-01T08:00:00.000000Z'
created_at: '2024-01-01T00:00:00.000000Z'
updated_at: '2024-01-01T00:00:00.000000Z'
links:
first: ...
last: ...
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
per_page: 15
to: 1
total: 1
properties:
data:
type: array
example:
-
id: 1
name: 'Alice Tan'
email: alice@acme.com
organisation_id: 1
roles:
- 'Super Admin'
last_login_at: '2024-06-01T08:00:00.000000Z'
created_at: '2024-01-01T00:00:00.000000Z'
updated_at: '2024-01-01T00:00:00.000000Z'
items:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Alice Tan'
email:
type: string
example: alice@acme.com
organisation_id:
type: integer
example: 1
roles:
type: array
example:
- 'Super Admin'
items:
type: string
last_login_at:
type: string
example: '2024-06-01T08:00:00.000000Z'
created_at:
type: string
example: '2024-01-01T00:00:00.000000Z'
updated_at:
type: string
example: '2024-01-01T00:00:00.000000Z'
links:
type: object
properties:
first:
type: string
example: ...
last:
type: string
example: ...
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
per_page:
type: integer
example: 15
to:
type: integer
example: 1
total:
type: integer
example: 1
tags:
- Users
'/api/users/{id}':
get:
summary: 'Show User'
operationId: showUser
description: ''
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
name: Alice
email: alice@example.com
properties:
data:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: Alice
email:
type: string
example: alice@example.com
403:
description: Forbidden
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
tags:
- Users
parameters:
-
in: path
name: id
description: 'The user ID.'
example: 1
required: true
schema:
type: integer
/api/calendars:
get:
summary: 'List Calendars'
operationId: listCalendars
description: "Returns a paginated list of calendars belonging to the authenticated user's organisation spaces.
\nSupports filtering, sorting and relationship inclusion through query parameters."
parameters:
-
in: query
name: include
description: "Comma-separated list of relationships to include. Available relationships:\n- space\n- reservations"
example: 'space,reservations'
required: false
schema:
type: string
description: "Comma-separated list of relationships to include. Available relationships:\n- space\n- reservations"
example: 'space,reservations'
-
in: query
name: 'filter[space_id]'
description: 'Filter by space ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by space ID.'
example: 1
-
in: query
name: 'filter[is_active]'
description: 'Filter by active status.'
example: true
required: false
schema:
type: boolean
description: 'Filter by active status.'
example: true
-
in: query
name: 'filter[visibility]'
description: 'Filter by visibility. Must be one of: Public, Private.'
example: Public
required: false
schema:
type: string
description: 'Filter by visibility. Must be one of: Public, Private.'
example: Public
-
in: query
name: 'filter[name]'
description: 'Filter by calendar name (partial match).'
example: 'Meeting Room'
required: false
schema:
type: string
description: 'Filter by calendar name (partial match).'
example: 'Meeting Room'
-
in: query
name: sort
description: "Sort field and direction. Note: Prefix with `-` for descending order.\nAvailable sort fields:\n- name\n- created_at\n- updated_at"
example: '-created_at'
required: false
schema:
type: string
description: "Sort field and direction. Note: Prefix with `-` for descending order.\nAvailable sort fields:\n- name\n- created_at\n- updated_at"
example: '-created_at'
-
in: query
name: per_page
description: 'Number of records per page. Defaults to 15.'
example: 15
required: false
schema:
type: integer
description: 'Number of records per page. Defaults to 15.'
example: 15
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
name: 'Meeting Room A'
slug: meeting-room-a
description: 'Large meeting room for up to 20 people'
is_active: true
space_id: 1
visibility: Public
min_capacity: 1
max_capacity: 20
is_required_approval: false
is_auto_approval: true
cancel_before_minutes: 60
book_before_minutes: 30
reminder_minutes: 15
crm_auto_import: true
is_unavailable_timeslots_visible: false
is_qr_code_visible: true
default_timeslots:
-
start_time: '09:00'
end_time: '10:00'
days:
- monday
- tuesday
- wednesday
- thursday
- friday
block_dates: []
notification_settings:
new_reservation:
enabled: true
message: 'Your reservation has been confirmed'
custom_properties: { }
channels:
- WhatsApp
- Sms
terms: 'Please arrive 5 minutes before your scheduled time'
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
links:
first: '@{{$baseUrl}}/api/calendars?page=1'
last: '@{{$baseUrl}}/api/calendars?page=1'
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
path: '@{{$baseUrl}}/api/calendars'
per_page: 15
to: 1
total: 1
properties:
data:
type: array
example:
-
id: 1
name: 'Meeting Room A'
slug: meeting-room-a
description: 'Large meeting room for up to 20 people'
is_active: true
space_id: 1
visibility: Public
min_capacity: 1
max_capacity: 20
is_required_approval: false
is_auto_approval: true
cancel_before_minutes: 60
book_before_minutes: 30
reminder_minutes: 15
crm_auto_import: true
is_unavailable_timeslots_visible: false
is_qr_code_visible: true
default_timeslots:
-
start_time: '09:00'
end_time: '10:00'
days:
- monday
- tuesday
- wednesday
- thursday
- friday
block_dates: []
notification_settings:
new_reservation:
enabled: true
message: 'Your reservation has been confirmed'
custom_properties: []
channels:
- WhatsApp
- Sms
terms: 'Please arrive 5 minutes before your scheduled time'
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
items:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Meeting Room A'
slug:
type: string
example: meeting-room-a
description:
type: string
example: 'Large meeting room for up to 20 people'
is_active:
type: boolean
example: true
space_id:
type: integer
example: 1
visibility:
type: string
example: Public
min_capacity:
type: integer
example: 1
max_capacity:
type: integer
example: 20
is_required_approval:
type: boolean
example: false
is_auto_approval:
type: boolean
example: true
cancel_before_minutes:
type: integer
example: 60
book_before_minutes:
type: integer
example: 30
reminder_minutes:
type: integer
example: 15
crm_auto_import:
type: boolean
example: true
is_unavailable_timeslots_visible:
type: boolean
example: false
is_qr_code_visible:
type: boolean
example: true
default_timeslots:
type: array
example:
-
start_time: '09:00'
end_time: '10:00'
days:
- monday
- tuesday
- wednesday
- thursday
- friday
items:
type: object
properties:
start_time:
type: string
example: '09:00'
end_time:
type: string
example: '10:00'
days:
type: array
example:
- monday
- tuesday
- wednesday
- thursday
- friday
items:
type: string
block_dates:
type: array
example: []
notification_settings:
type: object
properties:
new_reservation:
type: object
properties:
enabled:
type: boolean
example: true
message:
type: string
example: 'Your reservation has been confirmed'
custom_properties:
type: object
properties: { }
channels:
type: array
example:
- WhatsApp
- Sms
items:
type: string
terms:
type: string
example: 'Please arrive 5 minutes before your scheduled time'
created_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
updated_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/api/calendars?page=1'
last:
type: string
example: '@{{$baseUrl}}/api/calendars?page=1'
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
path:
type: string
example: '@{{$baseUrl}}/api/calendars'
per_page:
type: integer
example: 15
to:
type: integer
example: 1
total:
type: integer
example: 1
tags:
- Calendars
post:
summary: 'Create Calendar'
operationId: createCalendar
description: "Creates a new calendar in the system.
\nThe calendar will be associated with the specified space in the authenticated user's organisation."
parameters: []
responses:
201:
description: 'Created successfully'
content:
application/json:
schema:
type: object
example:
data:
id: 1
name: 'Meeting Room A'
slug: meeting-room-a
description: 'Large meeting room for up to 20 people'
is_active: true
space_id: 1
visibility: Public
min_capacity: 1
max_capacity: 20
is_required_approval: false
is_auto_approval: true
cancel_before_minutes: 60
book_before_minutes: 30
reminder_minutes: 15
crm_auto_import: true
is_unavailable_timeslots_visible: false
is_qr_code_visible: true
default_timeslots:
-
start_time: '09:00'
end_time: '10:00'
days:
- monday
- tuesday
- wednesday
- thursday
- friday
block_dates: []
notification_settings:
new_reservation:
enabled: true
message: 'Your reservation has been confirmed'
custom_properties: { }
channels:
- WhatsApp
- Sms
terms: 'Please arrive 5 minutes before your scheduled time'
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
message: 'Calendar created successfully'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Meeting Room A'
slug:
type: string
example: meeting-room-a
description:
type: string
example: 'Large meeting room for up to 20 people'
is_active:
type: boolean
example: true
space_id:
type: integer
example: 1
visibility:
type: string
example: Public
min_capacity:
type: integer
example: 1
max_capacity:
type: integer
example: 20
is_required_approval:
type: boolean
example: false
is_auto_approval:
type: boolean
example: true
cancel_before_minutes:
type: integer
example: 60
book_before_minutes:
type: integer
example: 30
reminder_minutes:
type: integer
example: 15
crm_auto_import:
type: boolean
example: true
is_unavailable_timeslots_visible:
type: boolean
example: false
is_qr_code_visible:
type: boolean
example: true
default_timeslots:
type: array
example:
-
start_time: '09:00'
end_time: '10:00'
days:
- monday
- tuesday
- wednesday
- thursday
- friday
items:
type: object
properties:
start_time:
type: string
example: '09:00'
end_time:
type: string
example: '10:00'
days:
type: array
example:
- monday
- tuesday
- wednesday
- thursday
- friday
items:
type: string
block_dates:
type: array
example: []
notification_settings:
type: object
properties:
new_reservation:
type: object
properties:
enabled:
type: boolean
example: true
message:
type: string
example: 'Your reservation has been confirmed'
custom_properties:
type: object
properties: { }
channels:
type: array
example:
- WhatsApp
- Sms
items:
type: string
terms:
type: string
example: 'Please arrive 5 minutes before your scheduled time'
created_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
updated_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
message:
type: string
example: 'Calendar created successfully'
422:
description: 'Validation Error'
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
name:
- 'The name field is required.'
space_id:
- 'The selected space id is invalid.'
max_capacity:
- 'The max capacity must be greater than min capacity.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
name:
type: array
example:
- 'The name field is required.'
items:
type: string
space_id:
type: array
example:
- 'The selected space id is invalid.'
items:
type: string
max_capacity:
type: array
example:
- 'The max capacity must be greater than min capacity.'
items:
type: string
tags:
- Calendars
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: "The calendar's name."
example: 'Meeting Room A'
slug:
type: string
description: 'The URL-friendly slug. Will be auto-generated if not provided.'
example: meeting-room-a
nullable: true
description:
type: string
description: 'Description of the calendar.'
example: 'Large meeting room for up to 20 people'
nullable: true
space_id:
type: integer
description: "The ID of the space this calendar belongs to. Must be from user's organisation."
example: 1
is_active:
type: boolean
description: 'Whether the calendar is active and accepting reservations. Default: true.'
example: true
visibility:
type: string
description: 'The visibility of the calendar. Must be one of: Public, Private. Default: Public.'
example: Public
min_capacity:
type: integer
description: 'Minimum capacity for reservations.'
example: 1
nullable: true
max_capacity:
type: integer
description: 'Maximum capacity for reservations.'
example: 20
nullable: true
is_required_approval:
type: boolean
description: 'Whether reservations require approval. Default: false.'
example: false
is_auto_approval:
type: boolean
description: 'Whether approved reservations are automatically confirmed. Default: true.'
example: true
cancel_before_minutes:
type: integer
description: 'Minutes before reservation start when cancellation is allowed.'
example: 60
nullable: true
book_before_minutes:
type: integer
description: 'Minutes before reservation start when booking is allowed.'
example: 30
nullable: true
is_max_advance_booking_enabled:
type: boolean
description: 'Whether to enable a maximum advance booking limit for this calendar.'
example: false
max_advance_booking_days:
type: integer
description: 'Maximum number of days in advance a customer can make a booking. Required when is_max_advance_booking_enabled is true. This field is required when is_max_advance_booking_enabled is true. Must be at least 1. Must not be greater than 365.'
example: 30
nullable: true
reminder_minutes:
type: integer
description: 'Minutes before reservation to send reminders.'
example: 15
nullable: true
crm_auto_import:
type: boolean
description: 'Whether to auto-import customers from reservations. Default: true.'
example: true
is_unavailable_timeslots_visible:
type: boolean
description: 'Whether to show unavailable timeslots to customers. Default: false.'
example: false
is_qr_code_visible:
type: boolean
description: 'Whether to display QR codes for reservations. Default: true.'
example: true
default_timeslots:
type: array
description: 'Default available time slots for the calendar.'
example:
- eos
items:
type: string
nullable: true
block_dates:
type: array
description: 'Array of blocked dates and time slots.'
example:
- autem
items:
type: string
nullable: true
notification_settings:
type: object
description: 'Notification settings for calendar events.'
example: []
properties:
new_reservation:
type: object
description: 'Settings for new reservation notifications.'
example: []
properties:
enabled:
type: boolean
description: 'Whether to send new reservation notifications.'
example: true
message:
type: string
description: 'Template message for new reservation notifications.'
example: 'Your reservation has been confirmed'
nullable: true
custom_properties:
type: object
description: 'Custom properties for the calendar.'
example: []
properties: { }
nullable: true
channels:
type: array
description: 'Communication channels for notifications.'
example:
- WhatsApp
- Sms
items:
type: string
terms:
type: string
description: 'Terms and conditions for using the calendar.'
example: 'Please arrive 5 minutes before your scheduled time'
nullable: true
required:
- name
- space_id
'/api/calendars/{id}':
get:
summary: 'Show Calendar Details'
operationId: showCalendarDetails
description: 'Retrieves detailed information about a specific calendar, including any requested relationships.'
parameters:
-
in: query
name: include
description: "Comma-separated list of relationships to include. Available relationships:\n- space\n- reservations"
example: 'space,reservations'
required: false
schema:
type: string
description: "Comma-separated list of relationships to include. Available relationships:\n- space\n- reservations"
example: 'space,reservations'
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
name: 'Meeting Room A'
slug: meeting-room-a
description: 'Large meeting room for up to 20 people'
is_active: true
space_id: 1
visibility: Public
min_capacity: 1
max_capacity: 20
is_required_approval: false
is_auto_approval: true
cancel_before_minutes: 60
book_before_minutes: 30
reminder_minutes: 15
crm_auto_import: true
is_unavailable_timeslots_visible: false
is_qr_code_visible: true
default_timeslots:
-
start_time: '09:00'
end_time: '10:00'
days:
- monday
- tuesday
- wednesday
- thursday
- friday
block_dates: []
notification_settings:
new_reservation:
enabled: true
message: 'Your reservation has been confirmed'
custom_properties: { }
channels:
- WhatsApp
- Sms
terms: 'Please arrive 5 minutes before your scheduled time'
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Meeting Room A'
slug:
type: string
example: meeting-room-a
description:
type: string
example: 'Large meeting room for up to 20 people'
is_active:
type: boolean
example: true
space_id:
type: integer
example: 1
visibility:
type: string
example: Public
min_capacity:
type: integer
example: 1
max_capacity:
type: integer
example: 20
is_required_approval:
type: boolean
example: false
is_auto_approval:
type: boolean
example: true
cancel_before_minutes:
type: integer
example: 60
book_before_minutes:
type: integer
example: 30
reminder_minutes:
type: integer
example: 15
crm_auto_import:
type: boolean
example: true
is_unavailable_timeslots_visible:
type: boolean
example: false
is_qr_code_visible:
type: boolean
example: true
default_timeslots:
type: array
example:
-
start_time: '09:00'
end_time: '10:00'
days:
- monday
- tuesday
- wednesday
- thursday
- friday
items:
type: object
properties:
start_time:
type: string
example: '09:00'
end_time:
type: string
example: '10:00'
days:
type: array
example:
- monday
- tuesday
- wednesday
- thursday
- friday
items:
type: string
block_dates:
type: array
example: []
notification_settings:
type: object
properties:
new_reservation:
type: object
properties:
enabled:
type: boolean
example: true
message:
type: string
example: 'Your reservation has been confirmed'
custom_properties:
type: object
properties: { }
channels:
type: array
example:
- WhatsApp
- Sms
items:
type: string
terms:
type: string
example: 'Please arrive 5 minutes before your scheduled time'
created_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
updated_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
tags:
- Calendars
put:
summary: 'Update Calendar'
operationId: updateCalendar
description: "Updates an existing calendar's information.
\nAll fields are optional."
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
name: 'Meeting Room A'
slug: meeting-room-a
description: 'Large meeting room for up to 20 people'
is_active: true
space_id: 1
visibility: Public
min_capacity: 1
max_capacity: 20
is_required_approval: false
is_auto_approval: true
cancel_before_minutes: 60
book_before_minutes: 30
reminder_minutes: 15
crm_auto_import: true
is_unavailable_timeslots_visible: false
is_qr_code_visible: true
default_timeslots:
-
start_time: '09:00'
end_time: '10:00'
days:
- monday
- tuesday
- wednesday
- thursday
- friday
block_dates: []
notification_settings:
new_reservation:
enabled: true
message: 'Your reservation has been confirmed'
custom_properties: { }
channels:
- WhatsApp
- Sms
terms: 'Please arrive 5 minutes before your scheduled time'
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
message: 'Calendar updated successfully'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Meeting Room A'
slug:
type: string
example: meeting-room-a
description:
type: string
example: 'Large meeting room for up to 20 people'
is_active:
type: boolean
example: true
space_id:
type: integer
example: 1
visibility:
type: string
example: Public
min_capacity:
type: integer
example: 1
max_capacity:
type: integer
example: 20
is_required_approval:
type: boolean
example: false
is_auto_approval:
type: boolean
example: true
cancel_before_minutes:
type: integer
example: 60
book_before_minutes:
type: integer
example: 30
reminder_minutes:
type: integer
example: 15
crm_auto_import:
type: boolean
example: true
is_unavailable_timeslots_visible:
type: boolean
example: false
is_qr_code_visible:
type: boolean
example: true
default_timeslots:
type: array
example:
-
start_time: '09:00'
end_time: '10:00'
days:
- monday
- tuesday
- wednesday
- thursday
- friday
items:
type: object
properties:
start_time:
type: string
example: '09:00'
end_time:
type: string
example: '10:00'
days:
type: array
example:
- monday
- tuesday
- wednesday
- thursday
- friday
items:
type: string
block_dates:
type: array
example: []
notification_settings:
type: object
properties:
new_reservation:
type: object
properties:
enabled:
type: boolean
example: true
message:
type: string
example: 'Your reservation has been confirmed'
custom_properties:
type: object
properties: { }
channels:
type: array
example:
- WhatsApp
- Sms
items:
type: string
terms:
type: string
example: 'Please arrive 5 minutes before your scheduled time'
created_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
updated_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
message:
type: string
example: 'Calendar updated successfully'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
422:
description: 'Validation Error'
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
name:
- 'The name field is required.'
space_id:
- 'The selected space id is invalid.'
max_capacity:
- 'The max capacity must be greater than min capacity.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
name:
type: array
example:
- 'The name field is required.'
items:
type: string
space_id:
type: array
example:
- 'The selected space id is invalid.'
items:
type: string
max_capacity:
type: array
example:
- 'The max capacity must be greater than min capacity.'
items:
type: string
tags:
- Calendars
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: "The calendar's name."
example: 'Meeting Room A'
slug:
type: string
description: 'The URL-friendly slug.'
example: meeting-room-a
nullable: true
description:
type: string
description: 'Description of the calendar.'
example: 'Large meeting room for up to 20 people'
nullable: true
space_id:
type: integer
description: "The ID of the space this calendar belongs to. Must be from user's organisation."
example: 1
is_active:
type: boolean
description: 'Whether the calendar is active and accepting reservations.'
example: true
visibility:
type: string
description: 'The visibility of the calendar. Must be one of: Public, Private.'
example: Public
min_capacity:
type: integer
description: 'Minimum capacity for reservations.'
example: 1
nullable: true
max_capacity:
type: integer
description: 'Maximum capacity for reservations.'
example: 20
nullable: true
is_required_approval:
type: boolean
description: 'Whether reservations require approval.'
example: false
is_auto_approval:
type: boolean
description: 'Whether approved reservations are automatically confirmed.'
example: true
cancel_before_minutes:
type: integer
description: 'Minutes before reservation start when cancellation is allowed.'
example: 60
nullable: true
book_before_minutes:
type: integer
description: 'Minutes before reservation start when booking is allowed.'
example: 30
nullable: true
is_max_advance_booking_enabled:
type: boolean
description: 'Whether to enable a maximum advance booking limit for this calendar.'
example: false
max_advance_booking_days:
type: integer
description: 'Maximum number of days in advance a customer can make a booking. Required when is_max_advance_booking_enabled is true. This field is required when is_max_advance_booking_enabled is true. Must be at least 1. Must not be greater than 365.'
example: 30
nullable: true
reminder_minutes:
type: integer
description: 'Minutes before reservation to send reminders.'
example: 15
nullable: true
crm_auto_import:
type: boolean
description: 'Whether to auto-import customers from reservations.'
example: true
is_unavailable_timeslots_visible:
type: boolean
description: 'Whether to show unavailable timeslots to customers.'
example: false
is_qr_code_visible:
type: boolean
description: 'Whether to display QR codes for reservations.'
example: true
default_timeslots:
type: array
description: 'Default available time slots for the calendar.'
example:
- et
items:
type: string
nullable: true
block_dates:
type: array
description: 'Array of blocked dates and time slots.'
example:
- deleniti
items:
type: string
nullable: true
notification_settings:
type: object
description: 'Notification settings for calendar events.'
example: []
properties:
new_reservation:
type: object
description: 'Settings for notifications sent when a new reservation is created.'
example:
enabled: true
message: 'Your reservation has been confirmed.'
properties:
enabled:
type: boolean
description: 'Whether to send notifications for new reservations.'
example: true
message:
type: string
description: 'Template message for new reservation notifications. Available variables: @{{CUSTOMER_NAME}}, @{{CALENDAR_NAME}}, @{{RESERVATION_DATE}}, @{{RESERVATION_TIME}}.'
example: 'Your reservation for @{{CALENDAR_NAME}} has been confirmed for @{{RESERVATION_DATE}} at @{{RESERVATION_TIME}}.'
nullable: true
custom_properties:
type: object
description: 'Custom properties for the calendar.'
example: []
properties: { }
nullable: true
channels:
type: array
description: 'Communication channels for notifications.'
example:
- WhatsApp
- Sms
items:
type: string
terms:
type: string
description: 'Terms and conditions for using the calendar.'
example: 'Please arrive 5 minutes before your scheduled time'
nullable: true
delete:
summary: 'Delete Calendar'
operationId: deleteCalendar
description: "Deletes a calendar from the system.
\nOnly calendars without active reservations can be deleted."
parameters: []
responses:
204:
description: 'No Content'
content:
application/json:
schema:
type: object
example: { }
properties: { }
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
422:
description: 'Cannot Delete'
content:
application/json:
schema:
type: object
example:
message: 'Cannot delete calendar with active reservations'
properties:
message:
type: string
example: 'Cannot delete calendar with active reservations'
tags:
- Calendars
parameters:
-
in: path
name: id
description: 'The ID of the calendar.'
example: 1
required: true
schema:
type: integer
/api/reservations:
get:
summary: 'List Reservations'
operationId: listReservations
description: "Returns a paginated list of reservations belonging to the authenticated user's organisation.
\nResults can be filtered, sorted and include related data through query parameters."
parameters:
-
in: query
name: include
description: "Comma-separated list of relationships to include:\n- calendar\n- customer\n- calendar.space"
example: 'calendar,customer'
required: false
schema:
type: string
description: "Comma-separated list of relationships to include:\n- calendar\n- customer\n- calendar.space"
example: 'calendar,customer'
-
in: query
name: 'filter[calendar_id]'
description: 'Filter by calendar ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by calendar ID.'
example: 1
-
in: query
name: 'filter[customer_id]'
description: 'Filter by customer ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by customer ID.'
example: 1
-
in: query
name: 'filter[status]'
description: "Filter by reservation status. Must be one of:\n- pending\n- rejected\n- reserved\n- cancelled\n- no_show"
example: reserved
required: false
schema:
type: string
description: "Filter by reservation status. Must be one of:\n- pending\n- rejected\n- reserved\n- cancelled\n- no_show"
example: reserved
-
in: query
name: 'filter[date]'
description: 'Filter by reservation date. Use ISO 8601 format (YYYY-MM-DD).'
example: '2024-01-01'
required: false
schema:
type: string
description: 'Filter by reservation date. Use ISO 8601 format (YYYY-MM-DD).'
example: '2024-01-01'
-
in: query
name: 'filter[date_from]'
description: 'Filter reservations from this date onwards (inclusive). Use ISO 8601 format (YYYY-MM-DD).'
example: '2024-01-01'
required: false
schema:
type: string
description: 'Filter reservations from this date onwards (inclusive). Use ISO 8601 format (YYYY-MM-DD).'
example: '2024-01-01'
-
in: query
name: 'filter[date_to]'
description: 'Filter reservations up to this date (inclusive). Use ISO 8601 format (YYYY-MM-DD).'
example: '2024-01-31'
required: false
schema:
type: string
description: 'Filter reservations up to this date (inclusive). Use ISO 8601 format (YYYY-MM-DD).'
example: '2024-01-31'
-
in: query
name: 'filter[name]'
description: 'Filter by customer name (partial match).'
example: John
required: false
schema:
type: string
description: 'Filter by customer name (partial match).'
example: John
-
in: query
name: 'filter[email]'
description: 'Filter by customer email (partial match).'
example: john@example.com
required: false
schema:
type: string
description: 'Filter by customer email (partial match).'
example: john@example.com
-
in: query
name: 'filter[phone_number]'
description: 'Filter by customer phone number (partial match).'
example: '+60123'
required: false
schema:
type: string
description: 'Filter by customer phone number (partial match).'
example: '+60123'
-
in: query
name: sort
description: 'Sort field and direction. Allowed fields: date, start_at, created_at, status, name.'
example: '-date'
required: false
schema:
type: string
description: 'Sort field and direction. Allowed fields: date, start_at, created_at, status, name.'
example: '-date'
-
in: query
name: per_page
description: 'Number of records per page.'
example: 15
required: false
schema:
type: integer
description: 'Number of records per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
ref_id: RSV001234
date: '2024-03-15'
start_at: '2024-03-15T14:30:00.000000Z'
end_at: '2024-03-15T16:00:00.000000Z'
name: 'John Smith'
email: john.smith@example.com
phone_number: '+60123456789'
number_of_guests: 4
status: reserved
remarks: 'Birthday celebration'
custom_fields:
dietary_requirements: Vegetarian
special_requests: 'Window seat preferred'
timeslot_id: 5
calendar_id: 1
customer_id: 123
reminder_sent_at: '2024-03-14T14:30:00.000000Z'
rejection_reason: null
created_at: '2024-03-01T10:15:30.000000Z'
updated_at: '2024-03-10T16:22:45.000000Z'
calendar:
id: 1
name: 'Restaurant Reservations'
slug: restaurant-reservations
description: 'Main dining room reservations'
is_active: true
space_id: 1
visibility: public
min_capacity: 1
max_capacity: 8
is_required_approval: true
is_auto_approval: false
cancel_before_minutes: 60
book_before_minutes: 30
reminder_minutes: 1440
crm_auto_import: true
is_unavailable_timeslots_visible: false
is_qr_code_visible: true
space:
id: 1
name: 'Main Branch'
slug: main-branch
description: 'Our flagship restaurant location'
customer:
id: 123
name: 'John Smith'
email: john.smith@example.com
phone_number: '+60123456789'
current_point: 2500
tier_id: 2
links:
first: '@{{$baseUrl}}/reservations?page=1'
last: '@{{$baseUrl}}/reservations?page=5'
prev: null
next: '@{{$baseUrl}}/reservations?page=2'
meta:
current_page: 1
from: 1
last_page: 5
links:
-
url: null
label: '« Previous'
active: false
-
url: '@{{$baseUrl}}/reservations?page=1'
label: '1'
active: true
-
url: '@{{$baseUrl}}/reservations?page=2'
label: 'Next »'
active: false
path: '@{{$baseUrl}}/reservations'
per_page: 15
to: 15
total: 68
properties:
data:
type: array
example:
-
id: 1
ref_id: RSV001234
date: '2024-03-15'
start_at: '2024-03-15T14:30:00.000000Z'
end_at: '2024-03-15T16:00:00.000000Z'
name: 'John Smith'
email: john.smith@example.com
phone_number: '+60123456789'
number_of_guests: 4
status: reserved
remarks: 'Birthday celebration'
custom_fields:
dietary_requirements: Vegetarian
special_requests: 'Window seat preferred'
timeslot_id: 5
calendar_id: 1
customer_id: 123
reminder_sent_at: '2024-03-14T14:30:00.000000Z'
rejection_reason: null
created_at: '2024-03-01T10:15:30.000000Z'
updated_at: '2024-03-10T16:22:45.000000Z'
calendar:
id: 1
name: 'Restaurant Reservations'
slug: restaurant-reservations
description: 'Main dining room reservations'
is_active: true
space_id: 1
visibility: public
min_capacity: 1
max_capacity: 8
is_required_approval: true
is_auto_approval: false
cancel_before_minutes: 60
book_before_minutes: 30
reminder_minutes: 1440
crm_auto_import: true
is_unavailable_timeslots_visible: false
is_qr_code_visible: true
space:
id: 1
name: 'Main Branch'
slug: main-branch
description: 'Our flagship restaurant location'
customer:
id: 123
name: 'John Smith'
email: john.smith@example.com
phone_number: '+60123456789'
current_point: 2500
tier_id: 2
items:
type: object
properties:
id:
type: integer
example: 1
ref_id:
type: string
example: RSV001234
date:
type: string
example: '2024-03-15'
start_at:
type: string
example: '2024-03-15T14:30:00.000000Z'
end_at:
type: string
example: '2024-03-15T16:00:00.000000Z'
name:
type: string
example: 'John Smith'
email:
type: string
example: john.smith@example.com
phone_number:
type: string
example: '+60123456789'
number_of_guests:
type: integer
example: 4
status:
type: string
example: reserved
remarks:
type: string
example: 'Birthday celebration'
custom_fields:
type: object
properties:
dietary_requirements:
type: string
example: Vegetarian
special_requests:
type: string
example: 'Window seat preferred'
timeslot_id:
type: integer
example: 5
calendar_id:
type: integer
example: 1
customer_id:
type: integer
example: 123
reminder_sent_at:
type: string
example: '2024-03-14T14:30:00.000000Z'
rejection_reason:
type: string
example: null
nullable: true
created_at:
type: string
example: '2024-03-01T10:15:30.000000Z'
updated_at:
type: string
example: '2024-03-10T16:22:45.000000Z'
calendar:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Restaurant Reservations'
slug:
type: string
example: restaurant-reservations
description:
type: string
example: 'Main dining room reservations'
is_active:
type: boolean
example: true
space_id:
type: integer
example: 1
visibility:
type: string
example: public
min_capacity:
type: integer
example: 1
max_capacity:
type: integer
example: 8
is_required_approval:
type: boolean
example: true
is_auto_approval:
type: boolean
example: false
cancel_before_minutes:
type: integer
example: 60
book_before_minutes:
type: integer
example: 30
reminder_minutes:
type: integer
example: 1440
crm_auto_import:
type: boolean
example: true
is_unavailable_timeslots_visible:
type: boolean
example: false
is_qr_code_visible:
type: boolean
example: true
space:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Main Branch'
slug:
type: string
example: main-branch
description:
type: string
example: 'Our flagship restaurant location'
customer:
type: object
properties:
id:
type: integer
example: 123
name:
type: string
example: 'John Smith'
email:
type: string
example: john.smith@example.com
phone_number:
type: string
example: '+60123456789'
current_point:
type: integer
example: 2500
tier_id:
type: integer
example: 2
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/reservations?page=1'
last:
type: string
example: '@{{$baseUrl}}/reservations?page=5'
prev:
type: string
example: null
nullable: true
next:
type: string
example: '@{{$baseUrl}}/reservations?page=2'
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 5
links:
type: array
example:
-
url: null
label: '« Previous'
active: false
-
url: '@{{$baseUrl}}/reservations?page=1'
label: '1'
active: true
-
url: '@{{$baseUrl}}/reservations?page=2'
label: 'Next »'
active: false
items:
type: object
properties:
url:
type: string
example: null
nullable: true
label:
type: string
example: '« Previous'
active:
type: boolean
example: false
path:
type: string
example: '@{{$baseUrl}}/reservations'
per_page:
type: integer
example: 15
to:
type: integer
example: 15
total:
type: integer
example: 68
tags:
- Reservations
post:
summary: 'Create Reservation'
operationId: createReservation
description: "Creates a new reservation in the system.
\nThe reservation will be associated with a calendar and optionally with a customer.
\nAuto-generates a unique reference ID."
parameters: []
responses:
201:
description: 'Created successfully'
content:
application/json:
schema:
type: object
example:
data:
id: 1
ref_id: RSV001234
date: '2024-03-15'
start_at: '2024-03-15T14:30:00.000000Z'
end_at: '2024-03-15T16:00:00.000000Z'
name: 'John Smith'
email: john.smith@example.com
phone_number: '+60123456789'
number_of_guests: 4
status: pending
remarks: 'Birthday celebration'
custom_fields:
dietary_requirements: Vegetarian
timeslot_id: null
calendar_id: 1
customer_id: null
reminder_sent_at: null
rejection_reason: null
created_at: '2024-03-01T10:15:30.000000Z'
updated_at: '2024-03-01T10:15:30.000000Z'
message: 'Reservation created successfully'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
ref_id:
type: string
example: RSV001234
date:
type: string
example: '2024-03-15'
start_at:
type: string
example: '2024-03-15T14:30:00.000000Z'
end_at:
type: string
example: '2024-03-15T16:00:00.000000Z'
name:
type: string
example: 'John Smith'
email:
type: string
example: john.smith@example.com
phone_number:
type: string
example: '+60123456789'
number_of_guests:
type: integer
example: 4
status:
type: string
example: pending
remarks:
type: string
example: 'Birthday celebration'
custom_fields:
type: object
properties:
dietary_requirements:
type: string
example: Vegetarian
timeslot_id:
type: string
example: null
nullable: true
calendar_id:
type: integer
example: 1
customer_id:
type: string
example: null
nullable: true
reminder_sent_at:
type: string
example: null
nullable: true
rejection_reason:
type: string
example: null
nullable: true
created_at:
type: string
example: '2024-03-01T10:15:30.000000Z'
updated_at:
type: string
example: '2024-03-01T10:15:30.000000Z'
message:
type: string
example: 'Reservation created successfully'
422:
description: 'Validation failed'
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
date:
- 'The date must be a valid date after today.'
start_at:
- 'The start at field is required.'
calendar_id:
- 'The selected calendar id is invalid.'
number_of_guests:
- 'The number of guests must be at least 1.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
date:
type: array
example:
- 'The date must be a valid date after today.'
items:
type: string
start_at:
type: array
example:
- 'The start at field is required.'
items:
type: string
calendar_id:
type: array
example:
- 'The selected calendar id is invalid.'
items:
type: string
number_of_guests:
type: array
example:
- 'The number of guests must be at least 1.'
items:
type: string
tags:
- Reservations
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
date:
type: string
description: 'The date of the reservation in Y-m-d format. Must be a valid date. Must be a date after or equal to today.'
example: '2024-03-15'
start_at:
type: string
description: 'The start date and time of the reservation in ISO 8601 format. Must be a valid date. Must be a date after date.'
example: '2024-03-15T14:30:00Z'
end_at:
type: string
description: 'The end date and time of the reservation in ISO 8601 format. Must be a valid date. Must be a date after start_at.'
example: '2024-03-15T16:00:00Z'
name:
type: string
description: 'The full name of the person making the reservation. Must not be greater than 255 characters.'
example: 'John Smith'
email:
type: string
description: 'The email address of the person making the reservation. Must be a valid email address. Must not be greater than 255 characters.'
example: john.smith@example.com
nullable: true
phone_number:
type: string
description: 'The phone number of the person making the reservation. Must not be greater than 255 characters.'
example: '+60123456789'
nullable: true
number_of_guests:
type: integer
description: 'The number of guests for the reservation. Must be at least 1. Must be at least 1. Must not be greater than 999.'
example: 4
status:
type: string
description: 'The initial status of the reservation.'
example: pending
enum:
- pending
- reserved
- cancelled
- rejected
- no_show
remarks:
type: string
description: 'Additional remarks or notes about the reservation. Must not be greater than 65535 characters.'
example: 'Birthday celebration, window seat preferred'
nullable: true
custom_fields:
type: object
description: 'Custom fields as key-value pairs for additional reservation data.'
example:
dietary_requirements: Vegetarian
special_requests: 'Birthday cake arrangement'
properties: { }
nullable: true
timeslot_id:
type: integer
description: 'The ID of the associated timeslot, if applicable.'
example: 5
nullable: true
calendar_id:
type: string
description: 'The ID of the calendar this reservation belongs to. Must belong to your organisation and accessible spaces.'
example: 1
customer_id:
type: string
description: 'The ID of the associated customer, if the reservation is linked to an existing customer. The id of an existing record in the customers table.'
example: 123
nullable: true
rejection_reason:
type: string
description: 'The reason for rejection if the status is set to rejected. Must not be greater than 65535 characters.'
example: 'Fully booked on requested date'
nullable: true
required:
- date
- start_at
- end_at
- name
- number_of_guests
- status
- calendar_id
'/api/reservations/{id}':
get:
summary: 'Show Reservation Details'
operationId: showReservationDetails
description: 'Retrieves detailed information about a specific reservation, including any requested relationships.'
parameters:
-
in: query
name: include
description: "Comma-separated list of relationships to include. Available relationships:\n- calendar\n- customer\n- calendar.space"
example: 'calendar,customer'
required: false
schema:
type: string
description: "Comma-separated list of relationships to include. Available relationships:\n- calendar\n- customer\n- calendar.space"
example: 'calendar,customer'
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
ref_id: RSV001234
date: '2024-03-15'
start_at: '2024-03-15T14:30:00.000000Z'
end_at: '2024-03-15T16:00:00.000000Z'
name: 'John Smith'
email: john.smith@example.com
phone_number: '+60123456789'
number_of_guests: 4
status: reserved
remarks: 'Birthday celebration'
custom_fields:
dietary_requirements: Vegetarian
timeslot_id: 5
calendar_id: 1
customer_id: 123
reminder_sent_at: '2024-03-14T14:30:00.000000Z'
rejection_reason: null
created_at: '2024-03-01T10:15:30.000000Z'
updated_at: '2024-03-10T16:22:45.000000Z'
calendar:
id: 1
name: 'Restaurant Reservations'
space_id: 1
properties:
data:
type: object
properties:
id:
type: integer
example: 1
ref_id:
type: string
example: RSV001234
date:
type: string
example: '2024-03-15'
start_at:
type: string
example: '2024-03-15T14:30:00.000000Z'
end_at:
type: string
example: '2024-03-15T16:00:00.000000Z'
name:
type: string
example: 'John Smith'
email:
type: string
example: john.smith@example.com
phone_number:
type: string
example: '+60123456789'
number_of_guests:
type: integer
example: 4
status:
type: string
example: reserved
remarks:
type: string
example: 'Birthday celebration'
custom_fields:
type: object
properties:
dietary_requirements:
type: string
example: Vegetarian
timeslot_id:
type: integer
example: 5
calendar_id:
type: integer
example: 1
customer_id:
type: integer
example: 123
reminder_sent_at:
type: string
example: '2024-03-14T14:30:00.000000Z'
rejection_reason:
type: string
example: null
nullable: true
created_at:
type: string
example: '2024-03-01T10:15:30.000000Z'
updated_at:
type: string
example: '2024-03-10T16:22:45.000000Z'
calendar:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Restaurant Reservations'
space_id:
type: integer
example: 1
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
tags:
- Reservations
put:
summary: 'Update Reservation'
operationId: updateReservation
description: "Updates an existing reservation's information.
All fields are optional.
\nStatus transitions are validated according to business rules."
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
data:
id: 1
ref_id: RSV001234
date: '2024-03-15'
start_at: '2024-03-15T14:30:00.000000Z'
end_at: '2024-03-15T16:00:00.000000Z'
name: 'John Smith'
email: john.smith@example.com
phone_number: '+60123456789'
number_of_guests: 6
status: reserved
remarks: 'Anniversary celebration - updated guest count'
custom_fields:
dietary_requirements: 'Vegetarian, Gluten-free'
timeslot_id: 5
calendar_id: 1
customer_id: 123
reminder_sent_at: '2024-03-14T14:30:00.000000Z'
rejection_reason: null
created_at: '2024-03-01T10:15:30.000000Z'
updated_at: '2024-03-10T16:22:45.000000Z'
message: 'Reservation updated successfully'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
ref_id:
type: string
example: RSV001234
date:
type: string
example: '2024-03-15'
start_at:
type: string
example: '2024-03-15T14:30:00.000000Z'
end_at:
type: string
example: '2024-03-15T16:00:00.000000Z'
name:
type: string
example: 'John Smith'
email:
type: string
example: john.smith@example.com
phone_number:
type: string
example: '+60123456789'
number_of_guests:
type: integer
example: 6
status:
type: string
example: reserved
remarks:
type: string
example: 'Anniversary celebration - updated guest count'
custom_fields:
type: object
properties:
dietary_requirements:
type: string
example: 'Vegetarian, Gluten-free'
timeslot_id:
type: integer
example: 5
calendar_id:
type: integer
example: 1
customer_id:
type: integer
example: 123
reminder_sent_at:
type: string
example: '2024-03-14T14:30:00.000000Z'
rejection_reason:
type: string
example: null
nullable: true
created_at:
type: string
example: '2024-03-01T10:15:30.000000Z'
updated_at:
type: string
example: '2024-03-10T16:22:45.000000Z'
message:
type: string
example: 'Reservation updated successfully'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Reservation not found'
properties:
message:
type: string
example: 'Reservation not found'
422:
description: 'Validation Error'
content:
application/json:
schema:
type: object
example:
message: 'The given data was invalid.'
errors:
status:
- 'Invalid status transition from reserved to pending.'
number_of_guests:
- 'The number of guests must be at least 1.'
properties:
message:
type: string
example: 'The given data was invalid.'
errors:
type: object
properties:
status:
type: array
example:
- 'Invalid status transition from reserved to pending.'
items:
type: string
number_of_guests:
type: array
example:
- 'The number of guests must be at least 1.'
items:
type: string
tags:
- Reservations
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
date:
type: string
description: 'The date of the reservation in Y-m-d format. Must be a valid date. Must be a date after or equal to today.'
example: '2024-03-15'
start_at:
type: string
description: 'The start date and time of the reservation in ISO 8601 format. Must be a valid date. Must be a date after date.'
example: '2024-03-15T14:30:00Z'
end_at:
type: string
description: 'The end date and time of the reservation in ISO 8601 format. Must be a valid date. Must be a date after start_at.'
example: '2024-03-15T16:00:00Z'
name:
type: string
description: 'The full name of the person making the reservation. Must not be greater than 255 characters.'
example: 'John Smith'
email:
type: string
description: 'The email address of the person making the reservation. Must be a valid email address. Must not be greater than 255 characters.'
example: john.smith@example.com
nullable: true
phone_number:
type: string
description: 'The phone number of the person making the reservation. Must not be greater than 255 characters.'
example: '+60123456789'
nullable: true
number_of_guests:
type: integer
description: 'The number of guests for the reservation. Must be at least 1. Must be at least 1. Must not be greater than 999.'
example: 4
status:
type: string
description: 'The initial status of the reservation.'
example: pending
enum:
- pending
- reserved
- cancelled
- rejected
- no_show
remarks:
type: string
description: 'Additional remarks or notes about the reservation. Must not be greater than 65535 characters.'
example: 'Birthday celebration, window seat preferred'
nullable: true
custom_fields:
type: object
description: 'Custom fields as key-value pairs for additional reservation data.'
example:
dietary_requirements: Vegetarian
special_requests: 'Birthday cake arrangement'
properties: { }
nullable: true
timeslot_id:
type: integer
description: 'The ID of the associated timeslot, if applicable.'
example: 5
nullable: true
calendar_id:
type: string
description: 'The ID of the calendar this reservation belongs to. Must belong to your organisation and accessible spaces.'
example: 1
customer_id:
type: string
description: 'The ID of the associated customer, if the reservation is linked to an existing customer. The id of an existing record in the customers table.'
example: 123
nullable: true
rejection_reason:
type: string
description: 'The reason for rejection if the status is set to rejected. Must not be greater than 65535 characters.'
example: 'Fully booked on requested date'
nullable: true
delete:
summary: 'Delete Reservation'
operationId: deleteReservation
description: "Soft deletes a reservation from the system.
\nThe reservation can be restored later if needed."
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
message: 'Reservation deleted successfully'
properties:
message:
type: string
example: 'Reservation deleted successfully'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Record not found'
properties:
message:
type: string
example: 'Record not found'
tags:
- Reservations
parameters:
-
in: path
name: id
description: 'The ID of the reservation to show.'
example: 1
required: true
schema:
type: integer
/api/user:
get:
summary: 'Get authenticated user'
operationId: getAuthenticatedUser
description: "Retrieves the currently authenticated user's information."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
id: 3
organisation_id: 2
name: User
email: vendor@pixalink.io
email_verified_at: '2025-01-31T21:16:49.000000Z'
created_at: '2025-01-31T21:16:49.000000Z'
updated_at: '2025-01-31T21:16:49.000000Z'
deleted_at: null
properties:
id:
type: integer
example: 3
description: 'The unique identifier of the user'
organisation_id:
type: integer
example: 2
description: 'The ID of the organisation the user belongs to'
name:
type: string
example: User
description: "The user's full name"
email:
type: string
example: vendor@pixalink.io
description: "The user's email address"
email_verified_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
description: "datetime The timestamp when the user's email was verified"
created_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
description: 'datetime The timestamp when the user was created'
updated_at:
type: string
example: '2025-01-31T21:16:49.000000Z'
description: 'datetime The timestamp when the user was last updated'
deleted_at:
type: string
example: null
description: 'datetime|null The timestamp when the user was deleted, null if not deleted'
tags:
- Endpoints
/api/countries:
get:
summary: 'List all countries'
operationId: listAllCountries
description: 'Get a list of all countries with their details.'
parameters:
-
in: query
name: 'filter[name]'
description: 'Filter countries by name.'
example: Malaysia
required: false
schema:
type: string
description: 'Filter countries by name.'
example: Malaysia
-
in: query
name: 'filter[nationality]'
description: 'Filter countries by nationality.'
example: Malaysian
required: false
schema:
type: string
description: 'Filter countries by nationality.'
example: Malaysian
-
in: query
name: 'filter[iso]'
description: 'Filter countries by ISO code.'
example: MY
required: false
schema:
type: string
description: 'Filter countries by ISO code.'
example: MY
-
in: query
name: per_page
description: 'Number of records to display per page.'
example: 15
required: false
schema:
type: integer
description: 'Number of records to display per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
name: Malaysia
nationality: Malaysian
iso: MY
iso3: MYS
links:
first: '@{{$baseUrl}}/api/countries?page=1'
last: '@{{$baseUrl}}/api/countries?page=1'
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
links:
-
url: null
label: '« Previous'
active: false
-
url: '@{{$baseUrl}}/api/countries?page=1'
label: '1'
active: true
-
url: null
label: 'Next »'
active: false
path: '@{{$baseUrl}}/api/countries'
per_page: 15
to: 1
total: 1
properties:
data:
type: array
example:
-
id: 1
name: Malaysia
nationality: Malaysian
iso: MY
iso3: MYS
items:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: Malaysia
nationality:
type: string
example: Malaysian
iso:
type: string
example: MY
iso3:
type: string
example: MYS
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/api/countries?page=1'
last:
type: string
example: '@{{$baseUrl}}/api/countries?page=1'
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
links:
type: array
example:
-
url: null
label: '« Previous'
active: false
-
url: '@{{$baseUrl}}/api/countries?page=1'
label: '1'
active: true
-
url: null
label: 'Next »'
active: false
items:
type: object
properties:
url:
type: string
example: null
nullable: true
label:
type: string
example: '« Previous'
active:
type: boolean
example: false
path:
type: string
example: '@{{$baseUrl}}/api/countries'
per_page:
type: integer
example: 15
to:
type: integer
example: 1
total:
type: integer
example: 1
tags:
- 'Location Management'
/api/states:
get:
summary: 'List all states'
operationId: listAllStates
description: 'Get a list of all states with their associated countries.'
parameters:
-
in: query
name: 'filter[name]'
description: 'Filter states by name.'
example: Selangor
required: false
schema:
type: string
description: 'Filter states by name.'
example: Selangor
-
in: query
name: 'filter[country_id]'
description: 'Filter states by country ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter states by country ID.'
example: 1
-
in: query
name: include
description: 'Include related models (Possible values: country).'
example: country
required: false
schema:
type: string
description: 'Include related models (Possible values: country).'
example: country
-
in: query
name: per_page
description: 'Number of records to display per page.'
example: 15
required: false
schema:
type: integer
description: 'Number of records to display per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
name: Selangor
country:
id: 1
name: Malaysia
nationality: Malaysian
iso: MY
iso3: MYS
phone_code: '60'
links:
first: '@{{$baseUrl}}/api/states?page=1'
last: '@{{$baseUrl}}/api/states?page=5'
prev: null
next: '@{{$baseUrl}}/api/states?page=2'
meta:
current_page: 1
from: 1
last_page: 5
path: '@{{$baseUrl}}/api/states'
per_page: 15
to: 15
total: 75
properties:
data:
type: array
example:
-
id: 1
name: Selangor
country:
id: 1
name: Malaysia
nationality: Malaysian
iso: MY
iso3: MYS
phone_code: '60'
items:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: Selangor
country:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: Malaysia
nationality:
type: string
example: Malaysian
iso:
type: string
example: MY
iso3:
type: string
example: MYS
phone_code:
type: string
example: '60'
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/api/states?page=1'
last:
type: string
example: '@{{$baseUrl}}/api/states?page=5'
prev:
type: string
example: null
nullable: true
next:
type: string
example: '@{{$baseUrl}}/api/states?page=2'
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 5
path:
type: string
example: '@{{$baseUrl}}/api/states'
per_page:
type: integer
example: 15
to:
type: integer
example: 15
total:
type: integer
example: 75
tags:
- 'Location Management'
/api/cities:
get:
summary: 'List all cities'
operationId: listAllCities
description: 'Get a list of all cities with their associated states.'
parameters:
-
in: query
name: 'filter[name]'
description: 'Filter cities by name.'
example: 'Petaling Jaya'
required: false
schema:
type: string
description: 'Filter cities by name.'
example: 'Petaling Jaya'
-
in: query
name: 'filter[state_id]'
description: 'Filter cities by state ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter cities by state ID.'
example: 1
-
in: query
name: include
description: 'Include related models (Possible values: state, state.country).'
example: 'state,state.country'
required: false
schema:
type: string
description: 'Include related models (Possible values: state, state.country).'
example: 'state,state.country'
-
in: query
name: sort
description: 'Sort by specified field. Prefix with `-` for descending sort. Possible values: name'
example: '-name'
required: false
schema:
type: string
description: 'Sort by specified field. Prefix with `-` for descending sort. Possible values: name'
example: '-name'
-
in: query
name: per_page
description: 'Number of records to display per page.'
example: 15
required: false
schema:
type: integer
description: 'Number of records to display per page.'
example: 15
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
name: 'Petaling Jaya'
state:
id: 1
name: Selangor
country:
id: 1
name: Malaysia
nationality: Malaysian
iso: MY
iso3: MYS
phone_code: '60'
links:
first: '@{{$baseUrl}}/api/cities?page=1'
last: '@{{$baseUrl}}/api/cities?page=1'
prev: null
next: null
meta:
current_page: 1
from: 1
last_page: 1
links:
-
url: null
label: '« Previous'
active: false
-
url: '@{{$baseUrl}}/api/cities?page=1'
label: '1'
active: true
-
url: null
label: 'Next »'
active: false
path: '@{{$baseUrl}}/api/cities'
per_page: 15
to: 1
total: 1
properties:
data:
type: array
example:
-
id: 1
name: 'Petaling Jaya'
state:
id: 1
name: Selangor
country:
id: 1
name: Malaysia
nationality: Malaysian
iso: MY
iso3: MYS
phone_code: '60'
items:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Petaling Jaya'
state:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: Selangor
country:
type: object
properties:
id:
type: integer
example: 1
name:
type: string
example: Malaysia
nationality:
type: string
example: Malaysian
iso:
type: string
example: MY
iso3:
type: string
example: MYS
phone_code:
type: string
example: '60'
links:
type: object
properties:
first:
type: string
example: '@{{$baseUrl}}/api/cities?page=1'
last:
type: string
example: '@{{$baseUrl}}/api/cities?page=1'
prev:
type: string
example: null
nullable: true
next:
type: string
example: null
nullable: true
meta:
type: object
properties:
current_page:
type: integer
example: 1
from:
type: integer
example: 1
last_page:
type: integer
example: 1
links:
type: array
example:
-
url: null
label: '« Previous'
active: false
-
url: '@{{$baseUrl}}/api/cities?page=1'
label: '1'
active: true
-
url: null
label: 'Next »'
active: false
items:
type: object
properties:
url:
type: string
example: null
nullable: true
label:
type: string
example: '« Previous'
active:
type: boolean
example: false
path:
type: string
example: '@{{$baseUrl}}/api/cities'
per_page:
type: integer
example: 15
to:
type: integer
example: 1
total:
type: integer
example: 1
tags:
- 'Location Management'
/api/sso/redirect:
post:
summary: 'Generate a signed SSO login URL for a customer.'
operationId: generateASignedSSOLoginURLForACustomer
description: "Verifies the HMAC-signed request, finds or creates the customer by phone number,\nthen returns a temporary signed URL that logs the customer into the loyalty portal.\nThe signed URL is valid for 10 minutes."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
redirect_url: 'https://explore.pixalink.io/space/1/loyalty/login/42?expires=1746518400&signature=abc123...'
properties:
redirect_url:
type: string
example: 'https://explore.pixalink.io/space/1/loyalty/login/42?expires=1746518400&signature=abc123...'
description: 'Temporary signed URL that logs the customer into the loyalty portal.'
401:
description: 'Missing or invalid authentication credentials'
content:
application/json:
schema:
type: object
example:
message: 'Invalid signature.'
properties:
message:
type: string
example: 'Invalid signature.'
403:
description: 'Client is not configured for SSO or OAuth2 is disabled'
content:
application/json:
schema:
type: object
example:
message: 'Client is not configured for SSO.'
properties:
message:
type: string
example: 'Client is not configured for SSO.'
404:
description: 'SSO feature disabled for this deployment'
content:
application/json:
schema:
type: object
example:
message: 'Not Found'
properties:
message:
type: string
example: 'Not Found'
422:
description: 'Validation failed'
content:
application/json:
schema:
type: object
example:
message: 'The customer phone field is required.'
errors:
customer_phone:
- 'The customer phone field is required.'
properties:
message:
type: string
example: 'The customer phone field is required.'
errors:
type: object
properties:
customer_phone:
type: array
example:
- 'The customer phone field is required.'
items:
type: string
tags:
- SSO
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
customer_phone:
type: string
description: 'Phone number of the customer in E.164 format. Must match the regex /^\+[1-9]\d{7,14}$/.'
example: '+60123456789'
space_id:
type: integer
description: 'ID of the Pixalink space. Must belong to your organisation. The id of an existing record in the spaces table.'
example: 42
timestamp:
type: integer
description: 'Current Unix timestamp in seconds. Must be within ±60s of server time.'
example: 1746518400
redirect_to:
type: string
description: 'Optional page to land on after login. Defaults to home.'
example: rewards
enum:
- home
- profile
- rewards
- points
- credits
- membership
- order
- reservations
- referral
required:
- customer_phone
- space_id
- timestamp
/api/sso/resolve:
post:
summary: 'Resolve an outbound SSO ticket.'
operationId: resolveAnOutboundSSOTicket
description: "Verifies the HMAC-signed request, then exchanges a single-use ticket for the customer\nidentity it was minted against. The ticket is consumed on first use, so a second call\nwith the same ticket returns 410. Only the organisation that owns the ticket may resolve it."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
customer:
id: 42
name: 'Jane Doe'
phone_number: '+60123456789'
email: jane@example.com
space_id: 1
organisation_id: 7
context: { }
properties:
customer:
type: object
properties:
id:
type: integer
example: 42
name:
type: string
example: 'Jane Doe'
phone_number:
type: string
example: '+60123456789'
email:
type: string
example: jane@example.com
space_id:
type: integer
example: 1
organisation_id:
type: integer
example: 7
description: 'The resolved customer identity.'
context:
type: object
properties: { }
description: 'Any context stored when the ticket was minted (e.g. an outlet or table reference).'
401:
description: 'Missing or invalid HMAC credentials'
content:
application/json:
schema:
type: object
example:
message: 'Invalid signature.'
properties:
message:
type: string
example: 'Invalid signature.'
410:
description: 'Ticket not found, already used, or expired'
content:
application/json:
schema:
type: object
example:
message: 'Ticket not found, already used, or expired.'
properties:
message:
type: string
example: 'Ticket not found, already used, or expired.'
tags:
- SSO
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
ticket:
type: string
description: 'The opaque ticket received on the redirect URL.'
example: kP3x…opaque…9Qz
timestamp:
type: integer
description: 'Current Unix timestamp in seconds. Must be within ±60s of server time.'
example: 1746518400
required:
- ticket
- timestamp