Gateway Integration
This guide explains how a gateway application can integrate with SynthesQ while preserving audit trail integrity, permission freshness, and per-tenant token isolation.
Overview
A Gateway sits between end-users and SynthesQ. It authenticates humans via its own sessions or JWTs, then calls SynthesQ using a long-lived integration token. SynthesQ provides four capabilities to support this pattern:
| Capability | Purpose |
|---|---|
| Acting-user propagation | Attribute API actions to the real human, not the service account |
| Credential validation | Validate user passwords against SynthesQ without direct DB access |
| Permission query | Fetch a user's roles and permissions for local caching |
| Token provisioning | Create and rotate per-tenant integration tokens programmatically |
TIP
All Gateway endpoints require an existing integration token for authentication. See the Authentication guide for how to create tokens via the SPA interface.
Acting-User Propagation
When a Gateway makes API calls on behalf of a human user, SynthesQ needs to know who the real user is so that audit logs attribute changes correctly.
How it works
Send two headers with any tenant-scoped API request:
| Header | Description |
|---|---|
X-Acting-User-ID | The ULID of the real human user in the tenant |
X-Acting-User-Email | The email of the real human user (optional, for logging) |
SynthesQ's ResolveActingUser middleware detects these headers on integration-token requests and swaps the authenticated identity so that:
- Owen-It audit logs record changes against the real human
- All downstream authorization checks use the human's permissions
- The service account identity is transparent to application code
Requirements
- The request must be authenticated with an integration token (personal and application tokens ignore these headers)
- The acting user must exist in the tenant's user table and be active
- If the header is missing or the user is not found, the request proceeds normally with the service account identity
Example
POST /api/v1/crm/leads HTTP/1.1
Authorization: Bearer <integration-token>
X-Acting-User-ID: 01hwxyz123abc456def789ghi0
Content-Type: application/json
{
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@example.com"
}The lead is created and the audit log records the human user (ID 01hwxyz...) as the creator, not the integration service account.
WARNING
If the acting user ID does not match an active user, the middleware logs a warning and falls through silently - the action is attributed to the service account. Monitor your logs for ResolveActingUser: acting user not found or inactive messages to detect misconfigured Gateway headers.
Credential Validation (Auth Proxy)
The Gateway login flow needs to validate user credentials against SynthesQ's identity store without issuing a SynthesQ token.
Endpoint
POST /api/v1/auth/loginMiddleware: auth:sanctum, tenant, throttle:auth-proxy
Request
{
"email": "user@example.com",
"password": "their-password"
}Responses
200 - Valid credentials:
{
"success": true,
"message": "Credentials validated successfully",
"data": {
"user": {
"id": "01hwxyz123abc456def789ghi0",
"name": "Jane Smith",
"email": "jane@example.com",
"roles": ["sales-manager", "lead-viewer"]
}
}
}401 - Invalid credentials:
{
"success": false,
"message": "Invalid credentials"
}423 - Two-factor authentication required:
{
"success": false,
"message": "Two-factor authentication required"
}INFO
This endpoint does not issue a SynthesQ token. The Gateway manages its own session or JWT issuance after validating the credentials. Rate-limited to 10 requests per minute per IP.
Permission Query
Gateway applications cache per-user permissions locally using a stale-while-revalidate strategy. This endpoint returns the full permission set for a user.
Endpoint
GET /api/v1/users/{id}/permissionsMiddleware: auth:sanctum, tenant, token.abilities:users:view-permissions
Required ability: users:view-permissions
Response
{
"success": true,
"message": "User permissions retrieved",
"data": {
"roles": ["sales-manager"],
"permissions": [
"leads:view",
"leads:create",
"leads:update",
"opportunities:view",
"opportunities:create"
]
}
}The roles array contains Bouncer role names assigned to the user in the current tenant. The permissions array contains all resolved abilities (flattened from roles and direct grants).
Integration Token Provisioning
Each tenant in the Gateway needs its own SynthesQ integration token for isolated API access. These endpoints let a landlord admin provision and rotate tokens programmatically.
Provision a Token
POST /api/v1/landlord/tenants/{tenantId}/integration-tokensMiddleware: auth:sanctum, token.abilities:admin:provision-integration-token
Required ability: admin:provision-integration-token
Request
{
"abilities": ["*"],
"expiration_days": 365,
"ip_whitelist": ["10.0.0.0/8"]
}| Field | Type | Required | Description |
|---|---|---|---|
abilities | string[] | No | Token abilities. Defaults to ["*"] (full access) |
expiration_days | integer | No | Days until expiry (1–3650). Integration tokens default to no expiry |
ip_whitelist | string[] | No | Allowed source IPs or CIDR ranges |
Response (201)
{
"success": true,
"message": "Integration token provisioned successfully",
"data": {
"token_id": "42",
"plain_text_token": "xR9k2m...64-char-random-string...",
"expires_at": "2027-03-16T00:00:00Z"
}
}WARNING
The plain_text_token is shown only once in this response. Store it securely - it cannot be retrieved later.
Service user
On first provisioning call for a tenant, SynthesQ automatically creates a dedicated service user (integration-service@tenant-{id}.synthesq.internal). Subsequent provisioning calls for the same tenant reuse this user.
Rotate a Token
POST /api/v1/landlord/tenants/{tenantId}/integration-tokens/{tokenId}/rotateMiddleware: auth:sanctum, token.abilities:admin:rotate-integration-token
Required ability: admin:rotate-integration-token
Request
{
"expiration_days": 365,
"revoke_old": true,
"revocation_reason": "Scheduled quarterly rotation"
}| Field | Type | Required | Description |
|---|---|---|---|
expiration_days | integer | No | Days until the new token expires (1–3650) |
revoke_old | boolean | No | Whether to revoke the old token. Defaults to true |
revocation_reason | string | No | Audit trail reason for revocation (max 255 chars) |
Response (200)
{
"success": true,
"message": "Integration token rotated successfully",
"data": {
"token_id": "43",
"plain_text_token": "bK7j3p...64-char-random-string...",
"expires_at": "2027-03-16T00:00:00Z"
}
}When revoke_old is true (default), the previous token is immediately revoked and can no longer authenticate requests. Set to false for a grace period during rolling deployments.
Typical Gateway Flow
A complete integration typically follows this sequence:
- Provision - Landlord admin provisions an integration token per tenant via the provisioning endpoint
- Login - Gateway receives user credentials, validates them via the auth proxy, and issues its own JWT
- Cache permissions - Gateway fetches the user's permission set and caches it locally
- API calls - Gateway calls SynthesQ with the integration token +
X-Acting-User-IDheader - Rotate - Periodically rotate integration tokens using the rotation endpoint
┌──────┐ ┌─────────┐ ┌──────────┐
│ User │────▶│ Gateway │────▶│ SynthesQ │
└──────┘ └─────────┘ └──────────┘
│ │ │
│ login │ POST │
│─────────────▶│ /auth/login │
│ │───────────────▶│ validate credentials
│ │◀───────────────│ return user + roles
│◀─────────────│ issue JWT │
│ │ │
│ action │ POST /api │
│─────────────▶│ + X-Acting │
│ │ -User-ID │
│ │───────────────▶│ swap to acting user
│ │◀───────────────│ audit = real human
│◀─────────────│ │Error Reference
| Status | Meaning | Common cause |
|---|---|---|
401 | Unauthenticated | Missing or invalid Bearer token |
403 | Forbidden | Token lacks required ability (e.g., admin:provision-integration-token) |
404 | Not found | Tenant or token ID does not exist |
422 | Validation error | Invalid request body (e.g., expiration_days out of range) |
423 | 2FA required | User has two-factor authentication enabled (auth proxy only) |
429 | Rate limited | Auth proxy: exceeds 10 requests/minute per IP |
Related
- Authentication guide - Token types, abilities, rate limiting
- Interactive API Explorer - Try endpoints directly