RBP Foreign Developer Guide

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.


What your app must expose

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.


Register your app with Core

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"
}

Verify requests from Core

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:

  1. Parse the header as <bucket>:<signature>.
  2. Ensure the bucket (Unix time // 60) is within one minute of the current time.
  3. Recompute signature = HMAC-SHA256(coreTokenSecret, "{appSlug}:{bucket}").
  4. Compare the computed signature to the received one in constant time.

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.


Call other apps through the gateway

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.


Authentication and authorization


Routing summary

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).