Linking Your App to SeloraX
This guide walks through the complete process of building an app, registering it on the SeloraX platform, and connecting it to merchant stores. Whether you are building a public marketplace app or a private integration, this guide covers every step.
Choose Your Integration Type
Before you start, decide which integration model fits your use case:
Option A: Custom App (Private Integration)
Best for: connecting your own store to external tools, building internal automations, or testing the API.
- Created from the merchant dashboard (no admin involvement)
- Auto-installed to your store
- Get API credentials immediately
- No OAuth flow, no iframe, no marketplace listing
Follow the Custom Apps guide for this path.
Option B: Platform App (Marketplace App)
Best for: building an app that any merchant can install from the marketplace.
- Registered by the platform admin
- Uses OAuth 2.0 for installation
- Can be embedded in the merchant dashboard as an iframe
- Supports billing (one-time, recurring, usage-based)
- Listed in the app marketplace
Continue reading this guide for the platform app path.
Building a Platform App
1. Plan Your App
Define what your app does and what data it needs:
| Decision | Options |
|---|---|
| Data access | Which scopes? (read:orders, write:products, etc.) |
| Real-time events | Which webhook topics? (order.created, product.updated, etc.) |
| User interface | Embedded iframe in dashboard? Or headless/backend only? |
| Monetization | Free? One-time charge? Subscription? Usage-based? |
| Architecture | Your app needs a backend server for OAuth and API calls |
2. Register Your App
App registration is handled by the SeloraX platform admin. Provide the following:
{
"name": "My App",
"app_url": "https://my-app.example.com",
"webhook_url": "https://my-app.example.com/api/oauth",
"redirect_urls": ["https://my-app.example.com/oauth/callback"],
"requested_scopes": ["read:orders", "read:products"],
"webhook_topics": ["order.created", "order.status_changed"],
"developer_name": "Your Name",
"developer_email": "[email protected]",
"pricing_type": "free"
}You receive three credentials (shown only once):
| Credential | Format | Purpose |
|---|---|---|
| Client ID | sx_app_<32hex> | Identifies your app in OAuth and API calls |
| Client Secret | sx_secret_<64hex> | Authenticates your app (keep secret) |
| Session Signing Key | <64hex> | Verifies iframe session tokens |
See Creating an App for the full list of configuration fields.
3. Implement the OAuth Install Flow
When a merchant clicks "Install" in the marketplace, SeloraX starts the OAuth 2.0 authorization code flow:
Merchant clicks "Install"
│
▼
SeloraX redirects to ──► GET /api/apps/oauth/authorize
│ ?client_id=sx_app_...
│ &redirect_uri=https://my-app.example.com/oauth/callback
│ &scope=read:orders,read:products
│ &state=random_csrf_token
│
▼
Merchant authorizes your app
│
▼
SeloraX redirects to ──► https://my-app.example.com/oauth/callback
?code=sx_ac_...
&state=random_csrf_token
│
▼
Your server exchanges code for tokens
│
▼
POST /api/apps/oauth/token
{
grant_type: "authorization_code",
client_id: "sx_app_...",
client_secret: "sx_secret_...",
code: "sx_ac_...",
redirect_uri: "https://my-app.example.com/oauth/callback"
}
│
▼
Response:
{
access_token: "sx_at_...",
refresh_token: "sx_rt_...",
token_type: "bearer",
expires_in: 86400,
scope: "read:orders read:products",
store_id: 22,
installation_id: 5
}
Implementation:
// OAuth callback handler
app.get("/oauth/callback", async (req, res) => {
const { code, state } = req.query;
// Verify state matches what you sent (CSRF protection)
if (state !== expectedState) {
return res.status(403).send("Invalid state parameter");
}
// Exchange authorization code for tokens
const { data } = await axios.post(`${SELORAX_API}/apps/oauth/token`, {
grant_type: "authorization_code",
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
code,
redirect_uri: REDIRECT_URI,
});
// Store tokens in your database (keyed by store_id)
await db.storeTokens(data.store_id, {
access_token: data.access_token,
refresh_token: data.refresh_token,
installation_id: data.installation_id,
scope: data.scope,
});
res.send("Installation successful! You can close this window.");
});Key rules:
- Authorization codes expire in 60 seconds -- exchange them immediately
- The
redirect_urimust exactly match one of your registeredredirect_urls - Store tokens securely -- the access token grants API access to the merchant's store
- Access tokens expire in 24 hours; use the refresh token to get a new one
4. Authenticate API Requests
After installation, you have two ways to authenticate:
Bearer Token (recommended for most cases)
curl -H "Authorization: Bearer sx_at_..." \
https://api.selorax.io/api/apps/v1/ordersClient Credentials (for server-to-server, never expires)
curl -H "X-Client-Id: sx_app_..." \
-H "X-Client-Secret: sx_secret_..." \
-H "X-Store-Id: 22" \
https://api.selorax.io/api/apps/v1/ordersClient credentials are ideal for background jobs, cron tasks, and integrations where you do not have a user session.
5. Refresh Expired Tokens
Access tokens expire after 24 hours. Use the refresh token to get a new one:
async function refreshAccessToken(storeId) {
const tokens = await db.getTokens(storeId);
const { data } = await axios.post(`${SELORAX_API}/apps/oauth/token`, {
grant_type: "refresh_token",
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
refresh_token: tokens.refresh_token,
});
// Store the new tokens (refresh token is also rotated)
await db.storeTokens(storeId, {
access_token: data.access_token,
refresh_token: data.refresh_token,
});
return data.access_token;
}Refresh tokens are valid for 90 days. If a refresh token expires, the merchant must reinstall your app.
6. Handle Webhooks
When your app is installed, the platform auto-creates webhook subscriptions for the topics in your webhook_topics field. Each subscription gets a unique HMAC signing secret.
Receive the signing secret during install:
The platform sends a POST to {your_webhook_url}/token with the installation details and webhook signing secrets. Store these secrets to verify incoming webhooks.
Verify and process webhooks:
const crypto = require("crypto");
app.post("/webhooks", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-selorax-signature"];
const timestamp = req.headers["x-selorax-timestamp"];
const secret = getSigningSecret();
const signedContent = `${timestamp}.${req.body}`;
const expected =
"sha256=" +
crypto.createHmac("sha256", secret).update(signedContent).digest("hex");
if (
expected.length !== signature.length ||
!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(req.body);
// Process the event
switch (event.event_topic) {
case "order.created":
handleNewOrder(event.data);
break;
case "order.status_changed":
handleStatusChange(event.data);
break;
}
res.status(200).send("OK");
});Delivery guarantees:
- At-least-once delivery (your handler should be idempotent)
- Retries on failure: immediate, 1min, 5min, 30min, 2hr, 12hr (6 total attempts)
- Auto-disabled after 20 consecutive failures
- View delivery logs in the merchant dashboard
7. Embed in the Dashboard (Optional)
If your app has a UI, it loads inside the merchant dashboard as an iframe. The platform sends a session token via postMessage.
Frontend (your iframe page):
<script>
// Signal readiness
window.parent.postMessage({ type: "app-bridge:ready" }, "*");
// Receive session token
window.addEventListener("message", (event) => {
if (event.data.type === "selorax:session-token") {
const { token, store_id } = event.data;
// Use token for authenticated frontend requests
fetchData(token, store_id);
}
});
</script>Backend (verify the session token):
const crypto = require("crypto");
function verifySessionToken(token, signingKey) {
const [header, payload, signature] = token.split(".");
const expected = crypto
.createHmac("sha256", signingKey)
.update(`${header}.${payload}`)
.digest("base64url");
if (signature !== expected) return null;
const decoded = JSON.parse(Buffer.from(payload, "base64url").toString());
if (decoded.exp < Math.floor(Date.now() / 1000)) return null;
return decoded; // { iss, dest, aud, sub, sid, app_id }
}Session tokens expire in 10 minutes. The dashboard automatically sends a new one when the merchant navigates to your app.
8. Charge Merchants (Optional)
If your app has a pricing model, use the billing API to create charges:
// Create a one-time charge
const { data } = await axios.post(
`${SELORAX_API}/apps/v1/billing/charges`,
{
name: "Pro Feature Unlock",
amount: 499,
currency: "BDT",
charge_type: "one_time",
return_url: "https://my-app.example.com/billing/complete",
},
{
headers: { Authorization: `Bearer ${accessToken}` },
},
);
// Redirect merchant to approve the charge
// data.confirmation_url → merchant approves → payment processed → redirect to return_urlSee the Billing documentation for recurring subscriptions, usage-based billing, and wallet top-ups.
Complete Integration Checklist
- Register your app with the platform admin
- Store
client_id,client_secret, andsession_signing_keysecurely - Implement OAuth callback endpoint (
/oauth/callback) - Exchange authorization codes for tokens
- Store access and refresh tokens per store
- Implement token refresh logic (24-hour expiry)
- Make API calls with Bearer token or client credentials
- Set up webhook endpoint to receive events
- Verify HMAC signatures on all webhook deliveries
- (If embedded) Implement iframe with
postMessagesession tokens - (If paid) Implement billing charge creation and approval flow
- Test the full install flow end-to-end
Common Issues
"Invalid redirect_uri"
The redirect_uri in your token exchange must exactly match one of the URLs in your app's redirect_urls array, including protocol and trailing slashes.
"Authorization code expired"
Codes are valid for 60 seconds. Exchange them immediately in your callback handler. Do not redirect the user before exchanging the code.
"Insufficient scope"
Your app is requesting an API endpoint that requires a scope you did not include in requested_scopes. Update your app configuration and ask the merchant to reinstall.
Webhook deliveries failing
- Ensure your endpoint returns a
2xxstatus code within 15 seconds - Check that your server is publicly accessible (not running locally)
- Verify the signing secret matches the one received during installation
- Check the delivery logs in Settings > Apps > Webhooks > Deliveries
Next Steps
- Your First App -- Step-by-step tutorial with complete code
- OAuth Flow -- Deep dive into the authorization flow
- API Reference -- All available endpoints
- Webhooks -- Event system documentation
- Messaging App Tutorial -- Build a full-featured app