API
The xPage API lets you read and write resources on a user's hosting. All requests are scoped to a single installation via a restricted token.
Base URL: https://.../api/apps/v1
All endpoints (except auth) require:
Authorization: Bearer <token>
Pagination
List endpoints share a common set of query parameters:
| Param | Default | Description |
|---|---|---|
page | 1 | Page number |
page_size | 20 | Results per page (max 200) |
order_by | resource-specific | Field to sort by (see each endpoint) |
order_direction | DESC | ASC or DESC |
Paginated response shape:
{
"data": {
"<resource>": [...],
"pagination": {
"total": 100,
"per_page": 20,
"current_page": 1,
"last_page": 5
}
}
}
Products
List products
GET /product
Permission: product:view
Query params:
| Param | Description |
|---|---|
search | Full-text search on title, description, slug (min 2 chars) |
title | Exact title match |
status | active or inactive |
is_digital | true or false |
out_of_stock | true returns products with no stock; false returns products with stock |
category | Filter by category ID |
order_by | title, category, created_at |
Response:
{
"data": {
"products": [
{
"id": "uuid",
"slug": "string",
"title": "string",
"description": "string|null",
"page_title": "string|null",
"meta_description": "string|null",
"is_digital": false,
"status": "active|inactive",
"stock_tracked": true,
"allow_order_when_oos": false,
"created_at": "2024-01-01T00:00:00Z",
"image": { "...file" },
"images": [{ "...file" }],
"ugc_videos": [{ "...file" }],
"options": [{ "...option" }],
"variants": [{ "...variant" }],
"category": { "id": "uuid", "name": "string" },
"default": { "...variant" },
"properties": [{ "id": "uuid", "name": "string", "value": "string" }]
}
],
"pagination": { "..." }
}
}
Get product
GET /product/{id}
Permission: product:view
Response:
{
"data": {
"product": { "...product" }
}
}
Create product
POST /product
Permission: product:create
Request must be multipart/form-data. Product fields are sent as a JSON string in the metadata field. Image and file uploads are sent as separate multipart parts.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
metadata | JSON string | Yes | Product data (see structure below) |
images[] | file | No | New image files to upload (jpeg, png, webp, gif — max 5 MB each) |
files[] | file | No | Digital product files (pdf, mp3, mp4, zip, etc.) |
metadata structure:
{
"title": "string",
"slug": "string",
"description": "string|null",
"page_title": "string|null",
"meta_description": "string|null",
"status": "active|inactive",
"is_digital": false,
"category_id": "uuid|null",
"images": [
{ "id": "uuid", "order": 0 }
],
"options": [
{
"order": 0,
"name": "string",
"values": [
{ "order": 0, "value": "string" }
]
}
],
"variants": [
{
"price": 29.99,
"compare_price": 39.99,
"sku": "string|null",
"quantity": 100,
"image": "file_uuid|null",
"file": "file_uuid|null",
"values": [{ "order": 0, "value": "string" }]
}
],
"default": {
"price": 29.99,
"compare_price": 39.99,
"sku": "string|null",
"quantity": 100,
"file": "file_uuid|null"
},
"properties": [
{ "name": "string", "value": "string" }
],
"ugc_videos": [
{ "id": "file_uuid", "order": 0 }
]
}
images and ugc_videos reference file UUIDs from previously uploaded files (via POST /file). The variants.*.image and variants.*.file fields also reference file UUIDs. Use metadata.default for simple products with no variants, or metadata.variants for products with options.
Response:
{
"data": {
"product": { "...product" }
}
}
Update product
POST /product/{id}/update
Permission: product:update
Same request structure as create. All fields are optional — only provided fields are updated.
For options and variants, pass the existing id field to update an item, or omit it to create a new one.
Response:
{
"data": {
"product": { "...product" }
}
}
Orders
List orders
GET /order
Permission: order:view
Query params:
| Param | Description |
|---|---|
search | Search by order ID, ref, customer name, email, phone, address |
ref | Exact match on order reference number |
status | pending, paid, or abandoned |
delivery_status | unfulfilled, fulfilled, shipped, delivered, or cancelled. Comma-separated or array for multiple |
return_status | Filter by return delivery status (same values as delivery_status) |
refund_status | pending or refunded. Comma-separated or array for multiple |
exclude_delivery_status | Exclude orders with this delivery status |
exclude_return_status | Exclude orders with this return status |
exclude_refund_status | Exclude orders with this refund status |
customer_id | Filter by customer UUID |
product_id | Filter by product UUID (comma-separated or array for multiple) |
landing_page_id | Filter by landing page UUID |
is_cancelled | true or false |
is_archived | true or false |
from | Created at — ISO date or Unix timestamp lower bound |
to | Created at — ISO date or Unix timestamp upper bound |
updated_from | Updated at lower bound |
updated_to | Updated at upper bound |
order_by | total, quantity, date, status |
Response:
{
"data": {
"orders": [
{
"id": "uuid",
"ref": "string",
"status": "pending|paid|abandoned",
"currency": "string",
"customer_first_name": "string",
"customer_last_name": "string",
"customer_name": "string",
"customer_email": "string",
"customer_phone": "string",
"customer_address": "string",
"customer_city": "string",
"customer_state": "string",
"customer_postal_code": "string",
"customer_id": "uuid|null",
"customer_country": { "...country" },
"billing_first_name": "string",
"billing_last_name": "string",
"billing_name": "string",
"billing_email": "string",
"billing_phone": "string",
"billing_address": "string",
"billing_city": "string",
"billing_state": "string",
"billing_postal_code": "string",
"card_brand": "string|null",
"card_last4": "string|null",
"shipping_rate": 5.00,
"shipping_name": "string|null",
"discount": 10,
"discount_code": "string|null",
"tip": 0,
"tip_type": "string|null",
"tip_value": 0,
"bundle_name": "string|null",
"bundle_option_name": "string|null",
"bundle_price": null,
"fee": 0,
"subtotal": 29.99,
"total": 34.99,
"quantity": 1,
"is_archived": false,
"is_cancelled": false,
"cancelled_at": "datetime|null",
"paid_at": "datetime|null",
"created_at": "datetime",
"meta_data": {},
"payment_method": {
"id": "uuid",
"name": "string",
"status": "string",
"payment_types": [],
"connected": true
},
"variants": [
{
"id": "uuid",
"quantity": 1,
"price": 29.99,
"sku": "string|null",
"title": "string",
"is_digital": false,
"options": {},
"variant": { "...variant" },
"image_url": "string|null"
}
],
"deliveries": [
{
"id": "uuid",
"status": "unfulfilled|fulfilled|shipped|delivered|cancelled",
"tracking_number": "string|null",
"tracking_link": "string|null",
"shipping_carrier": { "id": "uuid", "name": "string", "code": "string", "status": "string" },
"order_variants": [{ "...order_variant" }]
}
],
"returns": [
{
"id": "uuid",
"order_id": "uuid",
"reason": "string|null",
"return_variants": [{ "..." }],
"delivery": { "...delivery" }
}
],
"refunds": [
{
"id": "uuid",
"order_id": "uuid",
"status": "pending|refunded",
"amount": 29.99,
"reason": "string|null",
"return": { "...return" },
"refund_variants": [{ "..." }]
}
],
"landing_page": { "...landing_page" },
"source_link": "string|null",
"checkout_link": "string",
"thank_you_link": "string"
}
],
"pagination": { "..." }
}
}
Get order
GET /order/{id}
Permission: order:view
Response:
{
"data": {
"order": { "...order" }
}
}
Create order
POST /order
Permission: order:create
Used to sync orders created outside of xPage (e.g. from an external checkout).
Body:
| Field | Type | Required | Description |
|---|---|---|---|
status | string | No | pending, paid, or abandoned |
customer_first_name | string | No | |
customer_last_name | string | No | |
customer_email | string | No | |
customer_phone | string | No | |
customer_address | string | No | |
customer_city | string | No | |
customer_state | string | No | |
customer_postal_code | string | No | |
billing_first_name | string | No | |
billing_last_name | string | No | |
billing_email | string | No | |
billing_phone | string | No | |
billing_address | string | No | |
billing_city | string | No | |
billing_state | string | No | |
billing_postal_code | string | No | |
country_id | string | No | Country ID |
is_archived | boolean | No | |
is_cancelled | boolean | No |
Response:
{
"data": {
"order": { "...order" }
}
}
Update delivery
PUT /order/delivery/{deliveryId}
Permission: order:update
Body:
| Field | Type | Required | Description |
|---|---|---|---|
status | string | No | unfulfilled, fulfilled, shipped, delivered, or cancelled |
tracking_number | string | No | Carrier tracking number |
shipping_carrier | string | No | Shipping carrier ID (required if tracking_number is set) |
Response:
{
"data": {
"delivery": { "...delivery" }
}
}
Export orders
GET /order/export
Permission: order:export
Accepts the same filter params as GET /order. Returns a downloadable CSV file.
| Param | Description |
|---|---|
format | Export format — currently csv (default) |
Response is a file download (Content-Type: text/csv), not JSON.
Customers
List customers
GET /customer
Permission: customer:view
Query params:
| Param | Description |
|---|---|
search | Full-text search on name, email, phone, address, city, postal code |
country | Filter by country ID |
order_by | amount_spent, order_count, name, email, phone, address, city, postal_code, created_at, subscription_status |
Response:
{
"data": {
"customers": [
{
"id": "uuid",
"name": "string",
"email": "string",
"phone": "string|null",
"address": "string|null",
"city": "string|null",
"country_id": "string|null",
"postal_code": "string|null",
"created_at": "datetime",
"updated_at": "datetime",
"country": { "...country" },
"order_count": 3,
"amount_spent": 89.97,
"latest_order": { "...order" },
"subscription_status": "subscribed|not_subscribed"
}
],
"pagination": { "..." }
}
}
Get customer
GET /customer/{id}
Permission: customer:view
Response:
{
"data": {
"customer": { "...customer" }
}
}
Create customer
POST /customer
Permission: customer:create
Body:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | |
name | string | No | |
phone | string | No | |
address | string | No | |
city | string | No | |
postal_code | string | No | |
country_id | string | No | Country ID |
subscription_status | string | No | subscribed or not_subscribed |
Response:
{
"data": {
"customer": { "...customer" }
}
}
Reviews
List reviews
GET /review
Permission: review:view
Query params:
| Param | Description |
|---|---|
product_id | Filter by product UUID |
status | pending, hidden, or published |
rating | Minimum rating (integer 1–5) |
order_by | product_id, rating, created_at |
Response:
{
"data": {
"reviews": [
{
"id": "uuid",
"content": "string",
"status": "pending|hidden|published",
"rating": 5,
"reviewer_name": "string",
"reviewer_email": "string|null",
"created_at": "datetime",
"images": [{ "...file" }],
"product": { "...product" }
}
],
"pagination": { "..." }
}
}
Get review
GET /review/{id}
Permission: review:view
Response:
{
"data": {
"review": { "...review" }
}
}
Create review
POST /review
Permission: review:create
Request must be multipart/form-data.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
metadata | JSON string | Yes | Review data (see structure below) |
images[] | file | No | Image files to upload (max 5 MB each) |
metadata structure:
{
"product_id": "uuid",
"content": "string",
"rating": 5,
"reviewer_name": "string",
"reviewer_email": "string",
"status": "pending|hidden|published",
"image_ids": ["uuid"]
}
| Field | Required | Description |
|---|---|---|
product_id | Yes | UUID of the product being reviewed |
content | Yes | Review text |
rating | Yes | Integer 1–5 |
reviewer_name | Yes | |
reviewer_email | No | |
status | No | Defaults to pending |
image_ids | No | UUIDs of previously uploaded files to attach |
Response:
{
"data": {
"review": { "...review" }
}
}
Landing pages
List landing pages
GET /landing-page
Permission: landing_page:view
Query params:
| Param | Description |
|---|---|
product_id | Filter by product UUID (comma-separated for multiple) |
domain_id | Filter by domain UUID |
cod_type | checkout, popup, or embedded |
order_by | created_at, updated_at |
Response:
{
"data": {
"landingPages": [
{
"id": "uuid",
"title": "string",
"slug": "string",
"meta_description": "string|null",
"product_id": "uuid",
"domain_id": "uuid|null",
"created_at": "datetime",
"updated_at": "datetime",
"product": { "...product" },
"url": "string",
"screenshot_url": "string|null",
"lang": "string|null",
"cod_type": "checkout|popup|embedded"
}
],
"pagination": { "..." }
}
}
Get landing page
GET /landing-page/{id}
Permission: landing_page:view
Response:
{
"data": {
"landing_page": { "...landing_page" }
}
}
Shipping carriers
List carriers
GET /shipping-carrier
No permission required. Returns active shipping carriers available on the platform.
Response:
{
"data": {
"carriers": [
{
"id": "uuid",
"name": "string",
"code": "string",
"status": "string"
}
]
}
}
Files
List files
GET /file
Query params:
| Param | Description |
|---|---|
fileable_type | product, variant, landing_page, review |
fileable_id | UUID of the associated resource |
mime_type | Exact MIME type or pattern with wildcard (e.g. image/*). Comma-separated for multiple |
from | Created at lower bound |
to | Created at upper bound |
order_by | created_at |
Response:
{
"data": {
"files": [
{
"id": "uuid",
"name": "string",
"size": 204800,
"alt": "string",
"mime_type": "image/webp",
"created_at": "datetime",
"url": "string",
"permanent_url": "string",
"thumbnail": "string|null"
}
],
"pagination": { "..." }
}
}
Get file
GET /file/{id}
Response:
{
"data": {
"file": { "...file" }
}
}
Upload file
POST /file
Request must be multipart/form-data.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | The file to upload |
fileable_type | string | No (required with fileable_id) | product, variant, landing_page, review |
fileable_id | string | No (required with fileable_type) | UUID of the resource to link the file to |
role | string | No | product_images, variant_image, digital_product, landing_page_images, review_images, ugc_video |
order | integer | No | Sort order when linked to a resource |
alt | string | No | Alt text |
Response:
{
"data": {
"file": { "...file" }
}
}
Delete file
POST /file/{id}
Deletes the file and unlinks it from all associated resources.
Response:
{
"data": {}
}
Unlink file
DELETE /file/{id}/link/{fileable_type}/{fileable_id}
Removes the association between a file and a resource without deleting the file itself.
| Path param | Description |
|---|---|
fileable_type | product, variant, landing_page, review |
fileable_id | UUID of the resource to unlink from |
Response:
{
"data": {
"file": { "...file" }
}
}
Permissions
List available permissions
GET /permission
Returns all permission subjects and their available actions. No authentication required.
Response:
{
"data": {
"permissions": {
"product": ["view", "create", "update", "delete"],
"order": ["view", "create", "update", "delete", "export"],
"customer": ["view", "create", "update", "delete"],
"review": ["view", "create", "update", "delete"],
"landing_page": ["view", "create", "update", "delete", "publish"],
"shipping": ["view", "create", "update", "delete"],
"setting": ["view", "update"],
"payment_method": ["view", "update"],
"analytic": ["view"],
"domain": ["view", "create", "update", "delete"],
"bundle": ["view", "create", "update", "delete"],
"policy": ["view", "create", "update", "delete"],
"plan": ["switch"],
"invoice": ["view", "pay"],
"file": ["browse", "upload", "delete"],
"app": ["install", "uninstall", "boot"]
}
}
}
If a required permission is not granted to your app, the API returns 403 Forbidden.
Response format
Success:
{
"data": { "..." }
}
Error:
{
"code": "ERROR_CODE",
"message": "Human-readable description",
"errors": {
"field": ["validation message"]
}
}