Allegro API Gateway
The Allegro API Gateway is the single entry point for external partners that need to call the Allegro platform via REST. It sits in front of the Allegro Core, Allegro Mediation, and Allegro Operations services, translating REST calls into GraphQL. A Swagger UI is available for exploring the available endpoints.
Note: The Allegro API Gateway service starts on a set port. The DevOps team provides the actual service URLs for QA and higher environments. The following are the key UI endpoints: Swagger UI at <api-gateway-service-domain>/swagger-ui/, OpenAPI spec at <api-gateway-service-domain>/openapi, and health check at <api-gateway-service-domain>/q/health.
High-Level Request Flow
The following describes how an API call flows through the Allegro API Gateway:
- The external client sends an API call to the gateway. The gateway accepts two credential shapes:
Authorization: Bearer <JWT>—the partner manages their own Keycloak token lifecycle.X-Allegro-API-Key: pak_…—the partner exchanges credentials for a long-lived API key via/partner/session/login; the gateway refreshes the underlying Keycloak token internally.
- NGINX Ingress enforces rate limiting and payload size limits.
- The Allegro API Gateway performs JWT validation, partner session resolution, per-partner authorization, request validation, and circuit breaking.
- The gateway translates the REST request into a GraphQL request and forwards it to the appropriate Allegro service:
- Allegro Core service for core APIs
- Allegro Mediation service for mediation APIs
- Allegro Operations service for job scheduling operations
Key Features
The Allegro API Gateway handles the following functions:
| Feature | Description |
|---|---|
| Authentication | Accepts two credential paths: a direct bearer token (Quarkus OIDC validates signature, expiry, and issuer against the Aria Keycloak realm, with optional audience claim); or a partner-session API key (X-Allegro-API-Key) that the gateway resolves to a live bearer token and refreshes transparently. |
| Partner session brokerage | POST /partner/session/login exchanges a username/password or client ID/secret for an opaque pak_… API key. The gateway stores access and refresh tokens server-side and rotates them within a configurable refresh-buffer window. GET /partner/session introspects the session; DELETE /partner/session revokes it. |
| Per-partner authorization | The gateway maps each partner to an authorization profile via a Keycloak realm role (allegro:profile:<name>). Profile definitions (allowed path prefixes) are stored in partner-profiles.yml. The gateway returns 403 if the request path falls outside the profile’s allow-list. |
| Circuit breaking and fault tolerance | The GraphQLClient enforces a 30-second timeout, opens the circuit breaker after 50 percent failure in 10 requests (30-second cooldown), retries twice on connection or timeout errors, and returns a structured error JSON fallback when the circuit is open. |
| Rate limiting | Enforced at the NGINX Ingress layer: 20 requests per second, 600 requests per minute, three times burst, 10 concurrent connections, and a 10 MB body limit. Clients that exceed these limits receive 429 Too Many Requests. |
| Request validation | The RequestValidationFilter validates all POST, PUT, and PATCH requests for correct Content-Type, non-empty body, valid JSON, and a body size within 1 MB. Returns 400, 413, or 415 on failure. |
| Response hardening | The SecurityHeadersFilter adds security headers—including X-Frame-Options: DENY, Content Security Policy, HSTS, X-Content-Type-Options: nosniff, Referrer-Policy, and Cache-Control: no-store—to every response. |
| REST-to-GraphQL translation | Converts REST calls into GraphQL queries and mutations and forwards validated tokens to backend services for Aria-level authorization. |
Request Validation
The Allegro API Gateway validates all incoming POST, PUT, and PATCH requests. The following table describes the validation rules and the HTTP status code each rule returns on failure.
| Validation rule | HTTP status on failure | Trigger |
|---|---|---|
Content-Type must be application/json |
415 Unsupported Media Type | Wrong or missing content type |
| Body must not be empty | 400 Bad Request | Empty request body |
| Body must be valid JSON | 400 Bad Request | Malformed JSON syntax |
| Body must not exceed 1 MB | 413 Request Entity Too Large | Oversized payload |
GET and DELETE requests, and public endpoints (/q/*, /swagger-ui/*, /openapi), are not subject to these validations.
Circuit Breaking and Fault Tolerance
The GraphQLClient uses SmallRye Fault Tolerance (MicroProfile) to protect against backend failures. The following table describes each mechanism and its behavior.
| Mechanism | Configuration | Behavior |
|---|---|---|
| Timeout | 30 seconds | Fails fast if the backend does not respond within the timeout window. |
| Circuit breaker | Opens after 50 percent failure rate in 10 requests | Stops calling a failing backend for 30 seconds, then retries. Requires three consecutive successes to close. |
| Retry | Two retries | Retries on SocketTimeoutException and ConnectException only. |
| Fallback | Structured error JSON | Returns {"error": "Service temporarily unavailable"} when the circuit is open. |
The OkHttp client also enforces the following timeouts: connect timeout of 10 seconds, read timeout of 30 seconds, and write timeout of 10 seconds.
Rate Limiting
Rate limiting is enforced at the NGINX Ingress layer. The following table lists the default values.
| Setting | Default | Description |
|---|---|---|
| limit-rps | 20 | Maximum requests per second per client IP |
| limit-rpm | 600 | Maximum requests per minute per client IP |
| limit-burst-multiplier | 3 | Burst allowance (three times the per-second limit) |
| limit-connections | 10 | Maximum concurrent connections per client IP |
| proxy-body-size | 10m | Maximum request body size at ingress |
When a client exceeds these limits, the gateway returns 429 Too Many Requests. You can override the default values for a specific environment in the corresponding YAML file. The following example shows sample NGINX ingress annotations:
ingress:
annotations:
nginx.ingress.kubernetes.io/limit-rps: "50"
nginx.ingress.kubernetes.io/limit-rpm: "1500"
Authentication
The Allegro API Gateway uses quarkus-oidc in service mode (bearer-only; no login redirects). On startup, the gateway fetches Keycloak’s JWKS public keys from:
{KEYCLOAK_URL}/realms/{KEYCLOAK_REALM}/protocol/openid-connect/certs
Every request to an API endpoint must carry either Authorization: Bearer <token> or X-Allegro-API-Key: pak_…. The gateway validates the bearer token locally—without a network call per request—checking the signature against cached JWKS, expiry, and issuer. Tokens that are invalid, expired, from the wrong issuer, or missing receive 401 Unauthorized immediately.
Path-Based Security
The following table shows which paths require authentication and for what purpose.
| Path pattern | Authentication required | Purpose |
|---|---|---|
/auth/*, /customer/*, /billing/*, /balance/*, /usage/*, /common/*, /operations/*, /mediation/*, /gateway/*, /pricing/* |
Yes—valid bearer token and a partner profile that allows the path | All API endpoints |
/partner/session, /partner/session/login |
The login endpoint is open; introspect and revoke operations require the X-Allegro-API-Key header |
Partner session lifecycle |
/q/* |
No | Health checks and metrics |
/swagger-ui/*, /openapi/* |
No (requires SWAGGER_UI_ENABLED=true; returns 404 otherwise) |
API documentation |
Partner Session Brokerage
Partners that do not want to manage Keycloak token rotation can exchange short-lived credentials for a long-lived opaque API key. The following table describes the available session operations.
| Operation | Method and path | Description |
|---|---|---|
| Login | POST /partner/session/login |
Send grantType: password with username and password, or grantType: client_credentials with clientId and clientSecret. The gateway exchanges credentials with Keycloak, stores tokens server-side, and returns an opaque pak_… API key. |
| Introspect | GET /partner/session |
Include the X-Allegro-API-Key header. Returns session metadata (timestamps, session ID). The underlying tokens are never exposed. |
| Revoke | DELETE /partner/session |
Include the X-Allegro-API-Key header. Removes the session from the store. This operation is idempotent and returns 204 whether or not the key existed. |
After login, include the API key in the X-Allegro-API-Key header on every API call. The gateway resolves the key to a live access token, refreshing it from Keycloak as needed, and forwards only the validated bearer token to backend services. Store your API key in a secrets manager (such as Vault or AWS Secrets Manager) and never commit it to source control.
Per-Partner Authorization
After authentication, the gateway checks every request against a partner-specific path allow-list before forwarding it to a hub controller. Authorization relies on two sources of truth:
| Source | Controls | When it changes |
|---|---|---|
| Keycloak realm roles | Which partner maps to which authorization profile. The role is assigned in Keycloak and carried in the JWT as realm_access.roles. |
When a partner is added or their profile changes. No gateway file edit or pod restart required. |
partner-profiles.yml (ConfigMap) |
What each profile name allows—specifically, the path prefixes each profile can access. | When organization-level access policy changes. A pod restart is required when this file changes. |
Roles follow the naming convention allegro:profile:<profile-name>. The following example shows sample profile definitions:
profiles:
full-access:
allowedPaths: ["*"]
customer-billing:
allowedPaths: ["/customer", "/billing", "/balance", "/common"]
read-only:
allowedPaths: ["/customer", "/pricing", "/billing", "/balance", "/common"]
deny-all:
allowedPaths: []
defaultProfile: deny-all
At request time, the PartnerAuthorizationFilter reads the partner’s roles, identifies the allegro:profile: role, looks up the corresponding profile in the YAML, and returns 403 if the request path is not in the profile’s allowedPaths. Path matching is segment-aware: /customer matches /customer/account/123 but not /customers. Partners with no allegro:profile:* role fall through to deny-all.
Partner Onboarding
The following describes the end-to-end process for onboarding a new partner to the Allegro API Gateway.
At a Glance
Partners exchange a one-time Aria-issued credential (username/password or Keycloak client credentials) for a long-lived opaque API key (pak_…). The gateway holds the underlying OAuth tokens server-side and refreshes them transparently, so partners do not manage token rotation. Each partner is bound to an authorization profile that controls which API hubs they can reach.
| What partners receive | What partners provide |
|---|---|
| Long-lived API key, valid until revoked | Aria-issued Keycloak credentials (one-time exchange) |
| Server-side OAuth refresh—no token rotation required in client code | Partner identifier, intended use case, and technical contact |
| REST endpoints across 10 functional hubs (customer, pricing, billing, balance, usage, and others) | Production callback URLs and IP ranges, if applicable |
Predictable 403-on-deny authorization behavior |
A profile assignment agreed with Aria during onboarding |
Onboarding Steps
Step 1—Contact Aria. The Aria account team gathers the following information:
- Partner organization name and primary contact
- Technical contact (email and role)
- Intended use case and which API hubs the partner expects to call
- Source IP ranges, if the partner can commit to a fixed set
- Expected request volume
Step 2—Aria provisions credentials and assigns the authorization role. The identity administrator performs the following two actions in a single Keycloak admin session:
(a) Choose a credential shape:
| Credential shape | When to use | What Keycloak creates |
|---|---|---|
| User and password | A human will be logging in (for example, a partner web app performing the OAuth exchange). | A user in the aria realm with a password the partner sets on first login. |
| OAuth client and secret | Server-to-server integration with no human in the loop. | A confidential client with service-accounts-enabled and a generated client_secret. |
(b) Assign a realm role that encodes the partner’s authorization profile, using the naming convention allegro:profile:<profile-name>. For example, a partner that calls customer and billing endpoints receives the realm role allegro:profile:customer-billing. This role is assigned to the partner’s Keycloak user or service account in the same admin session as step (a). No files are edited on the gateway, and no pod restart is required.
Aria delivers credentials to the partner out of band, using a secret-share tool rather than email.
Step 3—Exchange credentials for an API key. The partner calls POST /partner/session/login once to receive a long-lived pak_… API key. The following examples show both credential patterns:
# Password grant (interactive partner)
curl -X POST https://api.allegro.example.com/partner/session/login \
-H "Content-Type: application/json" \
-d '{
"grantType": "password",
"username": "partner-acme-svc",
"password": "***"
}'
# Client credentials grant (machine-to-machine)
curl -X POST https://api.allegro.example.com/partner/session/login \
-H "Content-Type: application/json" \
-d '{
"grantType": "client_credentials",
"clientId": "partner-acme-app",
"clientSecret": "***"
}'
A successful response (201) returns the following:
{
"apiKey": "pak_xJ9k...",
"sessionExpiresAt": "2026-05-14T03:30:00Z",
"header": "X-Allegro-API-Key",
"note": "Treat the apiKey as a long-lived credential. Subsequent calls carry it in the header above."
}
Step 4—Make API calls. Include the API key in the X-Allegro-API-Key header on every API call. The gateway resolves the key to a valid bearer token internally.
ACCESS=$(jq -r '.apiKey' login-response.json)
# Read endpoint
curl https://api.allegro.example.com/customer/account/123 \
-H "X-Allegro-API-Key: $ACCESS"
# Mutating endpoint
curl -X POST https://api.allegro.example.com/customer/account \
-H "X-Allegro-API-Key: $ACCESS" \
-H "Content-Type: application/json" \
-d '{ "firstName": "Jane", "lastName": "Doe", ... }'
Step 5—Manage the session. Use the following commands to introspect or revoke the session:
# Introspect curl https://api.allegro.example.com/partner/session \ -H "X-Allegro-API-Key: $ACCESS" # Revoke (idempotent, returns 204) curl -X DELETE https://api.allegro.example.com/partner/session \ -H "X-Allegro-API-Key: $ACCESS"
A revoked or expired key returns 401 session not found or expired on the next request. Repeat Step 3 to obtain a fresh key.
Architecture Layers
The following table describes each layer in the gateway architecture, its responsibility, and how it fails.
| Layer | Responsibility | Failure response |
|---|---|---|
| NGINX Ingress | TLS termination, per-IP rate limiting, 10 MB payload cap | 429 Too Many Requests |
PartnerSessionResolver |
Resolves the API key to a live access token, rewrites the Authorization header, and strips the API key from the forwarded request |
401 session not found or expired |
| Quarkus OIDC | Validates the bearer token against Keycloak JWKS—checking signature, expiry, and issuer | 401 Unauthorized |
PartnerAuthorizationFilter |
Looks up the partner’s profile in partner-profiles.yml and checks the request path against the allow-list |
403 Forbidden—profile does not permit this path |
Hub controllers and GatewayRequestExecutor |
Translates REST to GraphQL, forwards the validated bearer token to Allegro Core, and marshals the response | 500 (or {"error":"Service temporarily unavailable"} when the circuit breaker is open) |
Partners interact only with the API key, the gateway public URL, and JSON responses. They never hold bearer tokens, refresh tokens, or backend service addresses.
HTTP Status Codes
The following table describes the HTTP status codes the gateway returns and the recommended action for each.
| Status | Source | Meaning | Action |
|---|---|---|---|
400 |
Request validation | Malformed JSON body, missing required field, or body exceeds 1 MB | Fix the request payload. |
401 (no body) |
Quarkus OIDC | Bearer token is absent, expired, from the wrong issuer, or has the wrong audience | Re-login via POST /partner/session/login. |
401 {"error":"session not found or expired"} |
PartnerSessionResolver |
API key is not in the session store—it was revoked, exceeded the idle TTL, or never existed | Re-login via POST /partner/session/login. |
403 {"error":"forbidden: profile does not permit this path"} |
PartnerAuthorizationFilter |
The partner is authenticated but their profile does not include the requested hub | Contact Aria to adjust the profile assignment. |
413 |
Request validation | Body exceeds 1 MB | Reduce the payload size or paginate the request. |
415 |
Request validation | Missing or incorrect Content-Type on POST, PUT, or PATCH requests |
Send Content-Type: application/json. |
5xx / {"error":"Service temporarily unavailable"} |
Backend circuit breaker | A downstream service is degraded and the circuit is open | Retry with exponential backoff. |
Session Management
To rotate your API key, call POST /partner/session/login to obtain a new key, then call DELETE /partner/session against the old key. The session idle TTL defaults to 30 minutes (sliding). Long-idle partners must re-login to obtain a new key.