API Overview
API Overview — v1
Interactive API Explorer: Test the Apps API, Public API, and Webhooks live at api.selorax.io/api/docs. Download the OpenAPI spec (JSON) or YAML to import into Postman via File > Import, or use it to generate client SDKs.
API Version
You are reading the documentation for API v1 — the current and only
supported version. All endpoints live under the /api/apps/v1/ namespace. See
the Changelog for recent changes and the Migration
Guide for versioning policy.
The SeloraX Platform API gives apps programmatic access to store data -- orders, products, customers, inventory, billing, and more. All endpoints live under the /api/apps/v1/ namespace and require app authentication.
Base URLs
| Environment | Base URL |
|---|---|
| Production | https://api.selorax.io |
| Development | https://api-dev.selorax.io |
All paths in this reference are relative to the base URL. For example, GET /api/apps/v1/orders in production resolves to:
https://api.selorax.io/api/apps/v1/orders
Authentication
Every API request must include authentication credentials. The platform supports two methods:
Bearer Token
Pass a session token obtained via the OAuth flow in the Authorization header:
Authorization: Bearer <token>
Client Credentials (Server-to-Server)
For background/server-side requests, pass your app's credentials directly:
X-Client-Id: sx_app_...
X-Client-Secret: sx_secret_...
X-Store-Id: 22
Both methods resolve to the same auth context. See the Authentication guide for details on obtaining credentials.
Scopes
Each endpoint requires a specific OAuth scope. If your app does not have the required scope, the API returns a 403 insufficient_scope error. Common scopes include:
| Scope | Read Access | Write Access |
|---|---|---|
read:orders / write:orders | List, get orders | Create, update orders, change status |
read:products / write:products | List, get products | Create, update, delete products |
read:customers / write:customers | List, get customers | Update customer info |
read:inventory / write:inventory | Read stock levels | Adjust stock |
read:discounts / write:discounts | List, get discounts | Create, update, delete discounts |
read:store | Read store information | — |
read:analytics / write:analytics | Read analytics data | — |
read:metafields / write:metafields | Read custom fields | Create, update, delete metafields |
billing | — | Create charges, manage subscriptions, wallet |
Response Format
All responses return JSON with a consistent envelope.
Success
{
"data": { ... },
"status": 200
}List endpoints also include a pagination object. Some endpoints include a message field.
Error
{
"message": "Invalid client credentials",
"code": "invalid_client",
"status": 401
}The HTTP status code on the response always matches the status field in the body.
Rate Limits
| Scope | Limit |
|---|---|
Global (all /api/ routes) | 1,000 requests / minute |
| Custom app credential endpoints | 10 requests / 15 minutes |
/oauth/token | 10 requests / minute |
/session/verify | 300 requests / minute |
Rate Limit Headers
Every response includes standard rate limit headers:
| Header | Description |
|---|---|
RateLimit-Limit | Maximum requests allowed in the current window |
RateLimit-Remaining | Requests remaining in the current window |
RateLimit-Reset | Seconds until the rate limit window resets |
When you exceed a rate limit the API returns HTTP 429 Too Many Requests. Use the RateLimit-Reset header to determine when to retry:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);
if (response.status !== 429) return response;
const resetAfter = response.headers.get("RateLimit-Reset") || 60;
console.log(`Rate limited. Retrying in ${resetAfter}s...`);
await new Promise((r) => setTimeout(r, resetAfter * 1000));
}
throw new Error("Rate limit exceeded after retries");
}Pagination
List endpoints support offset-based pagination via query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number (1-indexed) |
limit | integer | 50 | Items per page (max varies by endpoint) |
Example:
GET /api/apps/v1/orders?page=2&limit=50
Paginated responses include a pagination object alongside the data array:
{
"data": [...],
"pagination": {
"page": 2,
"limit": 50,
"total": 120
},
"status": 200
}Use the total field to calculate the number of available pages.
Store Scoping
All requests are automatically scoped to the store_id derived from the authentication context. You cannot access data belonging to other stores. There is no need to pass store_id as a query parameter on data-read endpoints -- it is inferred from your credentials.
Error Codes
The following error codes may appear in the code field of error responses. For a full reference with resolution steps, see the Error Codes page.
| Code | HTTP Status | Meaning |
|---|---|---|
missing_token | 401 | No Authorization header or client credentials provided |
invalid_token | 401 | Token is expired, malformed, or revoked |
invalid_client | 401 | Invalid client_id or client_secret |
missing_auth | 401 | App authentication required (scope middleware) |
missing_store_id | 400 | X-Store-Id header required when using client credentials |
not_installed | 401 | App is not installed for the specified store |
store_inactive | 403 | Store is suspended or frozen |
insufficient_scope | 403 | Your app lacks the required scope for this endpoint |
invalid_amount | 400 | Charge amount is outside the allowed min/max range |
invalid_installation | 400 | App installation is inactive or not found |
charge_not_found | 404 | The specified charge does not exist |
insufficient_balance | 400 | Store wallet balance is too low for the requested debit |
duplicate_name | 409 | Custom app with this name already exists for the same creator |
auth_error | 500 | Internal error during authentication |
Example Error Response
curl -X GET https://api.selorax.io/api/apps/v1/orders \
-H "Authorization: Bearer expired_token_here"{
"message": "Token is expired or invalid",
"code": "invalid_token",
"status": 401
}Content Type
All request bodies must be sent as application/json. All responses are returned as application/json.
curl -X POST https://api.selorax.io/api/apps/v1/billing/charges \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{ "name": "Pro Plan", "amount": 500.00 }'