Appearance
Token service and workspace API
Available now You get your client id and secret when you connect a producerapp to your workspace in the portal (see Credentials). Everything on this page then works on its own, with no RND step per request.
The bus issues its own OAuth2 access tokens. Your app swaps its client id and secret for a short-lived token, and uses the token to publish to its workspace or to dry-run a message against it.
In the URLs below, https://api.sombus.rnd-solutions.net/v1 is the Sandbox (use the Preview's base URL from Environments there), and {t} is your workspace id.
Get a token
POST /v1/oauth/token: the OAuth2 client-credentials grant (RFC 6749 §4.4).
Body:
application/x-www-form-urlencoded, withgrant_type=client_credentials.Client authentication, one of:
- HTTP Basic:
Authorization: Basic base64(client_id:client_secret), each part form-encoded first (RFC 6749 §2.3.1); - or
client_idandclient_secretin the body.
Using both in one request is refused. A body
client_idalongside Basic is allowed only if it is the same client.- HTTP Basic:
bash
curl -s https://api.sombus.rnd-solutions.net/v1/oauth/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=client_credentialsjson
{ "access_token": "eyJ…", "token_type": "Bearer", "expires_in": 3600 }Tokens last one hour. Reuse a token for its whole hour and ask for a new one shortly before expires_in runs out; there is no refresh token. Fetching a token per request runs into the token URL's rate limits (see Limits). Answers are sent with Cache-Control: no-store.
Your credentials
- Client id = your connection id:
cfollowed by 12 lowercase letters and the digits 2 to 7. One connection is one app in one workspace, with one credential. A consumer app's credential reads its queue through the HTTPS pull API and can't publish; a producer's can't read a queue. - Client secret starts with
sbs_. It is shown once, when it is issued. The bus keeps only a hash, so a lost secret can't be recovered, only replaced. Keep it out of source control; the prefix lets secret scanners spot a leak. - Rotation. A new secret ends every token issued before it within about 30 seconds; the previous secret can keep getting new tokens for an overlap window you choose. See Credentials.
What's in a token
An ES256-signed JWT. It says who you are, never what you may send: your permissions are looked up on every request, so a change or a revocation applies within about 30 seconds, whatever the token's expiry.
| Claim | Value |
|---|---|
iss | https://api.sombus.rnd-solutions.net/v1/oauth |
aud | https://api.sombus.rnd-solutions.net |
sub | your client id (connection id) |
tenant | your workspace id |
org | your organisation id |
app | your app id |
environment | sandbox (both hosted environments are test environments) |
credential_version | Your credential's version: 1 when issued, one more with each rotation. A token of an earlier version is refused |
iat, exp | issued at, and one hour later |
jti | a unique token id |
The header carries alg: ES256, typ: JWT and the signing key's kid.
A token only works on the environment that issued it: aud names it, and environment must match.
Signing key (JWKS)
GET /v1/oauth/jwks: the public key, for clients that want to check a token themselves. You don't have to: the bus checks every token it receives.
json
{ "keys": [ { "kty": "EC", "crv": "P-256", "x": "…", "y": "…", "kid": "…", "alg": "ES256", "use": "sig" } ] }kid is the key's RFC 7638 thumbprint. The answer may be cached for five minutes.
Publish and validate
Both routes take Authorization: Bearer <access token> and one SOM message as the JSON body, the same body as every other publish route (see the Bus API).
POST /v1/tenants/{t}/messagespublishes the message.POST /v1/tenants/{t}/validateis a dry run: the same checks, with your app's own permissions and your workspace's story state, but nothing is published or recorded. A message that would be accepted answers202with"dry_run": true.
{t} must be the workspace your credentials belong to: the tenant claim of your token.
The answers are the bus's usual ones (Verdicts and refusals): 202 accepted, 200 duplicate, 400 invalid SOM, 403 not allowed for this system_id or message_type, 409 sequence or reused message_id, 413 too large, 429 slow down, 503 retry with the same message_id.
Limits
The bus is for functional testing. It protects itself with a firewall and rate limits, applied before the message is read:
| What | Limit | Answer when exceeded |
|---|---|---|
Each connection (your app in a workspace), on /v1/tenants/{t}/… | Your workspace's quota tier, per second (see Quota tiers) | 429 rate.limited |
Each connection, on /v1/tenants/{t}/… | Your workspace's quota tier, per day (resets at midnight UTC) | 429 quota.exceeded |
| The token URL, per client using HTTP Basic | 30 requests in 5 minutes | 429 rate.limited |
| The token URL, per source address | 100 requests in 5 minutes | 429 rate.limited |
| Any route, per source address | 600 requests a minute | 429 rate.limited |
| The token URL | 20 requests a second, bursts of 40, for everyone together | 429 rate.limited |
The signed routes /v1/tenants/{t}/iam/…, all AWS signers together | 50 requests a second, bursts of 100 | 429 rate.limited |
| The whole API | 200 requests a second, bursts of 400 | 429 rate.limited |
| Body size | 240,000 bytes | 413 message.too_large |
Quota tiers
Every workspace has a quota: a tier, or limits RND sets for it. Each of its connections gets the whole allowance on its own, so one busy app never spends another's.
| Tier | Requests a second | Bursts of | Requests a day |
|---|---|---|---|
| minimal | 10 | 20 | 25,000 |
| standard | 20 | 40 | 100,000 |
| high | 100 | 200 | 1,000,000 |
RND picks the tier when it approves your organisation. A vendor invited by a publisher starts on minimal. Workspaces approved before tiers applied are on standard. A vendor's connections in a house get the vendor's quota, never the house's. If you need more, ask RND: it can change your tier, or set custom limits of up to 200 requests a second, bursts of 400, and 10,000,000 a day. A change applies to your existing connections within seconds, with no new credentials.
The per-connection rows apply to bearer tokens, from this token service or from your own identity provider. Requests signed with an AWS role share the signed routes' limit instead (see Authenticate).
A 429 may carry Retry-After (seconds). Back off and retry; resending a publish with the same message_id is safe. If your testing needs more, ask RND.
A request the firewall refuses as an attack pattern answers 403request.blocked. The firewall doesn't judge SOM content: the gateway does.
Errors
Token request
Errors follow RFC 6749 §5.2: { "error": "…", "error_description": "…" }.
| Status | error | Meaning | What to do |
|---|---|---|---|
| 400 | invalid_request | Body is not form-encoded, malformed Basic credentials, or two authentication methods in one request | Fix the request |
| 400 | unsupported_grant_type | grant_type is not client_credentials | Send client_credentials |
| 401 | invalid_client | Unknown client, wrong secret, a previous secret after its overlap, revoked connection, or a credential for another environment. The bus doesn't say which. With Basic, the answer carries WWW-Authenticate | Check the id and secret, and that you call the right environment |
| 429 | (bus shape, rule rate.limited) | Too many token requests | Reuse tokens for their hour; back off |
| 503 | temporarily_unavailable | The bus could not check your credentials or sign the token | Retry with backoff |
Workspace routes
Violations use the usual body: { "accepted": false, "violations": [ { "rule": "…", "message": "…" } ] }.
| Status | Rule | Meaning | What to do |
|---|---|---|---|
| 401 | producer.unauthenticated | Token missing, invalid, expired, issued before a rotation, for another environment, or for a workspace your app no longer belongs to | Get a new token; check the environment |
| 403 | producer.not_registered | Valid token, but its connection is not active: revoked or removed, or a token of the wrong kind for the route (a producer's on the pull API) | Check the connection in the portal's Apps page |
| 403 | tenant.mismatch | {t} is not your workspace | Use the workspace id from your token's tenant claim |
| 429 | rate.limited, quota.exceeded | Over a limit above | Back off; retry with the same message_id |
| 503 | registry.unavailable | The bus could not read your app's permissions; nothing was accepted | Retry with the same message_id |