OpenAPI
OpenAPI 3.0 specification for the eCommerce Exchange API. Import the raw YAML file into Postman, Insomnia, or your codegen tool.
openapi: 3.0.3
info:
title: eCommerce Exchange API
version: 1.0
description: |
HTTPS JSON API for catalog, cart, checkout, shopper accounts, and partner orders.
MojoShop is the reference storefront. Institutions can whitelabel against this same `/api/v1` contract. Public catalog payloads never include vendor identity.
Required on JSON writes: `Content-Type: application/json` and `Accept: application/json`.
Successful payloads wrap in `data`. Paginated lists also include Laravel `links` and `meta`.
**Auth:** shoppers use `Authorization: Bearer {token}` from register/login (Sanctum token named `storefront`). Partners use `X-Api-Key`. Carts send `X-Cart-Token` (also returned on cart responses).
**Idempotency:** checkout and partner orders accept optional `idempotency_key` in the JSON body. Reuse the same key only when retrying the same request.
**Vendor webhooks** are configured in `/vendor` (Webhooks). There is no public API to register URLs. Outbound events `order.paid`, `fulfillment.packed`, `fulfillment.shipped`, and `fulfillment.delivered` are signed with HMAC-SHA256 of `{timestamp}.{raw_body}` using headers `X-MojoShop-Event`, `X-MojoShop-Timestamp`, and `X-MojoShop-Signature`.
Rate limits: 60/min default; tighter on register, login, password reset, checkout, and partners. Over the limit returns HTTP 429 with `Retry-After`.
servers:
- url: "https://ecom.mojolabx.com"
description: Current application (APP_URL)
tags:
- name: Catalog
- name: Cart
- name: Checkout
- name: Orders
- name: Account
- name: Partners
paths:
/api/v1/products:
get:
tags:
- Catalog
summary: List published products
operationId: listProducts
parameters:
- name: q
in: query
schema:
type: string
maxLength: 200
- name: category_id
in: query
schema:
type: integer
- name: min_price
in: query
schema:
type: number
minimum: 0
- name: max_price
in: query
schema:
type: number
minimum: 0
- name: in_stock
in: query
schema:
type: boolean
- name: on_sale
in: query
schema:
type: boolean
- name: min_rating
in: query
schema:
type: number
minimum: 0
maximum: 5
- name: sort
in: query
schema:
type: string
enum:
- newest
- popular
- rating
- price_asc
- price_desc
- name
- name: per_page
in: query
schema:
type: integer
minimum: 1
maximum: 48
default: 24
- name: page
in: query
schema:
type: integer
minimum: 1
responses:
"200":
description: Paginated products
content:
application/json:
schema:
"$ref": "#/components/schemas/ProductList"
"422":
description: Validation failed
content:
application/json:
schema:
"$ref": "#/components/schemas/ValidationError"
"/api/v1/products/{id}":
get:
tags:
- Catalog
summary: Product detail
operationId: getProduct
parameters:
- "$ref": "#/components/parameters/ProductId"
responses:
"200":
description: Product
content:
application/json:
schema:
type: object
properties:
data:
"$ref": "#/components/schemas/Product"
"404":
description: Not found
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"/api/v1/products/{id}/related":
get:
tags:
- Catalog
summary: Related products
operationId: listRelatedProducts
parameters:
- "$ref": "#/components/parameters/ProductId"
responses:
"200":
description: Related products
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
"$ref": "#/components/schemas/Product"
"404":
description: Not found
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"/api/v1/products/{id}/reviews":
get:
tags:
- Catalog
summary: List reviews
operationId: listProductReviews
parameters:
- "$ref": "#/components/parameters/ProductId"
- name: page
in: query
schema:
type: integer
minimum: 1
responses:
"200":
description: Paginated reviews
"404":
description: Not found
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
post:
tags:
- Catalog
summary: Leave a review
operationId: createProductReview
security:
- bearerAuth: []
parameters:
- "$ref": "#/components/parameters/ProductId"
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- rating
properties:
rating:
type: integer
minimum: 1
maximum: 5
body:
type: string
maxLength: 1000
nullable: true
responses:
"201":
description: Review created
"401":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"403":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"404":
description: Not found
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"422":
description: Validation failed
content:
application/json:
schema:
"$ref": "#/components/schemas/ValidationError"
/api/v1/categories:
get:
tags:
- Catalog
summary: Active categories
operationId: listCategories
responses:
"200":
description: Categories
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
"$ref": "#/components/schemas/Category"
/api/v1/flash-banner:
get:
tags:
- Catalog
summary: Promo banner slides
operationId: getFlashBanner
responses:
"200":
description: Banner payload
/api/v1/cart:
get:
tags:
- Cart
summary: Get or create cart
operationId: getCart
security:
- cartToken: []
- bearerAuth: []
- cartToken: []
bearerAuth: []
responses:
"200":
description: Cart
headers:
X-Cart-Token:
schema:
type: string
content:
application/json:
schema:
"$ref": "#/components/schemas/CartResponse"
/api/v1/cart/items:
post:
tags:
- Cart
summary: Add cart item
operationId: addCartItem
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- product_variant_id
- quantity
properties:
product_variant_id:
type: integer
quantity:
type: integer
minimum: 1
responses:
"200":
description: Updated cart
headers:
X-Cart-Token:
schema:
type: string
"422":
description: Validation failed
content:
application/json:
schema:
"$ref": "#/components/schemas/ValidationError"
"/api/v1/cart/items/{id}":
patch:
tags:
- Cart
summary: Update cart item
operationId: updateCartItem
parameters:
- "$ref": "#/components/parameters/CartItemId"
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- quantity
properties:
quantity:
type: integer
minimum: 1
responses:
"200":
description: Updated cart
"404":
description: Not found
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"422":
description: Validation failed
content:
application/json:
schema:
"$ref": "#/components/schemas/ValidationError"
delete:
tags:
- Cart
summary: Remove cart item
operationId: removeCartItem
parameters:
- "$ref": "#/components/parameters/CartItemId"
responses:
"200":
description: Updated cart
"404":
description: Not found
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
/api/v1/checkout:
post:
tags:
- Checkout
summary: Checkout cart
operationId: checkout
requestBody:
required: true
content:
application/json:
schema:
"$ref": "#/components/schemas/CheckoutRequest"
responses:
"201":
description: Order created
"200":
description: Idempotent replay
"422":
description: Validation failed
content:
application/json:
schema:
"$ref": "#/components/schemas/ValidationError"
"502":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"503":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"/api/v1/orders/{number}":
get:
tags:
- Orders
summary: Guest order lookup
operationId: getOrder
parameters:
- "$ref": "#/components/parameters/OrderNumber"
- name: email
in: query
required: true
schema:
type: string
format: email
responses:
"200":
description: Order
"404":
description: Not found
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"422":
description: Validation failed
content:
application/json:
schema:
"$ref": "#/components/schemas/ValidationError"
/api/v1/auth/register:
post:
tags:
- Account
summary: Register shopper
operationId: registerCustomer
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- name
- email
- phone
- password
- password_confirmation
properties:
name:
type: string
email:
type: string
format: email
phone:
type: string
password:
type: string
minLength: 8
password_confirmation:
type: string
responses:
"201":
description: Registered
"422":
description: Validation failed
content:
application/json:
schema:
"$ref": "#/components/schemas/ValidationError"
/api/v1/auth/login:
post:
tags:
- Account
summary: Shopper login
operationId: loginCustomer
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- email
- password
properties:
email:
type: string
format: email
password:
type: string
responses:
"200":
description: Token issued
"422":
description: Validation failed
content:
application/json:
schema:
"$ref": "#/components/schemas/ValidationError"
/api/v1/auth/verify:
post:
tags:
- Account
summary: Verify email or SMS OTP
operationId: verifyCustomer
security:
- bearerAuth: []
requestBody:
content:
application/json:
schema:
type: object
properties:
email_code:
type: string
phone_code:
type: string
responses:
"200":
description: Verified
"401":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"403":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"422":
description: Validation failed
content:
application/json:
schema:
"$ref": "#/components/schemas/ValidationError"
/api/v1/auth/resend-verification:
post:
tags:
- Account
summary: Resend OTP
operationId: resendCustomerVerification
security:
- bearerAuth: []
requestBody:
content:
application/json:
schema:
type: object
properties:
channel:
type: string
enum:
- email
- phone
- both
responses:
"200":
description: Sent
"401":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"403":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
/api/v1/auth/forgot-password:
post:
tags:
- Account
summary: Request password reset
operationId: forgotPassword
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- email
properties:
email:
type: string
format: email
responses:
"200":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
/api/v1/auth/reset-password:
post:
tags:
- Account
summary: Reset password
operationId: resetPassword
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- email
- token
- password
- password_confirmation
properties:
email:
type: string
format: email
token:
type: string
password:
type: string
minLength: 8
password_confirmation:
type: string
responses:
"200":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"422":
description: Validation failed
content:
application/json:
schema:
"$ref": "#/components/schemas/ValidationError"
/api/v1/auth/logout:
post:
tags:
- Account
summary: Revoke storefront token
operationId: logoutCustomer
security:
- bearerAuth: []
responses:
"200":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"401":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
/api/v1/auth/me:
get:
tags:
- Account
summary: Current shopper
operationId: getMe
security:
- bearerAuth: []
responses:
"200":
description: Profile
"401":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"403":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
patch:
tags:
- Account
summary: Update shopper profile
operationId: updateMe
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- name
- email
properties:
name:
type: string
email:
type: string
format: email
phone:
type: string
nullable: true
shipping_address:
nullable: true
allOf:
- "$ref": "#/components/schemas/ShippingAddress"
responses:
"200":
description: Updated profile
"401":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"403":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"422":
description: Validation failed
content:
application/json:
schema:
"$ref": "#/components/schemas/ValidationError"
/api/v1/account/orders:
get:
tags:
- Account
summary: Shopper order history
operationId: listAccountOrders
security:
- bearerAuth: []
parameters:
- name: page
in: query
schema:
type: integer
minimum: 1
responses:
"200":
description: Paginated orders
"401":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"403":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
/api/v1/external/orders:
post:
tags:
- Partners
summary: Create externally paid order
operationId: createExternalOrder
security:
- apiKey: []
requestBody:
required: true
content:
application/json:
schema:
"$ref": "#/components/schemas/ExternalOrderRequest"
responses:
"201":
description: Paid order created
"200":
description: Idempotent replay
"401":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"403":
description: Error
content:
application/json:
schema:
"$ref": "#/components/schemas/Message"
"422":
description: Validation failed
content:
application/json:
schema:
"$ref": "#/components/schemas/ValidationError"
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: Sanctum
description: Shopper token from POST /api/v1/auth/login or /auth/register.
apiKey:
type: apiKey
in: header
name: X-Api-Key
description: Partner API key issued in /admin.
cartToken:
type: apiKey
in: header
name: X-Cart-Token
description: Guest or account cart token. Created on first cart call if omitted.
schemas:
Message:
type: object
properties:
message:
type: string
ValidationError:
type: object
properties:
message:
type: string
errors:
type: object
additionalProperties:
type: array
items:
type: string
Category:
type: object
properties:
id:
type: integer
parent_id:
type: integer
nullable: true
name:
type: string
slug:
type: string
description:
type: string
nullable: true
sort_order:
type: integer
Product:
type: object
description: Public product. Never includes vendor identity or unit_cost.
properties:
id:
type: integer
category_id:
type: integer
nullable: true
name:
type: string
slug:
type: string
description:
type: string
nullable: true
brand:
type: string
nullable: true
manufacturer:
type: string
nullable: true
status:
type: string
image_path:
type: string
nullable: true
from_price:
type: string
nullable: true
compare_at_price:
type: string
nullable: true
available_quantity:
type: integer
rating_avg:
type: number
rating_count:
type: integer
sold_count:
type: integer
discount_percent:
type: integer
nullable: true
published_at:
type: string
format: date-time
nullable: true
category:
type: object
nullable: true
properties:
id:
type: integer
name:
type: string
slug:
type: string
parent:
type: object
nullable: true
properties:
id:
type: integer
name:
type: string
slug:
type: string
variants:
type: array
items:
"$ref": "#/components/schemas/ProductVariant"
ProductVariant:
type: object
description: Public variant. Never includes unit_cost.
properties:
id:
type: integer
sku:
type: string
name:
type: string
price:
type: string
compare_at_price:
type: string
nullable: true
weight_kg:
type: string
nullable: true
unit_of_measure:
type: string
nullable: true
dimensions:
type: string
nullable: true
options:
type: object
nullable: true
is_active:
type: boolean
available_quantity:
type: integer
ProductList:
type: object
properties:
data:
type: array
items:
"$ref": "#/components/schemas/Product"
links:
type: object
meta:
type: object
CartResponse:
type: object
properties:
data:
type: object
properties:
token:
type: string
currency:
type: string
items:
type: array
meta:
type: object
properties:
cart_token:
type: string
ShippingAddress:
type: object
required:
- line1
- city
- country
properties:
line1:
type: string
line2:
type: string
nullable: true
city:
type: string
region:
type: string
description: Required for GH. GhanaRegion enum value such as greater_accra.
postal_code:
type: string
nullable: true
description: GhanaPost GPS code when available.
country:
type: string
minLength: 2
maxLength: 2
CheckoutRequest:
type: object
required:
- customer_name
- customer_email
- shipping_address
- accept_terms
properties:
customer_name:
type: string
customer_email:
type: string
format: email
customer_phone:
type: string
nullable: true
shipping_address:
"$ref": "#/components/schemas/ShippingAddress"
customer_notes:
type: string
maxLength: 1000
nullable: true
accept_terms:
type: boolean
idempotency_key:
type: string
maxLength: 255
nullable: true
ExternalOrderRequest:
type: object
required:
- cart_token
- customer_name
- customer_email
- shipping_address
- external_reference
properties:
cart_token:
type: string
customer_name:
type: string
customer_email:
type: string
format: email
customer_phone:
type: string
nullable: true
shipping_address:
"$ref": "#/components/schemas/ShippingAddress"
external_reference:
type: string
idempotency_key:
type: string
maxLength: 255
nullable: true
parameters:
ProductId:
name: id
in: path
required: true
schema:
type: integer
CartItemId:
name: id
in: path
required: true
schema:
type: integer
OrderNumber:
name: number
in: path
required: true
schema:
type: string