Connect Portal API
Billerang has two separate control planes, each with its own API and its own auth model.
Tenant API vs. Connect portal
Tenant /api/v1 is the billing engine API for a single tenant environment (a WildFly
deployment): sellers, customers, catalog, orders, invoices, and so on. It authenticates with
an OAuth 2.0 client-credentials bearer token from Keycloak and is meant for server-to-server
calls and the tenant frontend. See Billerang v1 API.
Connect portal /api/portal/* is the org-level control plane: it manages users, roles,
groups, profiles, organization settings, environments, and security policy (SSO, 2FA,
password policy) across every tenant environment an organization owns. It is a separate Go
service (billerang-connect-portal), not part of the WildFly backend.
An organization can own multiple tenant environments (sandbox, staging, production). The
Connect portal is where an org administrator manages people and access across all of them;
the tenant /api/v1 IAM surface manages roles and groups inside a single tenant. Both write
into the same Keycloak realm (billerang), at different scopes.
Auth model difference
Tenant /api/v1 | Connect portal /api/portal/* | |
|---|---|---|
| Credential | OAuth 2.0 bearer token (client credentials grant) | Browser session cookie (OIDC login) |
| Scoping | Tenant, resolved from the API host | Organization, resolved from the subdomain |
| Typical caller | Server-to-server integration, tenant frontend | Connect portal single-page app in a browser |
| Elevated actions | Role-based (Keycloak client roles) | Same, plus a portal-specific connect:org-admin role gate on security endpoints |
The Connect portal has no server-to-server bearer-token flow today. A caller logs in via
GET /auth/login, which redirects to Keycloak scoped to the org derived from the subdomain
({org}.connect.billerang.com), and GET /auth/callback sets an encrypted, HTTP-only session
cookie. Every subsequent /api/portal/* call must carry that cookie, and a middleware check
rejects the request if the subdomain's org does not match the session's org claim.
Reference
The full endpoint-by-endpoint reference (every route, request/response shape, and try-it-out)
is at /portalApiReference, an embedded Scalar viewer over
portal-v1.yaml.
This spec documents the routes as they exist today under /api/portal/*. Some paths use
verb-in-path actions and mixed key types that don't match the tenant /api/v1 naming
conventions; each operation in the spec carries an x-v1-target extension showing the
planned normalized path under a future /api/portal/v1/* scheme (not live yet). See
docs/adr/ADR-connect-portal-api.md in the repository for the full rationale.