openapi: 3.0.3
info:
title: 'CEMAC POS API'
description: 'REST API for CEMAC POS — the offline-capable, OHADA-compliant point of sale by Opesware SARL. Multi-tenant: a token only ever sees its own shop.'
version: 1.0.0
servers:
-
url: 'https://cemacpos.com'
tags:
-
name: Endpoints
description: ''
components:
securitySchemes:
default:
type: http
scheme: bearer
description: 'Obtain a token via POST /api/v1/auth/login (email + password), then send it as Authorization: Bearer <token>.'
security:
-
default: []
paths:
/api/v1/auth/login:
post:
summary: 'Issue a Sanctum token for valid credentials.'
operationId: issueASanctumTokenForValidCredentials
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: 'Le champ value doit être une adresse e-mail valide.'
example: gbailey@example.net
password:
type: string
description: ''
example: '|]|{+-'
device_name:
type: string
description: ''
example: architecto
nullable: true
required:
- email
- password
/api/v1/auth/register:
post:
summary: 'Public self-signup: creates a shop with a 30-day trial and returns a token.'
operationId: publicSelfSignupCreatesAShopWithA30DayTrialAndReturnsAToken
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
shop_name:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: b
business_type:
type: string
description: ''
example: architecto
nullable: true
phone:
type: string
description: 'Le champ value ne peut pas dépasser 40 caractères.'
example: 'n'
nullable: true
city:
type: string
description: 'Le champ value ne peut pas dépasser 120 caractères.'
example: g
nullable: true
tax_id:
type: string
description: 'Le champ value ne peut pas dépasser 60 caractères.'
example: z
nullable: true
locale:
type: string
description: ''
example: en
enum:
- fr
- en
nullable: true
owner_name:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: m
owner_email:
type: string
description: 'Le champ value doit être une adresse e-mail valide.'
example: gulgowski.asia@example.com
password:
type: string
description: 'Le champ value doit contenir au moins 8 caractères.'
example: 'vYgxwmi/#'
referral_code:
type: string
description: "Sales-partner referral. Free text on purpose: an unknown code is\nignored, never a signup blocker. Le champ value ne peut pas dépasser 60 caractères."
example: m
nullable: true
required:
- shop_name
- owner_name
- owner_email
- password
/api/v1/auth/forgot-password:
post:
summary: ''
operationId: postApiV1AuthForgotPassword
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: 'Le champ value doit être une adresse e-mail valide.'
example: gbailey@example.net
required:
- email
/api/v1/auth/reset-password:
post:
summary: ''
operationId: postApiV1AuthResetPassword
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
token:
type: string
description: ''
example: architecto
email:
type: string
description: 'Le champ value doit être une adresse e-mail valide.'
example: zbailey@example.net
password:
type: string
description: 'Le champ value doit contenir au moins 8 caractères.'
example: '-0pBNvYgxw'
required:
- token
- email
- password
/api/v1/device-pairings:
post:
summary: 'A device with no credentials starts a pairing session.'
operationId: aDeviceWithNoCredentialsStartsAPairingSession
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Le champ value ne peut pas dépasser 120 caractères.'
example: b
nullable: true
platform:
type: string
description: ''
example: desktop
enum:
- pwa
- desktop
nullable: true
'/api/v1/device-pairings/{code}':
get:
summary: 'Polled by the requesting device until an owner claims the code.'
operationId: polledByTheRequestingDeviceUntilAnOwnerClaimsTheCode
description: ''
parameters: []
responses:
404:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'No pairing request with this code.'
properties:
message:
type: string
example: 'No pairing request with this code.'
tags:
- Endpoints
parameters:
-
in: path
name: code
description: ''
example: architecto
required: true
schema:
type: string
/api/v1/device/sync/pull:
get:
summary: "Pull records changed since the client's cursor, per entity, paginated."
operationId: pullRecordsChangedSinceTheClientsCursorPerEntityPaginated
description: "Query params:\n - since: ISO-8601 cursor. Omitted on a device's first sync.\n - limit: page size, default DEFAULT_LIMIT, clamped to [1, MAX_LIMIT].\n - include_sales: sales (with items + payments) are only built and\n serialised when this is truthy (\"1\"). This block is by far the most\n expensive part of the response — every sale in the window, each\n eager-loading its line items and payments — and nothing currently\n consumes it: the desktop app is just this same SPA loaded in an\n Electron shell, the client's sync engine (resources/js/lib/sync.js)\n already discards data.sales, and there is no local sales mirror to\n populate. Defaulting it off keeps ordinary pulls (catalogue +\n customers) cheap on memory-constrained shared hosting. When omitted,\n `data.sales` is still present as an empty array and\n `has_more.sales` as false, so the response shape never changes."
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: "Identifiants de l'appareil manquants."
properties:
message:
type: string
example: "Identifiants de l'appareil manquants."
tags:
- Endpoints
/api/v1/device/sync/push:
post:
summary: ''
operationId: postApiV1DeviceSyncPush
description: ''
parameters: []
responses: { }
tags:
- Endpoints
/api/v1/partner/dashboard:
get:
summary: 'Headline totals.'
operationId: headlineTotals
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/partner/referrals:
get:
summary: 'Referred shops with the commission earned on each.'
operationId: referredShopsWithTheCommissionEarnedOnEach
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/partner/commissions:
get:
summary: 'Individual commission lines.'
operationId: individualCommissionLines
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/admin/partners:
get:
summary: 'All partners with referral + commission totals.'
operationId: allPartnersWithReferral+CommissionTotals
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: 'Create a partner (and its cross-tenant login).'
operationId: createAPartnerandItsCrossTenantLogin
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: b
email:
type: string
description: 'Le champ value doit être une adresse e-mail valide.'
example: zbailey@example.net
password:
type: string
description: 'Le champ value doit contenir au moins 8 caractères.'
example: '-0pBNvYgxw'
phone:
type: string
description: 'Le champ value ne peut pas dépasser 40 caractères.'
example: a
nullable: true
code:
type: string
description: 'Le champ value ne peut pas dépasser 60 caractères.'
example: 'y'
nullable: true
commission_rate:
type: number
description: 'Le champ value doit être au moins 0. Le champ value ne peut pas dépasser 100.'
example: 18
nullable: true
commission_months:
type: integer
description: 'Le champ value doit être au moins 1. Le champ value ne peut pas dépasser 120.'
example: 8
nullable: true
payout_method:
type: string
description: 'Le champ value ne peut pas dépasser 40 caractères.'
example: m
nullable: true
payout_phone:
type: string
description: 'Le champ value ne peut pas dépasser 40 caractères.'
example: 'y'
nullable: true
payout_account:
type: string
description: 'Le champ value ne peut pas dépasser 80 caractères.'
example: u
nullable: true
payout_name:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: w
nullable: true
required:
- name
- email
- password
'/api/v1/admin/partners/{id}':
put:
summary: ''
operationId: putApiV1AdminPartnersId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: b
commission_rate:
type: number
description: 'Le champ value doit être au moins 0. Le champ value ne peut pas dépasser 100.'
example: 22
commission_months:
type: integer
description: 'Le champ value doit être au moins 1. Le champ value ne peut pas dépasser 120.'
example: 7
status:
type: string
description: ''
example: active
enum:
- active
- suspended
payout_method:
type: string
description: 'Le champ value ne peut pas dépasser 40 caractères.'
example: z
nullable: true
payout_phone:
type: string
description: 'Le champ value ne peut pas dépasser 40 caractères.'
example: m
nullable: true
payout_account:
type: string
description: 'Le champ value ne peut pas dépasser 80 caractères.'
example: i
nullable: true
payout_name:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: 'y'
nullable: true
notes:
type: string
description: 'Le champ value ne peut pas dépasser 2000 caractères.'
example: v
nullable: true
parameters:
-
in: path
name: id
description: 'The ID of the partner.'
example: architecto
required: true
schema:
type: string
/api/v1/admin/partner-commissions:
get:
summary: 'Commission ledger across all partners.'
operationId: commissionLedgerAcrossAllPartners
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/admin/partner-commissions/pay:
post:
summary: 'Mark commissions paid out (or payable).'
operationId: markCommissionsPaidOutorPayable
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
ids:
type: array
description: 'Le champ value doit être un UUID valide.'
example:
- 6ff8f7f6-1eb3-3525-be4a-3932c805afed
items:
type: string
partner_id:
type: string
description: 'This field is required when ids is not present. Le champ value doit être un UUID valide. Must match an existing stored value.'
example: 6b72fe4a-5b40-307c-bc24-f79acf9a1bb9
status:
type: string
description: ''
example: paid
enum:
- pending
- payable
- paid
nullable: true
payout_reference:
type: string
description: 'Le champ value ne peut pas dépasser 120 caractères.'
example: m
nullable: true
/api/v1/auth/me:
get:
summary: ''
operationId: getApiV1AuthMe
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/auth/logout:
post:
summary: ''
operationId: postApiV1AuthLogout
description: ''
parameters: []
responses: { }
tags:
- Endpoints
/api/v1/dashboard:
get:
summary: ''
operationId: getApiV1Dashboard
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/profile:
put:
summary: ''
operationId: putApiV1Profile
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: b
phone:
type: string
description: 'Le champ value ne peut pas dépasser 40 caractères.'
example: 'n'
nullable: true
locale:
type: string
description: ''
example: en
enum:
- fr
- en
/api/v1/profile/password:
post:
summary: ''
operationId: postApiV1ProfilePassword
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
current_password:
type: string
description: ''
example: architecto
password:
type: string
description: 'Le champ value doit contenir au moins 8 caractères.'
example: ']|{+-0pBNvYg'
required:
- current_password
- password
/api/v1/billing/plans:
get:
summary: 'Available plans to subscribe to.'
operationId: availablePlansToSubscribeTo
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/billing:
get:
summary: 'Current subscription state + recent charges.'
operationId: currentSubscriptionState+RecentCharges
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/billing/subscribe:
post:
summary: 'Initiate a Mobile Money charge to start/renew a plan.'
operationId: initiateAMobileMoneyChargeToStartrenewAPlan
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
plan_id:
type: string
description: 'Le champ value doit être un UUID valide. Must match an existing stored value.'
example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
method:
type: string
description: ''
example: orange_money
enum:
- mtn_momo
- orange_money
phone:
type: string
description: 'Le champ value ne peut pas dépasser 40 caractères.'
example: g
required:
- plan_id
- method
- phone
'/api/v1/billing/confirm/{reference}':
post:
summary: 'Poll a charge; activates the subscription if the payment succeeded.'
operationId: pollAChargeActivatesTheSubscriptionIfThePaymentSucceeded
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: reference
description: ''
example: architecto
required: true
schema:
type: string
/api/v1/categories:
get:
summary: ''
operationId: getApiV1Categories
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Categories
description: ''
parameters: []
responses: { }
tags:
- Endpoints
'/api/v1/categories/{id}':
put:
summary: ''
operationId: putApiV1CategoriesId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
delete:
summary: ''
operationId: deleteApiV1CategoriesId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: id
description: 'The ID of the category.'
example: 019fbec2-b6a9-701b-b2b5-0b1eb8944664
required: true
schema:
type: string
/api/v1/products/barcodes/generate:
post:
summary: ''
operationId: postApiV1ProductsBarcodesGenerate
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
product_ids:
type: array
description: 'Le champ value doit être un UUID valide.'
example:
- 6ff8f7f6-1eb3-3525-be4a-3932c805afed
items:
type: string
category_id:
type: string
description: 'Le champ value doit être un UUID valide. Must match an existing stored value.'
example: 6b72fe4a-5b40-307c-bc24-f79acf9a1bb9
nullable: true
include_variants:
type: boolean
description: ''
example: true
nullable: true
/api/v1/products:
get:
summary: ''
operationId: getApiV1Products
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Products
description: ''
parameters: []
responses: { }
tags:
- Endpoints
'/api/v1/products/{id}':
get:
summary: ''
operationId: getApiV1ProductsId
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
put:
summary: ''
operationId: putApiV1ProductsId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
delete:
summary: ''
operationId: deleteApiV1ProductsId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: id
description: 'The ID of the product.'
example: 019fbec2-b6b4-722c-908b-618839613174
required: true
schema:
type: string
'/api/v1/products/{product_id}/adjust':
post:
summary: 'Manual stock adjustment / stocktake: set on-hand to a counted figure.'
operationId: manualStockAdjustmentStocktakeSetOnHandToACountedFigure
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
new_quantity:
type: number
description: 'Le champ value doit être au moins 0.'
example: 27
reason:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: 'n'
nullable: true
required:
- new_quantity
parameters:
-
in: path
name: product_id
description: 'The ID of the product.'
example: 019fbec2-b6b4-722c-908b-618839613174
required: true
schema:
type: string
'/api/v1/products/{product_id}/movements':
get:
summary: 'Per-product stock-movement ledger (purchases, sales, adjustments, returns).'
operationId: perProductStockMovementLedgerpurchasesSalesAdjustmentsReturns
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: product_id
description: 'The ID of the product.'
example: 019fbec2-b6b4-722c-908b-618839613174
required: true
schema:
type: string
/api/v1/reports/stock-valuation:
get:
summary: 'Inventory valuation: on-hand quantity × last cost, per tracked product.'
operationId: inventoryValuationOnHandQuantityLastCostPerTrackedProduct
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
'/api/v1/products/{product_id}/options':
get:
summary: ''
operationId: getApiV1ProductsProduct_idOptions
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: "Replace the product's axes wholesale."
operationId: replaceTheProductsAxesWholesale
description: "Sent as a whole set rather than patched one value at a time: the editor\nis a small form the shop fills in and saves, and a partial update would\nmake \"I removed XXL\" indistinguishable from \"I did not mention XXL\"."
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
options:
type: array
description: 'Le champ value doit contenir au moins 1 éléments. Le champ value ne doit pas contenir plus de 4 éléments.'
example:
- []
items:
type: object
properties:
name:
type: string
description: 'Le champ value ne peut pas dépasser 60 caractères.'
example: b
values:
type: array
description: 'Le champ value ne peut pas dépasser 60 caractères.'
example:
- 'n'
items:
type: string
required:
- name
- values
required:
- options
parameters:
-
in: path
name: product_id
description: 'The ID of the product.'
example: 019fbec2-b6b4-722c-908b-618839613174
required: true
schema:
type: string
'/api/v1/products/{product_id}/options/{option_id}':
delete:
summary: ''
operationId: deleteApiV1ProductsProduct_idOptionsOption_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: product_id
description: 'The ID of the product.'
example: 019fbec2-b6b4-722c-908b-618839613174
required: true
schema:
type: string
-
in: path
name: option_id
description: 'The ID of the option.'
example: 019fbff7-8d4a-7009-b2be-ca7f9ae75978
required: true
schema:
type: string
'/api/v1/products/{product_id}/variants/generate':
post:
summary: 'Create every missing combination.'
operationId: createEveryMissingCombination
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
default_price_xaf:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 27
nullable: true
parameters:
-
in: path
name: product_id
description: 'The ID of the product.'
example: 019fbec2-b6b4-722c-908b-618839613174
required: true
schema:
type: string
'/api/v1/products/{product_id}/variants':
get:
summary: ''
operationId: getApiV1ProductsProduct_idVariants
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1ProductsProduct_idVariants
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: product_id
description: 'The ID of the product.'
example: 019fbec2-b6b4-722c-908b-618839613174
required: true
schema:
type: string
'/api/v1/variants/{variant_id}':
put:
summary: ''
operationId: putApiV1VariantsVariant_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
delete:
summary: ''
operationId: deleteApiV1VariantsVariant_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: variant_id
description: 'The ID of the variant.'
example: 019fbff7-8d8a-71b2-8e17-66314fb6955e
required: true
schema:
type: string
/api/v1/customers/find-by-card:
get:
summary: "Find the customer wearing a given loyalty card — the till's scan-to-lookup."
operationId: findTheCustomerWearingAGivenLoyaltyCardTheTillsScanToLookup
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
card_no:
type: string
description: 'Le champ value ne peut pas dépasser 60 caractères.'
example: b
required:
- card_no
/api/v1/loyalty-cards/register:
post:
summary: "Registers a physical card — one already numbered (bought pre-printed\nin bulk) rather than one this system minted — against a customer."
operationId: registersAPhysicalCardOneAlreadyNumberedboughtPrePrintedInBulkRatherThanOneThisSystemMintedAgainstACustomer
description: "Either an existing customer_id, or a name to create one on the spot: a\nshop handing out cards at a counter is not going to open a separate\n\"new customer\" screen first for someone standing right there."
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
card_no:
type: string
description: 'Le champ value ne peut pas dépasser 60 caractères.'
example: b
customer_id:
type: string
description: 'Le champ value doit être un UUID valide. Must match an existing stored value.'
example: a4855dc5-0acb-33c3-b921-f4291f719ca0
nullable: true
name:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: z
nullable: true
required:
- card_no
/api/v1/customers:
get:
summary: ''
operationId: getApiV1Customers
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Customers
description: ''
parameters: []
responses: { }
tags:
- Endpoints
'/api/v1/customers/{id}':
get:
summary: ''
operationId: getApiV1CustomersId
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
put:
summary: ''
operationId: putApiV1CustomersId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
delete:
summary: ''
operationId: deleteApiV1CustomersId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: id
description: 'The ID of the customer.'
example: 019fbec2-b6df-70a4-9d78-ce57e285e388
required: true
schema:
type: string
/api/v1/suppliers:
get:
summary: ''
operationId: getApiV1Suppliers
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Suppliers
description: ''
parameters: []
responses: { }
tags:
- Endpoints
'/api/v1/suppliers/{id}':
put:
summary: ''
operationId: putApiV1SuppliersId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
delete:
summary: ''
operationId: deleteApiV1SuppliersId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: id
description: 'The ID of the supplier.'
example: architecto
required: true
schema:
type: string
/api/v1/purchases:
get:
summary: ''
operationId: getApiV1Purchases
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: 'Receive goods: increases stock, refreshes cost, posts the purchase entry.'
operationId: receiveGoodsIncreasesStockRefreshesCostPostsThePurchaseEntry
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
supplier_id:
type: string
description: 'Le champ value doit être un UUID valide. Must match an existing stored value.'
example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
nullable: true
reference:
type: string
description: 'Le champ value ne peut pas dépasser 120 caractères.'
example: g
nullable: true
note:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: z
nullable: true
vat_rate:
type: number
description: 'Le champ value doit être au moins 0. Le champ value ne peut pas dépasser 100.'
example: 17
nullable: true
items:
type: array
description: 'Le champ value doit contenir au moins 1 éléments.'
example:
- []
items:
type: object
properties:
product_id:
type: string
description: 'Le champ value doit être un UUID valide.'
example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
nullable: true
variant_id:
type: string
description: 'Le champ value doit être un UUID valide.'
example: 6b72fe4a-5b40-307c-bc24-f79acf9a1bb9
nullable: true
name:
type: string
description: 'Le champ value ne peut pas dépasser 200 caractères.'
example: m
nullable: true
quantity:
type: number
description: ''
example: 4326.41688
unit_cost_xaf:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 77
batch_number:
type: string
description: 'Le champ value ne peut pas dépasser 80 caractères.'
example: i
nullable: true
expiry_date:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:16'
nullable: true
required:
- quantity
- unit_cost_xaf
required:
- items
'/api/v1/purchases/{purchase_id}':
get:
summary: ''
operationId: getApiV1PurchasesPurchase_id
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: purchase_id
description: 'The ID of the purchase.'
example: architecto
required: true
schema:
type: string
'/api/v1/purchases/{purchase_id}/pay':
post:
summary: 'Settle (part of) a supplier invoice: Debit 401 / Credit treasury.'
operationId: settlepartOfASupplierInvoiceDebit401CreditTreasury
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
amount_xaf:
type: integer
description: 'Le champ value doit être au moins 1.'
example: 16
method:
type: string
description: ''
example: bank
enum:
- cash
- mtn_momo
- orange_money
- card
- bank
reference:
type: string
description: 'Le champ value ne peut pas dépasser 120 caractères.'
example: 'n'
nullable: true
required:
- amount_xaf
- method
parameters:
-
in: path
name: purchase_id
description: 'The ID of the purchase.'
example: architecto
required: true
schema:
type: string
'/api/v1/purchases/{purchase_id}/returns':
post:
summary: 'Send goods back to the supplier.'
operationId: sendGoodsBackToTheSupplier
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
items:
type: array
description: 'Le champ value doit contenir au moins 1 éléments.'
example:
- []
items:
type: object
properties:
purchase_item_id:
type: string
description: 'Le champ value doit être un UUID valide.'
example: a4855dc5-0acb-33c3-b921-f4291f719ca0
quantity:
type: number
description: ''
example: 4326.41688
required:
- purchase_item_id
- quantity
reason:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: b
nullable: true
required:
- items
parameters:
-
in: path
name: purchase_id
description: 'The ID of the purchase.'
example: architecto
required: true
schema:
type: string
/api/v1/reports/payables:
get:
summary: ''
operationId: getApiV1ReportsPayables
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/reports/receivables:
get:
summary: 'Who owes what, with ageing buckets.'
operationId: whoOwesWhatWithAgeingBuckets
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
'/api/v1/customers/{customer_id}/statement':
get:
summary: 'Balance, ceiling, unsettled sales and settlement history.'
operationId: balanceCeilingUnsettledSalesAndSettlementHistory
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: customer_id
description: 'The ID of the customer.'
example: 019fbec2-b6df-70a4-9d78-ce57e285e388
required: true
schema:
type: string
'/api/v1/customers/{customer_id}/payments':
post:
summary: 'Record a settlement against the balance (allocated oldest sale first).'
operationId: recordASettlementAgainstTheBalanceallocatedOldestSaleFirst
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
amount_xaf:
type: integer
description: 'Le champ value doit être au moins 1.'
example: 16
method:
type: string
description: ''
example: architecto
note:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: 'n'
nullable: true
required:
- amount_xaf
- method
parameters:
-
in: path
name: customer_id
description: 'The ID of the customer.'
example: 019fbec2-b6df-70a4-9d78-ce57e285e388
required: true
schema:
type: string
/api/v1/loyalty/tiers:
get:
summary: "The shop's loyalty tier ladder (Bronze/Silver/Gold or custom)."
operationId: theShopsLoyaltyTierLadderBronzeSilverGoldOrCustom
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
put:
summary: "Replace the shop's tier ladder. Read via tenant.settings.loyalty_tiers\n(LoyaltyService::tiers()) since it was configured months ago; nothing\never wrote to that path — a shop wanting a custom ladder had no way to\nset one short of a database edit."
operationId: replaceTheShopsTierLadderReadViaTenantsettingsloyaltyTiersLoyaltyServicetiersSinceItWasConfiguredMonthsAgoNothingEverWroteToThatPathAShopWantingACustomLadderHadNoWayToSetOneShortOfADatabaseEdit
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
tiers:
type: array
description: 'Le champ value doit contenir au moins 1 éléments. Le champ value ne doit pas contenir plus de 8 éléments.'
example:
- []
items:
type: object
properties:
key:
type: string
description: 'Le champ value ne peut pas dépasser 40 caractères.'
example: b
name:
type: string
description: 'Le champ value ne peut pas dépasser 60 caractères.'
example: 'n'
min_points:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 84
multiplier:
type: number
description: 'Le champ value doit être au moins 0. Le champ value ne peut pas dépasser 10.'
example: 6
required:
- key
- name
- min_points
- multiplier
required:
- tiers
/api/v1/loyalty/report:
get:
summary: "What the loyalty programme actually costs and does: points issued,\nredeemed and adjusted, the top holders of unredeemed points (a\nliability the shop is carrying), and the split by tier. Nothing about\nloyalty had a reporting screen before this — a shop could see one\ncustomer's ledger, never the programme as a whole."
operationId: whatTheLoyaltyProgrammeActuallyCostsAndDoesPointsIssuedRedeemedAndAdjustedTheTopHoldersOfUnredeemedPointsaLiabilityTheShopIsCarryingAndTheSplitByTierNothingAboutLoyaltyHadAReportingScreenBeforeThisAShopCouldSeeOneCustomersLedgerNeverTheProgrammeAsAWhole
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
'/api/v1/customers/{customer_id}/card':
get:
summary: "Print/download data for the customer's loyalty card."
operationId: printdownloadDataForTheCustomersLoyaltyCard
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: customer_id
description: 'The ID of the customer.'
example: 019fbec2-b6df-70a4-9d78-ce57e285e388
required: true
schema:
type: string
'/api/v1/customers/{customer_id}/points':
post:
summary: 'A manager correcting a balance outside any sale — a goodwill point, or fixing a mistake.'
operationId: aManagerCorrectingABalanceOutsideAnySaleAGoodwillPointOrFixingAMistake
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
delta:
type: integer
description: 'Must not be one of 0.'
example: 16
reason:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: 'n'
required:
- delta
- reason
parameters:
-
in: path
name: customer_id
description: 'The ID of the customer.'
example: 019fbec2-b6df-70a4-9d78-ce57e285e388
required: true
schema:
type: string
/api/v1/promotions:
get:
summary: ''
operationId: getApiV1Promotions
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Promotions
description: ''
parameters: []
responses: { }
tags:
- Endpoints
'/api/v1/promotions/{promotion_id}':
put:
summary: ''
operationId: putApiV1PromotionsPromotion_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
delete:
summary: ''
operationId: deleteApiV1PromotionsPromotion_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: promotion_id
description: 'The ID of the promotion.'
example: 019fbfac-cf48-710f-858f-765b3d797b71
required: true
schema:
type: string
/api/v1/quotes:
get:
summary: ''
operationId: getApiV1Quotes
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Quotes
description: ''
parameters: []
responses: { }
tags:
- Endpoints
'/api/v1/quotes/{quote_id}':
get:
summary: ''
operationId: getApiV1QuotesQuote_id
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
put:
summary: ''
operationId: putApiV1QuotesQuote_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
delete:
summary: ''
operationId: deleteApiV1QuotesQuote_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: quote_id
description: 'The ID of the quote.'
example: architecto
required: true
schema:
type: string
'/api/v1/quotes/{quote_id}/status':
post:
summary: 'Mark sent / declined without converting.'
operationId: markSentDeclinedWithoutConverting
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
status:
type: string
description: ''
example: expired
enum:
- draft
- sent
- declined
- expired
required:
- status
parameters:
-
in: path
name: quote_id
description: 'The ID of the quote.'
example: architecto
required: true
schema:
type: string
'/api/v1/quotes/{quote_id}/convert':
post:
summary: 'Accept the quote and raise the real sale.'
operationId: acceptTheQuoteAndRaiseTheRealSale
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
payments:
type: array
description: ''
example: null
items:
type: object
nullable: true
properties:
method:
type: string
description: 'This field is required when payments is present.'
example: null
amount_xaf:
type: integer
description: 'This field is required when payments is present. Le champ value doit être au moins 0.'
example: 27
reference:
type: string
description: 'Le champ value ne peut pas dépasser 120 caractères.'
example: 'n'
nullable: true
parameters:
-
in: path
name: quote_id
description: 'The ID of the quote.'
example: architecto
required: true
schema:
type: string
/api/v1/sales:
get:
summary: ''
operationId: getApiV1Sales
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Sales
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
id:
type: string
description: 'Le champ value doit être un UUID valide.'
example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
nullable: true
customer_id:
type: string
description: 'Le champ value doit être un UUID valide. Must match an existing stored value.'
example: 6b72fe4a-5b40-307c-bc24-f79acf9a1bb9
nullable: true
device_id:
type: string
description: 'Le champ value doit être un UUID valide.'
example: 977e5426-8d13-3824-86aa-b092f8ae52c5
nullable: true
note:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: 'y'
nullable: true
redeem_points:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 60
nullable: true
items:
type: array
description: 'Le champ value doit contenir au moins 1 éléments.'
example:
- []
items:
type: object
properties:
product_id:
type: string
description: 'Le champ value doit être un UUID valide.'
example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
nullable: true
variant_id:
type: string
description: 'Le champ value doit être un UUID valide.'
example: 6b72fe4a-5b40-307c-bc24-f79acf9a1bb9
nullable: true
name:
type: string
description: 'Le champ value ne peut pas dépasser 200 caractères.'
example: m
nullable: true
quantity:
type: number
description: ''
example: 4326.41688
unit_price_xaf:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 77
nullable: true
discount_xaf:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 8
nullable: true
vat_rate:
type: number
description: 'Le champ value doit être au moins 0. Le champ value ne peut pas dépasser 100.'
example: 8
nullable: true
required:
- quantity
payments:
type: array
description: ''
example: null
items:
type: object
nullable: true
properties:
method:
type: string
description: 'This field is required when payments is present.'
example: null
amount_xaf:
type: integer
description: 'This field is required when payments is present. Le champ value doit être au moins 0.'
example: 60
reference:
type: string
description: 'Le champ value ne peut pas dépasser 120 caractères.'
example: d
nullable: true
required:
- items
'/api/v1/sales/{sale_id}':
get:
summary: ''
operationId: getApiV1SalesSale_id
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: sale_id
description: 'The ID of the sale.'
example: 569e3716-b7cf-4f01-8186-92f2d7e82f7c
required: true
schema:
type: string
'/api/v1/sales/{sale_id}/void':
post:
summary: ''
operationId: postApiV1SalesSale_idVoid
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
reason:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: b
required:
- reason
parameters:
-
in: path
name: sale_id
description: 'The ID of the sale.'
example: 569e3716-b7cf-4f01-8186-92f2d7e82f7c
required: true
schema:
type: string
'/api/v1/sales/{sale_id}/refund':
post:
summary: 'Partial return / refund of selected items from a completed sale.'
operationId: partialReturnRefundOfSelectedItemsFromACompletedSale
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
method:
type: string
description: ''
example: architecto
reason:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: 'n'
nullable: true
items:
type: array
description: 'Le champ value doit contenir au moins 1 éléments.'
example:
- []
items:
type: object
properties:
sale_item_id:
type: string
description: 'Le champ value doit être un UUID valide.'
example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
quantity:
type: number
description: ''
example: 4326.41688
required:
- sale_item_id
- quantity
required:
- method
- items
parameters:
-
in: path
name: sale_id
description: 'The ID of the sale.'
example: 569e3716-b7cf-4f01-8186-92f2d7e82f7c
required: true
schema:
type: string
'/api/v1/sales/{sale_id}/attribute-loyalty':
post:
summary: "A customer shows their loyalty card after paying. Credits the points\nthat sale would have earned had she been on the ticket, without\nrewriting the sale itself — see\nLoyaltyService::attributeToCompletedSale() for why."
operationId: aCustomerShowsTheirLoyaltyCardAfterPayingCreditsThePointsThatSaleWouldHaveEarnedHadSheBeenOnTheTicketWithoutRewritingTheSaleItselfSeeLoyaltyServiceattributeToCompletedSaleForWhy
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
card_no:
type: string
description: 'Le champ value ne peut pas dépasser 60 caractères.'
example: b
required:
- card_no
parameters:
-
in: path
name: sale_id
description: 'The ID of the sale.'
example: 569e3716-b7cf-4f01-8186-92f2d7e82f7c
required: true
schema:
type: string
'/api/v1/sales/{sale_id}/receipt':
get:
summary: "Returns the receipt as base64 ESC/POS bytes ready to stream to a thermal\nprinter. ?width=58|80 selects paper (32 or 48 columns); ?locale=fr|en."
operationId: returnsTheReceiptAsBase64ESCPOSBytesReadyToStreamToAThermalPrinterwidth5880SelectsPaper32Or48Columnslocalefren
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: sale_id
description: 'The ID of the sale.'
example: 569e3716-b7cf-4f01-8186-92f2d7e82f7c
required: true
schema:
type: string
'/api/v1/sales/{sale_id}/receipt/text':
get:
summary: "The receipt as plain text, for sharing by WhatsApp or any other channel\nthe client can reach. Returned rather than sent: WhatsApp has no server\nAPI here, and a share link opened on the cashier's own phone is both\nfree and the way shops in the region actually send these."
operationId: theReceiptAsPlainTextForSharingByWhatsAppOrAnyOtherChannelTheClientCanReachReturnedRatherThanSentWhatsAppHasNoServerAPIHereAndAShareLinkOpenedOnTheCashiersOwnPhoneIsBothFreeAndTheWayShopsInTheRegionActuallySendThese
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: sale_id
description: 'The ID of the sale.'
example: 569e3716-b7cf-4f01-8186-92f2d7e82f7c
required: true
schema:
type: string
'/api/v1/sales/{sale_id}/receipt/email':
post:
summary: 'E-mail the receipt to the customer (or any address supplied).'
operationId: eMailTheReceiptToTheCustomerorAnyAddressSupplied
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: 'Le champ value doit être une adresse e-mail valide. Le champ value ne peut pas dépasser 160 caractères.'
example: gbailey@example.net
nullable: true
locale:
type: string
description: ''
example: fr
enum:
- fr
- en
nullable: true
parameters:
-
in: path
name: sale_id
description: 'The ID of the sale.'
example: 569e3716-b7cf-4f01-8186-92f2d7e82f7c
required: true
schema:
type: string
'/api/v1/sales/{sale_id}/invoice':
get:
summary: "A full, self-contained HTML invoice ready for the browser's print dialogue."
operationId: aFullSelfContainedHTMLInvoiceReadyForTheBrowsersPrintDialogue
description: "No PDF library on purpose: shared hosting in the region rarely has the\nextensions those need, and the browser already renders @page CSS to PDF\nfor free. ?format=a4|a3 picks the sheet, ?locale=fr|en the language."
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: sale_id
description: 'The ID of the sale.'
example: 569e3716-b7cf-4f01-8186-92f2d7e82f7c
required: true
schema:
type: string
/api/v1/expenses:
get:
summary: ''
operationId: getApiV1Expenses
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Expenses
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
category:
type: string
description: ''
example: transport
enum:
- rent
- utilities
- salaries
- transport
- supplies
- other
amount_xaf:
type: integer
description: 'Le champ value doit être au moins 1.'
example: 16
payment_method:
type: string
description: ''
example: orange_money
enum:
- cash
- mtn_momo
- orange_money
- card
- bank
description:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: 'Et animi quos velit et fugiat.'
nullable: true
spent_at:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:16'
nullable: true
required:
- category
- amount_xaf
- payment_method
'/api/v1/expenses/{expense_id}':
delete:
summary: ''
operationId: deleteApiV1ExpensesExpense_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: expense_id
description: 'The ID of the expense.'
example: architecto
required: true
schema:
type: string
/api/v1/shift:
get:
summary: "The cashier's open shift (with live expected-cash), or null."
operationId: theCashiersOpenShiftwithLiveExpectedCashOrNull
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/shifts:
get:
summary: ''
operationId: getApiV1Shifts
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/shift/open:
post:
summary: ''
operationId: postApiV1ShiftOpen
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
opening_float_xaf:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 27
required:
- opening_float_xaf
/api/v1/shift/close:
post:
summary: ''
operationId: postApiV1ShiftClose
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
counted_cash_xaf:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 27
note:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: 'n'
nullable: true
counted_by_method:
type: array
description: 'Le champ value doit être au moins 0.'
example:
- 84
items:
type: integer
nullable: true
required:
- counted_cash_xaf
/api/v1/shift/cash:
post:
summary: 'Record cash in or out of the drawer that is not a sale.'
operationId: recordCashInOrOutOfTheDrawerThatIsNotASale
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
direction:
type: string
description: ''
example: out
enum:
- in
- out
amount_xaf:
type: integer
description: 'Le champ value doit être au moins 1.'
example: 16
reason:
type: string
description: "Required, because an unexplained withdrawal is exactly the thing\nthis record exists to stop being unexplained. Le champ value ne peut pas dépasser 160 caractères."
example: 'n'
required:
- direction
- amount_xaf
- reason
/api/v1/reports/z:
get:
summary: ''
operationId: getApiV1ReportsZ
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/reports/sales-summary:
get:
summary: ''
operationId: getApiV1ReportsSalesSummary
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
from:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:16'
to:
type: string
description: 'Le champ value doit être une date valide. Le champ value doit être une date postérieure ou égale à from.'
example: '2052-08-27'
required:
- from
- to
/api/v1/reports/low-stock:
get:
summary: 'Products at or below their reorder level.'
operationId: productsAtOrBelowTheirReorderLevel
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/reports/expiring:
get:
summary: ''
operationId: getApiV1ReportsExpiring
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
within_days:
type: integer
description: 'Le champ value doit être au moins 1. Le champ value ne peut pas dépasser 3650.'
example: 1
nullable: true
/api/v1/alerts:
get:
summary: ''
operationId: getApiV1Alerts
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
dead_days:
type: integer
description: 'Le champ value doit être au moins 1. Le champ value ne peut pas dépasser 3650.'
example: 1
nullable: true
expiring_days:
type: integer
description: 'Le champ value doit être au moins 1. Le champ value ne peut pas dépasser 3650.'
example: 22
nullable: true
limit:
type: integer
description: 'Le champ value doit être au moins 1.'
example: 67
nullable: true
/api/v1/reports/margin:
get:
summary: ''
operationId: getApiV1ReportsMargin
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
from:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:17'
nullable: true
to:
type: string
description: 'Le champ value doit être une date valide. Le champ value doit être une date postérieure ou égale à from.'
example: '2052-08-27'
nullable: true
/api/v1/reports/losses:
get:
summary: ''
operationId: getApiV1ReportsLosses
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
from:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:17'
nullable: true
to:
type: string
description: 'Le champ value doit être une date valide. Le champ value doit être une date postérieure ou égale à from.'
example: '2052-08-27'
nullable: true
/api/v1/stocktakes:
get:
summary: ''
operationId: getApiV1Stocktakes
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Stocktakes
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
note:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: b
nullable: true
'/api/v1/stocktakes/{stocktake_id}':
get:
summary: ''
operationId: getApiV1StocktakesStocktake_id
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: stocktake_id
description: 'The ID of the stocktake.'
example: 019fbfaf-5d33-7199-bdf5-7a88af9cb364
required: true
schema:
type: string
'/api/v1/stocktakes/{stocktake_id}/counts':
post:
summary: ''
operationId: postApiV1StocktakesStocktake_idCounts
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
counts:
type: array
description: 'Le champ value doit contenir au moins 1 éléments.'
example:
- []
items:
type: object
properties:
item_id:
type: string
description: 'Le champ value doit être un UUID valide.'
example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
counted_quantity:
type: number
description: 'Le champ value doit être au moins 0.'
example: 84
nullable: true
required:
- item_id
required:
- counts
parameters:
-
in: path
name: stocktake_id
description: 'The ID of the stocktake.'
example: 019fbfaf-5d33-7199-bdf5-7a88af9cb364
required: true
schema:
type: string
'/api/v1/stocktakes/{stocktake_id}/commit':
post:
summary: ''
operationId: postApiV1StocktakesStocktake_idCommit
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: stocktake_id
description: 'The ID of the stocktake.'
example: 019fbfaf-5d33-7199-bdf5-7a88af9cb364
required: true
schema:
type: string
'/api/v1/stocktakes/{stocktake_id}/cancel':
post:
summary: ''
operationId: postApiV1StocktakesStocktake_idCancel
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: stocktake_id
description: 'The ID of the stocktake.'
example: 019fbfaf-5d33-7199-bdf5-7a88af9cb364
required: true
schema:
type: string
/api/v1/reports/analytics:
get:
summary: ''
operationId: getApiV1ReportsAnalytics
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
from:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:17'
nullable: true
to:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:17'
nullable: true
/api/v1/reports/vat-declaration:
get:
summary: 'Period VAT declaration: collected (4431) − deductible (4452).'
operationId: periodVATDeclarationCollected4431Deductible4452
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/reports/trial-balance:
get:
summary: ''
operationId: getApiV1ReportsTrialBalance
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/reports/income-statement:
get:
summary: ''
operationId: getApiV1ReportsIncomeStatement
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/reports/journal.csv:
get:
summary: "SYSCOHADA journal as CSV for the shop's accountant."
operationId: sYSCOHADAJournalAsCSVForTheShopsAccountant
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/reports/balance-sheet:
get:
summary: ''
operationId: getApiV1ReportsBalanceSheet
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
'/api/v1/reports/ledger/{accountCode}':
get:
summary: ''
operationId: getApiV1ReportsLedgerAccountCode
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: accountCode
description: ''
example: architecto
required: true
schema:
type: string
/api/v1/accounting/close-period:
post:
summary: 'Close the books through a date: posts drafts, books the stock variation, moves the watermark.'
operationId: closeTheBooksThroughADatePostsDraftsBooksTheStockVariationMovesTheWatermark
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
through:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:17'
required:
- through
/api/v1/accounting/journal-entries:
post:
summary: 'A manual journal entry — capital, a loan, a fixed asset, an opening balance.'
operationId: aManualJournalEntryCapitalALoanAFixedAssetAnOpeningBalance
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
date:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:17'
memo:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: b
lines:
type: array
description: 'Le champ value doit contenir au moins 2 éléments.'
example:
- []
items:
type: object
properties:
account_code:
type: string
description: 'Le champ value ne peut pas dépasser 10 caractères.'
example: bngzmi
debit_xaf:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 76
nullable: true
credit_xaf:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 60
nullable: true
required:
- account_code
required:
- date
- memo
- lines
/api/v1/tables:
get:
summary: ''
operationId: getApiV1Tables
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Tables
description: ''
parameters: []
responses: { }
tags:
- Endpoints
'/api/v1/tables/{id}':
put:
summary: ''
operationId: putApiV1TablesId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
delete:
summary: ''
operationId: deleteApiV1TablesId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: id
description: 'The ID of the table.'
example: architecto
required: true
schema:
type: string
/api/v1/kitchen:
get:
summary: ''
operationId: getApiV1Kitchen
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/orders:
get:
summary: 'Open orders (optionally for one table).'
operationId: openOrdersoptionallyForOneTable
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Orders
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
table_id:
type: string
description: 'Le champ value doit être un UUID valide. Must match an existing stored value.'
example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
nullable: true
customer_id:
type: string
description: 'Le champ value doit être un UUID valide. Must match an existing stored value.'
example: 6b72fe4a-5b40-307c-bc24-f79acf9a1bb9
nullable: true
guest_count:
type: integer
description: 'Le champ value doit être au moins 1.'
example: 27
nullable: true
note:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: i
nullable: true
'/api/v1/orders/{order_id}':
get:
summary: ''
operationId: getApiV1OrdersOrder_id
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: order_id
description: 'The ID of the order.'
example: 569e3716-b7cf-4f01-8186-92f2d7e82f7c
required: true
schema:
type: string
'/api/v1/orders/{order_id}/items':
post:
summary: ''
operationId: postApiV1OrdersOrder_idItems
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
product_id:
type: string
description: 'Le champ value doit être un UUID valide.'
example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
nullable: true
variant_id:
type: string
description: 'Le champ value doit être un UUID valide.'
example: 6b72fe4a-5b40-307c-bc24-f79acf9a1bb9
nullable: true
name:
type: string
description: 'Le champ value ne peut pas dépasser 200 caractères.'
example: m
nullable: true
quantity:
type: number
description: ''
example: 4326.41688
unit_price_xaf:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 77
nullable: true
discount_xaf:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 8
nullable: true
item_note:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: 'y'
nullable: true
required:
- quantity
parameters:
-
in: path
name: order_id
description: 'The ID of the order.'
example: 569e3716-b7cf-4f01-8186-92f2d7e82f7c
required: true
schema:
type: string
'/api/v1/orders/{order_id}/items/{item_id}':
patch:
summary: ''
operationId: patchApiV1OrdersOrder_idItemsItem_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
quantity:
type: number
description: 'Le champ value doit être au moins 0.'
example: 27
required:
- quantity
delete:
summary: ''
operationId: deleteApiV1OrdersOrder_idItemsItem_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: order_id
description: 'The ID of the order.'
example: 569e3716-b7cf-4f01-8186-92f2d7e82f7c
required: true
schema:
type: string
-
in: path
name: item_id
description: 'The ID of the item.'
example: 019fbfad-7fa1-73f6-87f1-bf5771424bc1
required: true
schema:
type: string
'/api/v1/orders/{order_id}/fire':
post:
summary: ''
operationId: postApiV1OrdersOrder_idFire
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: order_id
description: 'The ID of the order.'
example: 569e3716-b7cf-4f01-8186-92f2d7e82f7c
required: true
schema:
type: string
'/api/v1/orders/{order_id}/close':
post:
summary: ''
operationId: postApiV1OrdersOrder_idClose
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
redeem_points:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 27
nullable: true
payments:
type: array
description: 'Le champ value doit contenir au moins 1 éléments.'
example:
- []
items:
type: object
properties:
method:
type: string
description: ''
example: architecto
amount_xaf:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 39
reference:
type: string
description: 'Le champ value ne peut pas dépasser 120 caractères.'
example: g
nullable: true
required:
- method
- amount_xaf
required:
- payments
parameters:
-
in: path
name: order_id
description: 'The ID of the order.'
example: 569e3716-b7cf-4f01-8186-92f2d7e82f7c
required: true
schema:
type: string
'/api/v1/order-items/{item_id}/status':
patch:
summary: ''
operationId: patchApiV1OrderItemsItem_idStatus
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
status:
type: string
description: ''
example: served
enum:
- pending
- sent
- ready
- served
- void
required:
- status
parameters:
-
in: path
name: item_id
description: 'The ID of the item.'
example: 019fbfad-7fa1-73f6-87f1-bf5771424bc1
required: true
schema:
type: string
/api/v1/sync/pull:
get:
summary: "Pull records changed since the client's cursor, per entity, paginated."
operationId: pullRecordsChangedSinceTheClientsCursorPerEntityPaginated
description: "Query params:\n - since: ISO-8601 cursor. Omitted on a device's first sync.\n - limit: page size, default DEFAULT_LIMIT, clamped to [1, MAX_LIMIT].\n - include_sales: sales (with items + payments) are only built and\n serialised when this is truthy (\"1\"). This block is by far the most\n expensive part of the response — every sale in the window, each\n eager-loading its line items and payments — and nothing currently\n consumes it: the desktop app is just this same SPA loaded in an\n Electron shell, the client's sync engine (resources/js/lib/sync.js)\n already discards data.sales, and there is no local sales mirror to\n populate. Defaulting it off keeps ordinary pulls (catalogue +\n customers) cheap on memory-constrained shared hosting. When omitted,\n `data.sales` is still present as an empty array and\n `has_more.sales` as false, so the response shape never changes."
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/sync/push:
post:
summary: ''
operationId: postApiV1SyncPush
description: ''
parameters: []
responses: { }
tags:
- Endpoints
/api/v1/sync/conflicts:
get:
summary: ''
operationId: getApiV1SyncConflicts
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
'/api/v1/sync/conflicts/{conflict_id}/resolve':
post:
summary: ''
operationId: postApiV1SyncConflictsConflict_idResolve
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: conflict_id
description: 'The ID of the conflict.'
example: architecto
required: true
schema:
type: string
/api/v1/attendance/me:
get:
summary: "The signed-in user's own current clock status — the self-service widget."
operationId: theSignedInUsersOwnCurrentClockStatusTheSelfServiceWidget
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/attendance/clock-in:
post:
summary: ''
operationId: postApiV1AttendanceClockIn
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
note:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: b
nullable: true
/api/v1/attendance/clock-out:
post:
summary: ''
operationId: postApiV1AttendanceClockOut
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
note:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: b
nullable: true
/api/v1/attendance:
get:
summary: "Owner/manager roster view — every employee's sessions, filterable."
operationId: ownermanagerRosterViewEveryEmployeesSessionsFilterable
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
'/api/v1/attendance/{attendance_id}':
put:
summary: "A manager's correction — e.g. a forgotten clock-out."
operationId: aManagersCorrectionEgAForgottenClockOut
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
clock_in:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:17'
clock_out:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:17'
nullable: true
note:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: b
nullable: true
parameters:
-
in: path
name: attendance_id
description: 'The ID of the attendance.'
example: architecto
required: true
schema:
type: string
/api/v1/audit-logs:
get:
summary: ''
operationId: getApiV1AuditLogs
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/devices:
get:
summary: ''
operationId: getApiV1Devices
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Devices
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Le champ value ne peut pas dépasser 120 caractères.'
example: b
platform:
type: string
description: ''
example: pwa
enum:
- pwa
- desktop
nullable: true
priority:
type: integer
description: 'Le champ value doit être au moins 1.'
example: 22
nullable: true
required:
- name
'/api/v1/devices/{device_id}/regenerate':
post:
summary: ''
operationId: postApiV1DevicesDevice_idRegenerate
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: device_id
description: 'The ID of the device.'
example: 019fc952-e363-7110-95bc-46cb53173445
required: true
schema:
type: string
'/api/v1/devices/{device_id}':
put:
summary: ''
operationId: putApiV1DevicesDevice_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
delete:
summary: ''
operationId: deleteApiV1DevicesDevice_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: device_id
description: 'The ID of the device.'
example: 019fc952-e363-7110-95bc-46cb53173445
required: true
schema:
type: string
/api/v1/device-pairings/claim:
post:
summary: 'An owner, already signed in, scans or types the code to approve it.'
operationId: anOwnerAlreadySignedInScansOrTypesTheCodeToApproveIt
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
code:
type: string
description: 'Le champ value doit contenir 8 caractères.'
example: bngzmiyv
name:
type: string
description: 'Le champ value ne peut pas dépasser 120 caractères.'
example: d
nullable: true
required:
- code
/api/v1/settings:
get:
summary: ''
operationId: getApiV1Settings
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
put:
summary: ''
operationId: putApiV1Settings
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: b
business_type:
type: string
description: ''
example: null
phone:
type: string
description: 'Le champ value ne peut pas dépasser 40 caractères.'
example: 'n'
nullable: true
email:
type: string
description: 'Le champ value doit être une adresse e-mail valide. Le champ value ne peut pas dépasser 160 caractères.'
example: ashly64@example.com
nullable: true
address:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: v
nullable: true
city:
type: string
description: 'Le champ value ne peut pas dépasser 120 caractères.'
example: d
nullable: true
tax_id:
type: string
description: 'Le champ value ne peut pas dépasser 60 caractères.'
example: l
nullable: true
rccm:
type: string
description: 'Le champ value ne peut pas dépasser 60 caractères.'
example: j
nullable: true
vat_rate:
type: number
description: 'Le champ value doit être au moins 0. Le champ value ne peut pas dépasser 100.'
example: 17
locale:
type: string
description: ''
example: en
enum:
- fr
- en
loyalty_enabled:
type: boolean
description: ''
example: false
points_per_currency:
type: number
description: 'Le champ value doit être au moins 0.'
example: 8
currency_per_point:
type: number
description: 'Le champ value doit être au moins 0.'
example: 75
loyalty_points_expiry_months:
type: integer
description: "Null/absent = expiry off. A shop must opt in; existing balances\nmust not start ageing out the moment this ships. Le champ value doit être au moins 1. Le champ value ne peut pas dépasser 60."
example: 14
nullable: true
/api/v1/users:
get:
summary: ''
operationId: getApiV1Users
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Users
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: b
email:
type: string
description: 'Le champ value doit être une adresse e-mail valide.'
example: zbailey@example.net
password:
type: string
description: 'Le champ value doit contenir au moins 8 caractères.'
example: '-0pBNvYgxw'
role:
type: string
description: ''
example: architecto
phone:
type: string
description: 'Le champ value ne peut pas dépasser 40 caractères.'
example: 'n'
nullable: true
required:
- name
- email
- password
- role
'/api/v1/users/{id}':
put:
summary: ''
operationId: putApiV1UsersId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: b
phone:
type: string
description: 'Le champ value ne peut pas dépasser 40 caractères.'
example: 'n'
nullable: true
role:
type: string
description: ''
example: null
is_active:
type: boolean
description: ''
example: true
password:
type: string
description: 'Le champ value doit contenir au moins 8 caractères.'
example: '|{+-0pBNvYgx'
nullable: true
delete:
summary: ''
operationId: deleteApiV1UsersId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: id
description: 'The ID of the user.'
example: architecto
required: true
schema:
type: string
/api/v1/employees:
get:
summary: ''
operationId: getApiV1Employees
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1Employees
description: ''
parameters: []
responses: { }
tags:
- Endpoints
'/api/v1/employees/{id}':
get:
summary: ''
operationId: getApiV1EmployeesId
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
put:
summary: ''
operationId: putApiV1EmployeesId
description: ''
parameters: []
responses: { }
tags:
- Endpoints
delete:
summary: "Soft delete only — a departed employee's payslips and attendance must stay real."
operationId: softDeleteOnlyADepartedEmployeesPayslipsAndAttendanceMustStayReal
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: id
description: 'The ID of the employee.'
example: architecto
required: true
schema:
type: string
/api/v1/payroll-runs:
get:
summary: ''
operationId: getApiV1PayrollRuns
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1PayrollRuns
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
period_start:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:17'
period_end:
type: string
description: 'Le champ value doit être une date valide. Le champ value doit être une date postérieure ou égale à period_start.'
example: '2052-08-27'
required:
- period_start
- period_end
'/api/v1/payroll-runs/{payrollRun_id}':
get:
summary: ''
operationId: getApiV1PayrollRunsPayrollRun_id
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: payrollRun_id
description: 'The ID of the payrollRun.'
example: architecto
required: true
schema:
type: string
'/api/v1/payroll-runs/{payrollRun_id}/finalize':
post:
summary: ''
operationId: postApiV1PayrollRunsPayrollRun_idFinalize
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: payrollRun_id
description: 'The ID of the payrollRun.'
example: architecto
required: true
schema:
type: string
'/api/v1/payroll-runs/{payrollRun_id}/pay':
post:
summary: ''
operationId: postApiV1PayrollRunsPayrollRun_idPay
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
payment_method:
type: string
description: ''
example: card
enum:
- cash
- mtn_momo
- orange_money
- card
- bank
required:
- payment_method
parameters:
-
in: path
name: payrollRun_id
description: 'The ID of the payrollRun.'
example: architecto
required: true
schema:
type: string
'/api/v1/payslips/{payslip_id}':
put:
summary: ''
operationId: putApiV1PayslipsPayslip_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
deductions_xaf:
type: integer
description: 'Le champ value doit être au moins 0.'
example: 27
notes:
type: string
description: 'Le champ value ne peut pas dépasser 255 caractères.'
example: 'n'
nullable: true
required:
- deductions_xaf
parameters:
-
in: path
name: payslip_id
description: 'The ID of the payslip.'
example: architecto
required: true
schema:
type: string
'/api/v1/payslips/{payslip_id}/verify-url':
get:
summary: 'The signed link the client renders as a QR before requesting the sheet — same two-step shape as sale invoices.'
operationId: theSignedLinkTheClientRendersAsAQRBeforeRequestingTheSheetSameTwoStepShapeAsSaleInvoices
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: payslip_id
description: 'The ID of the payslip.'
example: architecto
required: true
schema:
type: string
'/api/v1/payslips/{payslip_id}/print':
get:
summary: "A full, self-contained HTML payslip ready for the browser's print\ndialogue. Same no-PDF-library approach as the sale invoice — the\nbrowser's own @page CSS does the job."
operationId: aFullSelfContainedHTMLPayslipReadyForTheBrowsersPrintDialogueSameNoPDFLibraryApproachAsTheSaleInvoiceTheBrowsersOwnpageCSSDoesTheJob
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: payslip_id
description: 'The ID of the payslip.'
example: architecto
required: true
schema:
type: string
/api/v1/admin/dashboard:
get:
summary: ''
operationId: getApiV1AdminDashboard
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/admin/plans:
get:
summary: ''
operationId: getApiV1AdminPlans
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
post:
summary: ''
operationId: postApiV1AdminPlans
description: ''
parameters: []
responses: { }
tags:
- Endpoints
'/api/v1/admin/plans/{plan_id}':
put:
summary: ''
operationId: putApiV1AdminPlansPlan_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
delete:
summary: ''
operationId: deleteApiV1AdminPlansPlan_id
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: plan_id
description: 'The ID of the plan.'
example: 019fbec2-b453-719c-98f8-fa398f0b6949
required: true
schema:
type: string
/api/v1/admin/tenants:
post:
summary: 'Opesware onboards a shop directly from the console.'
operationId: opeswareOnboardsAShopDirectlyFromTheConsole
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
shop_name:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: b
business_type:
type: string
description: ''
example: architecto
nullable: true
phone:
type: string
description: 'Le champ value ne peut pas dépasser 40 caractères.'
example: 'n'
nullable: true
city:
type: string
description: 'Le champ value ne peut pas dépasser 120 caractères.'
example: g
nullable: true
owner_name:
type: string
description: 'Le champ value ne peut pas dépasser 160 caractères.'
example: z
owner_email:
type: string
description: 'Le champ value doit être une adresse e-mail valide.'
example: rempel.chadrick@example.org
password:
type: string
description: 'Le champ value doit contenir au moins 8 caractères.'
example: 'NvYgxwmi/#iw/kX'
plan_id:
type: string
description: 'Le champ value doit être un UUID valide. Must match an existing stored value.'
example: 7212c28d-f9ab-3dd7-af8a-06584a0d4cb7
nullable: true
required:
- shop_name
- owner_name
- owner_email
- password
get:
summary: ''
operationId: getApiV1AdminTenants
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
'/api/v1/admin/tenants/{id}/users':
get:
summary: ''
operationId: getApiV1AdminTenantsIdUsers
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: id
description: 'The ID of the tenant.'
example: architecto
required: true
schema:
type: string
'/api/v1/admin/tenants/{id}/users/{user}/password':
post:
summary: ''
operationId: postApiV1AdminTenantsIdUsersUserPassword
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
password:
type: string
description: 'Le champ value doit contenir au moins 12 caractères.'
example: '|]|{+-'
required:
- password
parameters:
-
in: path
name: id
description: 'The ID of the tenant.'
example: architecto
required: true
schema:
type: string
-
in: path
name: user
description: 'The user.'
example: architecto
required: true
schema:
type: string
'/api/v1/admin/tenants/{id}/users/{user}/active':
post:
summary: ''
operationId: postApiV1AdminTenantsIdUsersUserActive
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
is_active:
type: boolean
description: ''
example: true
required:
- is_active
parameters:
-
in: path
name: id
description: 'The ID of the tenant.'
example: architecto
required: true
schema:
type: string
-
in: path
name: user
description: 'The user.'
example: architecto
required: true
schema:
type: string
'/api/v1/admin/tenants/{id}':
get:
summary: ''
operationId: getApiV1AdminTenantsId
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
parameters:
-
in: path
name: id
description: 'The ID of the tenant.'
example: architecto
required: true
schema:
type: string
'/api/v1/admin/tenants/{id}/suspend':
post:
summary: ''
operationId: postApiV1AdminTenantsIdSuspend
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: id
description: 'The ID of the tenant.'
example: architecto
required: true
schema:
type: string
'/api/v1/admin/tenants/{id}/activate':
post:
summary: ''
operationId: postApiV1AdminTenantsIdActivate
description: ''
parameters: []
responses: { }
tags:
- Endpoints
parameters:
-
in: path
name: id
description: 'The ID of the tenant.'
example: architecto
required: true
schema:
type: string
'/api/v1/admin/tenants/{id}/plan':
post:
summary: ''
operationId: postApiV1AdminTenantsIdPlan
description: ''
parameters: []
responses: { }
tags:
- Endpoints
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
plan_id:
type: string
description: 'Le champ value doit être un UUID valide. Must match an existing stored value.'
example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
required:
- plan_id
parameters:
-
in: path
name: id
description: 'The ID of the tenant.'
example: architecto
required: true
schema:
type: string
/api/v1/admin/billing:
get:
summary: ''
operationId: getApiV1AdminBilling
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/admin/subscriptions:
get:
summary: ''
operationId: getApiV1AdminSubscriptions
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/admin/audit-logs:
get:
summary: ''
operationId: getApiV1AdminAuditLogs
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
/api/v1/admin/analytics:
get:
summary: ''
operationId: getApiV1AdminAnalytics
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Endpoints
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
from:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:17'
nullable: true
to:
type: string
description: 'Le champ value doit être une date valide.'
example: '2026-08-04T06:23:17'
nullable: true