This guide is for third-party developers who want to build an app that runs outside the Retailys Business Platform (RBP) cluster and talks to RBP through its public gateway.
RBP discovers and proxies your app through three well-known endpoints served at the root path of your public base URL:
| Endpoint | Purpose |
|---|---|
GET /manifest |
Returns your app manifest (JSON). |
GET /openapi.json |
Returns an OpenAPI 3.0 spec describing your API. |
GET /health |
Returns a simple liveness object ({ status: "ok" }). |
Keep these endpoints public (no JWT required) so Core can fetch them before a tenant installs your app.
A tenant admin registers the app by posting the manifest to Core. Replace USER_JWT with a token that has core:app:register for the target tenant.
curl -X POST https://api.retailys.us/api/registry/apps/register \
-H "Authorization: Bearer $USER_JWT" \
-H "Content-Type: application/json" \
-d '{
"slug": "myapp",
"name": "My App",
"description": "A short description.",
"version": "1.0.0",
"scope": "TENANT",
"public": true,
"baseUrl": "https://myapp.example.com",
"openapiUrl": "https://myapp.example.com/openapi.json",
"gatewayPrefix": "/api/apps/myapp",
"permissions": [
{ "key": "myapp:item:read", "name": "Read items" }
],
"roles": [
{ "key": "myapp:user", "name": "User", "permissions": ["myapp:item:read"] }
]
}'
Core returns the registered app, including credentials you must store securely:
{
"id": "app_xxx",
"slug": "myapp",
"clientId": "myapp_abc123",
"clientSecret": "sec_xxx",
"coreTokenSecret": "cts_xxx"
}
clientId / clientSecret — used to obtain service tokens for machine-to-machine calls.coreTokenSecret — per-app secret used to verify that an incoming request really came from Core (see below).When Core proxies a user request to your app, it includes an X-RBP-Core-Token header. This is a time-bucketed HMAC, not a static string. Validate it with the coreTokenSecret from registration:
<bucket>:<signature>.bucket (Unix time // 60) is within one minute of the current time.signature = HMAC-SHA256(coreTokenSecret, "{appSlug}:{bucket}").Core also sends the active tenant and caller identity:
GET /items HTTP/1.1
Host: myapp.example.com
Authorization: Bearer <user-jwt>
X-RBP-Core-Token: <bucket>:<signature>
X-RBP-Tenant: tenant_xxx
X-RBP-User: user_xxx
Reject any request whose token is missing, expired, or invalid.
Use your clientId and clientSecret to exchange a service token from Core:
curl -X POST https://api.retailys.us/api/auth/token \
-H "Content-Type: application/json" \
-d '{
"clientId": "myapp_abc123",
"clientSecret": "sec_xxx",
"tenantId": "tenant_xxx"
}'
Response:
{
"accessToken": "eyJ...",
"tokenType": "Bearer",
"expiresIn": 900
}
Use that token to call another app through the Core gateway:
curl https://api.retailys.us/api/apps/product/catalog \
-H "Authorization: Bearer $SERVICE_TOKEN"
The target app sees the same JWT claims and permission set that Core granted your app in that tenant.
https://api.retailys.us/api/auth/.well-known/jwks.json.tid claim (or the X-RBP-Tenant header).perms claim.| Public URL | Routed to | Notes |
|---|---|---|
https://api.retailys.us/api/apps/myapp/health |
your /health |
Through Core gateway |
https://api.retailys.us/api/apps/myapp/items |
your /items |
Core strips /api/apps/myapp |
https://api.retailys.us/api/openapi.json |
merged platform spec | Your paths appear under /api/apps/myapp |
Use relative paths in your controllers (do not repeat /api/apps/myapp).