Skip to content

How to configure an organization (tenant)

RA serves multiple organizations from one deployment. Each org has its own configuration blob and prompt overrides in the organization_config database table, resolved at request time by ConfigService: an org's override wins; otherwise the deploy-level Config.<KEY> default applies (config_service.py). This guide covers reading and writing that per-org configuration.

Prerequisites

  • The App.Platform.Operator role. The /platform/* endpoints are gated to this role by AuthMiddleware. It is independent of App.Admin.FullAccess — an admin user does not automatically have it. Locally, run the backend with FAKE_AUTH_USER_TYPE=PlatformOperator (see the caveat in the repository README.md — the frontend and load-testing fake-auth adapters do not recognise this value, so set it on the backend service only).
  • The org's UUID. Org IDs are deterministic UUIDv5 values derived from a slug (organizations.py). Worked example: slug aara36b2b7ae-cd2f-51ab-b9a5-369ca259991d.

Two ways to call the platform API: the altaforge-ra-cli tool (recommended) or raw HTTP. Set up the CLI first via the CLI setup runbook.

Read an organization's config

=== "CLI"

```bash
# Full config blob
altaforge-ra-cli --profile prod api platform config get <org-uuid>

# A single key
altaforge-ra-cli --profile prod api platform config get <org-uuid> --key ENABLE_ESCALATION_WORKFLOW
```

=== "HTTP"

```bash
# Full config blob
curl "$BASE/platform/organizations/<org-uuid>/config" \
  -H "Authorization: Bearer $TOKEN"

# A single key
curl "$BASE/platform/organizations/<org-uuid>/config/ENABLE_ESCALATION_WORKFLOW" \
  -H "Authorization: Bearer $TOKEN"
```

GET /platform/organizations/{organization_id}/config returns the full JSON blob (404 if the org has no config row). GET …/config/{key} returns a single top-level value (404 if the key is absent).

Set a single config key

Setting one key leaves the rest of the blob untouched. Values are parsed as JSON with a raw-string fallback, so 4096 stores an integer and SME Team stores a string.

=== "CLI"

```bash
altaforge-ra-cli --profile prod api platform config set <org-uuid> ENABLE_ESCALATION_WORKFLOW true
altaforge-ra-cli --profile prod api platform config set <org-uuid> ESCALATION_MAX_TOKENS 4096
```

=== "HTTP"

```bash
curl -X PUT "$BASE/platform/organizations/<org-uuid>/config/ENABLE_ESCALATION_WORKFLOW" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d 'true'
```

PUT …/config/{key} returns 204 No Content and invalidates the org's cached config so all workers pick up the change within the cache TTL window.

Replace the whole config blob

PUT …/config overwrites the entire blob — any key not in the body is removed. Use this only when you intend to set the complete config.

=== "CLI"

```bash
altaforge-ra-cli --profile prod api platform config replace <org-uuid> \
  -d '{"ENABLE_ESCALATION_WORKFLOW": true, "ENABLE_QUICK_LINKS": true}'
```

=== "HTTP"

```bash
curl -X PUT "$BASE/platform/organizations/<org-uuid>/config" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ENABLE_ESCALATION_WORKFLOW": true, "ENABLE_QUICK_LINKS": true}'
```

The CLI also offers config apply --body-file <file> to set many keys from a committed JSON file without wiping the rest of the blob, and config seed to write only missing keys — see the CLI README for the difference between set, apply, replace, and seed.

Set a prompt override

Prompt overrides are stored under a nested prompts sub-key of the org's config, not as flat top-level keys, and are resolved by ConfigService.get_prompt. The body is raw UTF-8 text, not JSON.

=== "CLI"

```bash
altaforge-ra-cli --profile prod api platform prompt set <org-uuid> ESCALATION_EMAIL_FOOTER \
  -d "Regards, the Support Team"
```

=== "HTTP"

```bash
curl -X PUT "$BASE/platform/organizations/<org-uuid>/prompts/ESCALATION_EMAIL_FOOTER" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: text/plain" \
  --data-binary "Regards, the Support Team"
```

PUT …/prompts/{prompt_key} returns 204 and invalidates the org's cache. Client-customisable prompt keys that the workflow reads include QUERY_REWRITE_PROMPT, MULTI_QUERY_PROMPT, and ESCALATION_EMAIL_FOOTER — see the Environment Configuration section of backend/README.md for each feature's prompt key and behaviour.

What you can configure per org

Any key that ConfigService resolves can be set per org; when unset, the org inherits the deploy-level Config.<KEY> default. Commonly tuned per-tenant settings (full descriptions in backend/README.md):

Key Effect
ENABLE_ESCALATION_WORKFLOW Adds escalation intent classification to the RAG workflow.
ENABLE_QUERY_REWRITING Rewrites the query before retrieval.
ENABLE_MULTI_QUERY_RETRIEVAL Fans out parallel query reformulations.
ENABLE_QUICK_LINKS Exposes the sidebar Quick Links list.
MANUAL_ESCALATION_EMAIL / MANUAL_ESCALATION_BUSINESS_AREA Enable the manual SME contact panel (both required).
DEFAULT_SEARCH_MODE azure_ai_search or agentic — the retrieval mode used when a request omits search_mode.
ACS_EMAIL_FROM_ADDRESS Per-org sender address for escalation emails (overrides the deploy default).

The Azure AD auth config (special case)

The five keys RA needs to validate a tenant's Azure AD tokens — the app client id, tenant id, issuer URL, JWKS URL, and which JWT claim carries the user id — are per-org config with no code or env fallback. An org missing any of them cannot authenticate real users, and a request against it returns 401 (not 500), since a token that cannot be validated is indistinguishable from an invalid one (middleware.py::JwtAuthBackend, config_service.py::resolve_azure_jwt_config).

Set them the same way as any other key. Locally, make run-dev / make run-prod seed these for the dev org automatically via the one-shot seed-org-auth-config Compose service, so you do not need to set them by hand for local development.

TODO(SME): confirm the canonical key names for the five Azure AD config values as stored in the organization_config blob (the README documents them by role — AZURE_APP_CLIENT_ID, AZURE_TENANT_ID, AZURE_ISSUER_URL, AZURE_JWKS_URL, USER_IDENTIFIER_PROPERTY — but verify these are the exact stored keys before publishing a copy-paste example).