Skip to content

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:

CapabilityPurpose
Acting-user propagationAttribute API actions to the real human, not the service account
Credential validationValidate user passwords against SynthesQ without direct DB access
Permission queryFetch a user's roles and permissions for local caching
Token provisioningCreate 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:

HeaderDescription
X-Acting-User-IDThe ULID of the real human user in the tenant
X-Acting-User-EmailThe 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

http
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/login

Middleware: auth:sanctum, tenant, throttle:auth-proxy

Request

json
{
  "email": "user@example.com",
  "password": "their-password"
}

Responses

200 - Valid credentials:

json
{
  "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:

json
{
  "success": false,
  "message": "Invalid credentials"
}

423 - Two-factor authentication required:

json
{
  "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}/permissions

Middleware: auth:sanctum, tenant, token.abilities:users:view-permissions

Required ability: users:view-permissions

Response

json
{
  "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-tokens

Middleware: auth:sanctum, token.abilities:admin:provision-integration-token

Required ability: admin:provision-integration-token

Request

json
{
  "abilities": ["*"],
  "expiration_days": 365,
  "ip_whitelist": ["10.0.0.0/8"]
}
FieldTypeRequiredDescription
abilitiesstring[]NoToken abilities. Defaults to ["*"] (full access)
expiration_daysintegerNoDays until expiry (1–3650). Integration tokens default to no expiry
ip_whiteliststring[]NoAllowed source IPs or CIDR ranges

Response (201)

json
{
  "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}/rotate

Middleware: auth:sanctum, token.abilities:admin:rotate-integration-token

Required ability: admin:rotate-integration-token

Request

json
{
  "expiration_days": 365,
  "revoke_old": true,
  "revocation_reason": "Scheduled quarterly rotation"
}
FieldTypeRequiredDescription
expiration_daysintegerNoDays until the new token expires (1–3650)
revoke_oldbooleanNoWhether to revoke the old token. Defaults to true
revocation_reasonstringNoAudit trail reason for revocation (max 255 chars)

Response (200)

json
{
  "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:

  1. Provision - Landlord admin provisions an integration token per tenant via the provisioning endpoint
  2. Login - Gateway receives user credentials, validates them via the auth proxy, and issues its own JWT
  3. Cache permissions - Gateway fetches the user's permission set and caches it locally
  4. API calls - Gateway calls SynthesQ with the integration token + X-Acting-User-ID header
  5. 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

StatusMeaningCommon cause
401UnauthenticatedMissing or invalid Bearer token
403ForbiddenToken lacks required ability (e.g., admin:provision-integration-token)
404Not foundTenant or token ID does not exist
422Validation errorInvalid request body (e.g., expiration_days out of range)
4232FA requiredUser has two-factor authentication enabled (auth proxy only)
429Rate limitedAuth proxy: exceeds 10 requests/minute per IP

Documentation for SynthesQ CRM/ERP Platform