This page documents the conventions, not a live endpoint. It describes the request/response contract you can rely on across the portfolio. Per-product paths, schemas and examples live on each API's Redoc reference; environment hosts and quotas live on the Sandbox access page.
Authentication
All endpoints require a JSON Web Token (JWT) bearer
credential unless a path is explicitly marked public. Send it in the
Authorization header on every request:
Authorization: Bearer <your-jwt>
Accept: application/json
Content-Type: application/json
Tokens are scoped to a single environment — a sandbox token never
authenticates against production and vice versa (see
Sandbox access). A missing, malformed or
expired token returns 401 Unauthorized; a valid token that
lacks permission for the resource returns 403 Forbidden.
Self-service key issuance from the portal is tracked separately and is not
live yet.
Base URLs & paths
Hosts follow a per-environment convention; request paths are identical across environments, so only the base URL changes when you promote an integration from sandbox to production.
| Environment | Base URL convention | Data |
|---|---|---|
| Sandbox | https://<product>.staging.glorylabs.nl |
Synthetic / disposable |
| Production | https://<product>.glorylabs.nl |
Real customer data |
OpenAPI specs are served at the springdoc default path
/v3/api-docs; the human-readable reference for each product is
linked from the portal home.
Requests & responses
- Request and response bodies are UTF-8 JSON. Send
Content-Type: application/jsonon any request with a body. - Timestamps are ISO 8601 in UTC
(e.g.
2026-06-15T09:30:00Z). - Unknown fields in a request body are rejected rather than silently
ignored, so a typo fails fast with
400 Bad Request. - Field names are
camelCase; monetary amounts are integer minor units (cents) with a separate currency code.
Error shape
Errors use a single, predictable envelope across every product so one handler works portfolio-wide. The HTTP status carries the category; the body carries the detail.
{
"timestamp": "2026-06-15T09:30:00Z",
"status": 400,
"error": "Bad Request",
"message": "validation failed: 'email' must be a valid address",
"path": "/v1/candidates",
"traceId": "b7f3c1a2d4e5f6a7"
}
Always log the traceId — quoting it lets us correlate a
failing call with server-side logs when you ask for help.
HTTP status codes
| Code | Meaning | What to do |
|---|---|---|
200 / 201 |
Success / resource created | Read the body; 201 includes the new resource |
204 |
Success, no content | Common for DELETE — don't parse a body |
400 |
Bad request / validation failed | Fix the payload; message says what's wrong |
401 |
Unauthenticated | Missing/expired token — refresh and retry |
403 |
Forbidden | Authenticated but not permitted — check scope |
404 |
Not found | Check the path and resource id |
409 |
Conflict | State clash (e.g. duplicate) — reconcile and retry |
429 |
Too many requests | Back off using Retry-After (see below) |
5xx |
Server error | Retry with backoff; quote the traceId if it persists |
Pagination
List endpoints are paginated with page (zero-based) and
size query parameters and return a consistent envelope, so the
same pagination loop works against every product.
GET /v1/candidates?page=0&size=50
{
"content": [ /* … items … */ ],
"page": 0,
"size": 50,
"totalElements": 137,
"totalPages": 3
}
Default size is 20 and the maximum is 100. Request a page past
the last and you get an empty content array, not an error.
Rate limiting
Requests are rate limited per credential. When you exceed the limit the API
responds with 429 Too Many Requests and a
Retry-After header (in seconds) telling you how long to wait
before retrying.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Build clients to honour Retry-After and back off rather than
hammering. The concrete sandbox limits are published on the
Sandbox access page; production limits are set
per product at onboarding.
Versioning & change policy
APIs are versioned in the path (/v1/…). Backwards-compatible
additions — new endpoints, new optional fields — ship within the current
version without a bump. Breaking changes land under a new version prefix
and are announced ahead of time.
Track what changed on the Changelog, and when a breaking version does land, follow the step-by-step Migration guides to upgrade safely.