SelSup platform · Guides
Extensions and custom integrations
Build a regular HTML/JavaScript page using any framework. SelSup embeds it in an iframe; it does not execute your React code inside the cabinet.
Build a regular HTML/JavaScript page using any framework. SelSup embeds it in an iframe; it does not execute your React code inside the cabinet.
Application developers and technology partners1
Choose the integration type
- 1
External backend service
Best for ERP, web-store, WMS, BI, and similar synchronization. The token stays on your server and data moves through the API and webhooks.
- 2
Cabinet iframe panel
Build a regular HTML/JavaScript page using any framework. SelSup embeds it in an iframe; it does not execute your React code inside the cabinet.
- 3
SelSup backend module
Required for a deep marketplace or delivery integration participating in shared SelSup background processes.
Implement one complete workflow and its observability first. Add bulk imports, UI, and more event types after reconciliation is stable.
2
External application architecture
Keep the token in a secrets manager and call SelSup only from your backend. Your browser application should talk to your API and never receive the SelSup token.
Separate the SelSup adapter, job queue, and domain model. API contract changes then remain isolated from the rest of the application.
Use an inbox for incoming events and an outbox for outgoing changes. Both journals should support replay and auditing.
SelSup webhook → inbox → domain worker → ERP/WMS
ERP/WMS change → outbox → SelSup API → reconciliation
3
Cabinet iframe panel
Build a regular HTML/JavaScript page using any framework. SelSup embeds it in an iframe; it does not execute your React code inside the cabinet.
Host the panel on a separate HTTPS site that does not share a cookie domain with the cabinet. Open Integrations → My extensions → Add extension and select a name, icon, URL, menu section and position. Save a private panel with no permissions, then enable only the permissions it needs. Store publication requires SelSup review.
4
Server-side integration module
A marketplace integration normally covers tokens, cards, prices, stock, orders, and background status updates. A delivery service adds rating, shipment creation, labels, and tracking.
This module changes the main SelSup backend and follows its review, testing, localization, and release process. Agree on interfaces and data boundaries before implementation.
Convert external API failures into stable ApplicationError values and revoked or invalid external tokens into AccessException according to backend project rules.
Do not add marketplace-specific fields to shared entities. Keep raw contracts in integration DTOs and map them at the boundary.
5
Release and operation
- 1
Create a test account
Separate test tokens, warehouses, products, and orders from customer production data.
- 2
Add observability
Track queue delay, errors by code, retry counts, and reconciliation discrepancies.
- 3
Plan contract migration
Support old fields during a transition and deploy consumers before disabling the old format.
- 4
Document disconnection
Users must know how to revoke a token, remove a webhook, and stop synchronization without losing data.
6
Product and order tabs
Choose a menu page (MENU_PAGE), a product card tab (PRODUCT_TAB), or an order tab (ORDER_TAB). Menu section and position apply only to MENU_PAGE. Tabs require a saved entity with an ID.
7
Context and permissions
Only granted context fields are shared. USER provides user.id and user.locale; CLIENT provides client.id; ORGANIZATION provides accessible organization id and name. PRODUCT separately grants product.modelId on a product card; ORDER grants order.id on an order. Full records, contacts, passwords and tokens are excluded. These are identifiers of the already open page, not proof of access for your backend.
{
"type": "selsup.extension.context",
"version": 1,
"context": {
"version": 1,
"extensionId": "example-extension-id",
"placement": "MENU_PAGE",
"user": {"id": 101, "locale": "ru"},
"client": {"id": 202},
"organizations": [{"id": 303, "name": "Demo"}]
}
}
[
{
"version": 1,
"extensionId": "example",
"placement": "PRODUCT_TAB",
"apiMethods": [],
"product": {
"modelId": 404
}
},
{
"version": 1,
"extensionId": "example",
"placement": "ORDER_TAB",
"apiMethods": [],
"order": {
"id": 505
}
}
]
8
postMessage v1 messages
Check the exact event.origin, event.source === window.parent, message type, version and structure. Send ready on connection and request-context to refresh. SelSup rechecks access and coalesces frequent requests. Never pass tokens in URLs, panel localStorage or messages.
const allowedOrigins = new Set(['https://your-cabinet.example.com']);
window.addEventListener('message', (event) => {
if (event.source !== window.parent || !allowedOrigins.has(event.origin)) return;
const message = event.data;
if (!message || message.type !== 'selsup.extension.context' || message.version !== 1) return;
const context = message.context;
if (!context || context.version !== 1 || !['MENU_PAGE', 'PRODUCT_TAB', 'ORDER_TAB'].includes(context.placement)) return;
// The complete sample also validates fields and strips unknown properties.
document.querySelector('#context').textContent = JSON.stringify(context, null, 2);
});
for (const origin of allowedOrigins) {
window.parent.postMessage({type: 'selsup.extension.ready', version: 1}, origin);
}
// Refresh after connecting: use the validated origin, never "*".
window.parent.postMessage({type: 'selsup.extension.request-context', version: 1},
'https://your-cabinet.example.com');
9
API through the iframe bridge
The author declares required methods in the extension settings. Each installing client explicitly grants methods from that declaration. API access defaults to none, including existing installations. Changing the requested list requires another review; installed copies never gain new permissions automatically.
The host calls only the methods below through SelSupFront services with the current employee’s permissions. Existing server access controls still apply. clientService and authService are fully prohibited; all methods outside this list are denied too. Apps cannot supply URLs, headers, tokens or arbitrary Axios configuration. The server checks the current installation and its grants on the actual API request.
API grants are independent of context permissions. context.apiMethods lists granted methods. A grant applies to the method across the cabinet within the employee’s access, rather than only the entity in the current tab. Results contain only the documented fields, never Axios objects or credentials.
One in-flight request and at most one start per second. The last 256 request IDs are protected against duplicates. Host errors: ACCESS_DENIED, INVALID_REQUEST, RATE_LIMITED, DUPLICATE_REQUEST, REQUEST_FAILED. SDK errors also include NOT_CONNECTED, DISCONNECTED and TIMEOUT (20 seconds). Never retry mutations automatically: an error, timeout or iframe closure does not mean the operation was rolled back.
tagService.findTags {query?, page?, limit?} => {rows: [{id, name}]}
tagService.createTag {name} => {id, name}
tagService.updateTag {id, name} => {id, name}
tagService.deleteTag {id} => null
productService.updateFavourite {id, favourite} => null
id: positive safe integer; updateFavourite.id = product.modelId
favourite: boolean
name: 1..100 characters (trimmed); query: 0..100 characters
page: 1..10000, default 1; limit: 1..100, default 30
{
"type": "selsup.extension.api-request",
"version": 1,
"requestId": "request-1",
"method": "tagService.createTag",
"params": {
"name": "Demo"
}
}
{
"type": "selsup.extension.api-response",
"version": 1,
"requestId": "request-1",
"ok": true,
"result": {
"id": 1,
"name": "Demo"
}
}
// connection = connectExtension(...) from selsup-extension.js
// Run from a button click after receiving context.apiMethods.
const tag = await connection.callApi('tagService.createTag', {name: 'Demo'});
console.log(tag.id, tag.name);
10
Sample app and hosting
The sample can be copied into a standalone GitHub repository. With Node.js 22+, npm test and npm start run tests and a local mock cabinet without API keys or real data. Configure config.js and deploy five static files to HTTPS hosting. Download the archive linked below and copy its contents into your GitHub repository.
cd selsup-extension-example
npm test
npm start
# Mock host: http://localhost:4174/
# Panel: http://127.0.0.1:4173/
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; connect-src 'none'; form-action 'none'; base-uri 'none'; frame-ancestors https://your-cabinet.example.com
Referrer-Policy: no-referrer
X-Content-Type-Options: nosniff
https://api.selsup.ru/assets/examples/selsup-extension-example.zip
11
Security boundaries
The sandbox blocks forms, popups, downloads, top navigation and sensitive browser APIs. The panel origin is shown above the iframe. An extension must never ask for a SelSup password. Its site can still see the visitor IP and retain or forward data granted to it. Review cannot freeze the code served at an external URL. Grant only necessary permissions.
SelSup itself authorizes iframe bridge calls; cookies and tokens never reach the app. Your own backend still needs its own authentication: context does not prove identity. Keep long-lived SelSup tokens on your server only. Full order/product records and inline card blocks are not supported.