Appearance
Configuring the workspace
This page documents the seven Administration screens that configure tenant-wide behavior: System Settings, Branding, Platform dictionary, Notification templates, API rate limits, Read replica routing and Chat flows. For the permission summary and conventions see Administration and DevOps overview.
System Settings
System Settings holds tenant-wide developer toggles. In this release it contains one switch: whether login (session) enforcement is applied to pages that require authentication. It is intended for faster development, for example opening a page in a second browser tab without signing in again. Tenant administrators and developers use it.
Where to find it: Workspace > Administration > System Settings. Page key system-settings.
Key concepts
| Term | Meaning |
|---|---|
| Session check | The enforcement that a page marked "Authenticated" requires a valid session. When off, login enforcement is skipped. |
| Tenant default | The value stored on the tenant record (session_check_enabled, default on). Every Application inherits it unless the Application overrides it in its own Details tab in Applications. |
Fields and options
Section Authentication:
| Field | Type or allowed values | Default | Required | Description |
|---|---|---|---|---|
| Session check (all users, this tenant) | Switch | On | No | Off skips login enforcement tenant-wide, for every "Authenticated" page whose own Application has not overridden the setting. The screen warns: for faster development only, never leave off in production. |
Actions
| Action | Effect | API |
|---|---|---|
| Toggle the switch | Saves immediately, reloads the tenant record and shows "Saved." The switch is disabled while saving | PUT /api/v1/branding/tenant-settings with {"sessionCheckEnabled": false} |
| Home | Returns to the Studio home | none |
Procedures
- Open System Settings.
- Turn Session check (all users, this tenant) off. The banner "Saved." appears.
- Open an Authenticated page in a new tab; it loads without a login prompt.
- Turn the switch on again before the tenant goes live.
Permissions
Reading is open. Writing requires branding:tenant-settings / update; without it the server answers 403 and the screen shows the error. The request names only sessionCheckEnabled, so it cannot overwrite branding values (absent fields are left untouched).
API and CLI
bash
erp api get /api/v1/branding/tenant-settings
erp api put /api/v1/branding/tenant-settings --body '{"sessionCheckEnabled":true}'Limits and behavior
- The same endpoint serves Branding and System Settings; a field set to
nullor omitted is not changed, an empty string clears a text override. - The effective value read by the runtime is returned by
GET /api/v1/branding/effectiveassessionCheckEnabled(true when the tenant record is missing).
Errors and troubleshooting
| Message or symptom | Cause | Fix |
|---|---|---|
| Error banner with an HTTP 403 text | Missing branding:tenant-settings / update | Grant the permission |
| Authenticated page still asks for login | The page's Application overrides the setting | Change the override in the Application's Details tab |
Branding
Branding sets this tenant's white-label identity: brand name, company font, logo images, and the color palette applied to the tenant's apps. An empty value means "use the platform default". Tenant administrators use it.
Where to find it: Workspace > Administration > Branding. Page key branding. For design-time themes see Themes.
Key concepts
| Term | Meaning |
|---|---|
| Stored tier | The raw values saved on the tenant record. The screen always edits this tier, never the blended result. |
| Effective branding | The stored value, or the platform default where the stored value is empty. Served by GET /api/v1/branding/effective. |
| Clear | An empty string sent on save removes an override (stored as null). |
Fields and options
Identity:
| Field | Type | Default | Required | Description |
|---|---|---|---|---|
| Brand name | Text (placeholder "ERP Studio") | Platform default ERP Studio | No | Name shown in the product chrome |
| Company font | Text (placeholder "Inter") | none | No | Font family name |
Color fields (text, placeholder #2563EB; a color swatch preview is shown). No format validation is applied by the server; use #RRGGBB.
| Group | Field | Platform default |
|---|---|---|
| Brand colors | Primary | #2563EB |
| Secondary | #06B6D4 | |
| Accent | #14B8A6 | |
| Semantic colors | Success | #22C55E |
| Warning | #F59E0B | |
| Danger | #EF4444 | |
| Info | #3B82F6 | |
| Surface & text | Background | #E7ECF3 |
| Surface | #FFFFFF | |
| Border | #E5E7EB | |
| Text primary | #111827 | |
| Text secondary | #6B7280 | |
| Interaction & chrome | Footer | #F1F5F9 |
| Divider | #E5E7EB | |
| Placeholder | #9CA3AF | |
| Disabled | #D1D5DB | |
| Link | #2563EB | |
| Selection | #BFDBFE | |
| Focus | #2563EB | |
| Hover | #F3F4F6 | |
| Chrome | Header (top bar) | none (theme decides) |
| Sidebar | none (theme decides) |
Images (each row shows a preview, the current URL or a hint, and the buttons Upload or Replace, and Clear when set):
| Field | Hint shown | Description |
|---|---|---|
| Logo | "Shown in the top bar and login screen." | Primary logo |
| Logo (dark mode) | "Used in place of Logo when dark mode is active." | Dark variant |
| Favicon | "Browser tab icon." | Tab icon |
| Login background | "Full-bleed image behind the login form." | Login page background |
The upload control accepts image files. The file is uploaded to the tenant's asset store immediately and its URL is placed in the field; the value is persisted only when you select Save Branding. The asset endpoint accepts PNG, JPEG, WebP, GIF and SVG (and several document types) up to 25 MB.
Actions
| Action | Effect | API |
|---|---|---|
| Upload / Replace | Uploads the file and fills the URL field | POST /api/v1/assets (multipart file) |
| Clear | Empties the image field (applied on save) | none until Save |
| Save Branding | Sends all fields (blank = clear) and reloads. Banner "Branding saved." | PUT /api/v1/branding/tenant-settings |
| Home | Returns to the Studio home | none |
Procedures
Apply a corporate palette:
- Open Branding. Enter Brand name
Northwind Traders, Company fontInter. - Under Brand colors set Primary
#0B5FFF, Secondary#00A3A3, Accent#FF7A00. - Under Images select Upload for Logo and choose
northwind-logo.png; the row shows the asset URL. - Select Save Branding. The banner "Branding saved." appears and the swatches reflect the saved values.
- To return the Accent color to the platform default, empty the field and save again.
Permissions
Reading the stored tier is open. Saving requires branding:tenant-settings / update; otherwise HTTP 403.
API and CLI
bash
erp api get /api/v1/branding/effective
erp api put /api/v1/branding/tenant-settings --body '{"brandName":"Northwind Traders","primaryColor":"#0B5FFF","accentColor":""}'Limits and behavior
- Fields omitted or
nullin the request are left unchanged; an empty string clears. The Studio screen always sends every field, so a Save from this screen rewrites all of them with what is shown. - The tenant tier only overrides platform defaults; an application theme can still apply on top at design time (see Themes).
Errors and troubleshooting
| Message or symptom | Cause | Fix |
|---|---|---|
Unsupported content type: x | The asset type is not allowed | Use PNG, JPEG, WebP, GIF or SVG |
file is required | Empty upload | Choose a file |
| Color does not change | Value not saved, or an application theme overrides the area | Save and check the Application's theme |
| Error 403 on save | Missing branding:tenant-settings / update | Grant the permission |
Platform dictionary
Platform dictionary lets a tenant search the platform's own terminology and customize the wording its users see, without changing the platform default. Only keys that a platform administrator has published to the catalog and marked overridable can be customized. Tenant administrators and localization owners use it.
Where to find it: Workspace > Administration > Platform dictionary. Page key platform-dictionary.
Key concepts
| Term | Meaning |
|---|---|
| Catalog entry | A translation key published for tenants: key, module, description, tags, default value per language and an Overridable flag. |
| Customer layer | The tenant's own value for a key and language. It takes precedence over the plugin and system layers. |
| Customized | The tenant has a value saved for this key and language. |
Fields and options
Toolbar:
| Field | Type or values | Default | Required | Description |
|---|---|---|---|---|
| Language | Select of languages as "Label (code)" | First language of the catalog | Yes | Language being browsed and edited |
| Search | Text (placeholder "key, description, module, or tag") | empty | No | Case-insensitive contains over key, description, module and tags |
Entry card:
| Element | Description |
|---|---|
| Module chip | The owning module |
| Customized chip (green) | A tenant value exists |
| Not overridable chip (lock icon) | The key may not be customized; the text field is disabled |
| Key | The translation key in monospace |
| Tag chips | Search tags |
| Description | Explains where the term appears |
| Value field | Pre-filled with the tenant value, else the platform default; placeholder is the default or "(no platform default yet)"; helper text "Platform default: ..." |
| Save | Enabled only when the value differs from the current one and the key is overridable |
| Revert icon (title "Revert to platform default") | Shown for customized entries; removes the tenant value |
Empty states: "Nothing has been published to the catalog yet - ask your platform administrator to publish keys from Translation Keys & Values." when the catalog is empty, and "No matching entries." when the search finds nothing.
Actions
| Action | Effect | API |
|---|---|---|
| Save | Stores the tenant value for the key and language | PUT /api/v1/authoring/platform-dictionary/{keyId} with {"languageCode":"fr","value":"..."} |
| Revert | Deletes the tenant value; resolution falls back to plugin and system layers | DELETE /api/v1/authoring/platform-dictionary/{keyId} with {"languageCode":"fr"} |
| Language change | Reloads the catalog for that language | GET /api/v1/authoring/platform-dictionary?languageCode=fr |
Procedures
Rename "Employee" to "Associate" for English users:
- Open Platform dictionary, choose Language
English (en)and searchemployee. - On the entry whose key is
hcm.employee.title(an example key), replace the value withAssociateand select Save. The Customized chip appears. - To undo, select the revert icon; the value returns to the platform default.
Permissions
Browsing is open. Save and Revert require the EDIT_TRANSLATIONS capability on translation for the actor.
API and CLI
bash
erp api get "/api/v1/authoring/platform-dictionary?languageCode=en"
erp api put /api/v1/authoring/platform-dictionary/412 --body '{"languageCode":"en","value":"Associate"}'Limits and behavior
- The catalog only lists keys that are public and overridable; the full internal key registry is visible to platform staff only.
- A customization is tenant-scoped and keyed by the key string, so it applies wherever that key is resolved.
- Deleting a customization that does not exist is not an error.
Errors and troubleshooting
| Message or symptom | Cause | Fix |
|---|---|---|
Unknown language code: xx | Language not defined | Pick a language from the list |
Translation key 'k' is not open for tenant override | Key not public or not overridable | Ask the platform administrator to publish it as overridable |
Unknown platform-wide translation key id N | Stale ID | Reload the screen |
| Save disabled | Key not overridable, or the value is unchanged | Edit the value |
Notification templates
Notification templates customizes the subject and body of the system notifications the platform sends (welcome e-mail, password reset, security alerts, document workflow notices). Each event has a built-in English text and, for most events, built-in translations. A tenant may override any of them, per language. Administrators and brand owners use it.
Where to find it: Workspace > Administration > Notification templates. Page key notification-templates.
Key concepts
| Term | Meaning |
|---|---|
| Event type | A notification the platform sends, for example WELCOME_EMAIL. |
| Locale | A language tag such as hi or pt-BR. The empty locale is the tenant's locale-agnostic default. |
| Override | The tenant's own subject and body for an event and locale (notification_template, one row per tenant, event type and locale). |
| Merge tag | A placeholder in double braces, replaced with real values when the notice is sent. |
Resolution order at send time: the recipient's locale override; then the tenant's locale-agnostic override; then the built-in translation for the language (matched by the language subtag, so hi-IN uses hi); then the built-in English text.
Built-in translations exist for ar, de, es, fr, hi, ja, pt, ru and zh. PASSWORD_RESET_REQUESTED has an English built-in only.
Fields and options
Event list (left):
| Event | Label | Merge tags |
|---|---|---|
WELCOME_EMAIL | Welcome Email | username |
PASSWORD_RESET | Password Reset (confirmation) | username |
PASSWORD_RESET_REQUESTED | Password Reset Requested | username, resetLink |
ACCOUNT_LOCKED | Account Locked | username |
PASSWORD_EXPIRY_REMINDER | Password Expiry Reminder | username |
LOGIN_ALERT | Login Alert | username, device, ipAddress |
NEW_DEVICE_ALERT | New Device Alert | username, device, ipAddress |
OTP_LOGIN | OTP Login Code | username, code |
DOCUMENT_CREATED | Document Created | ownerId, title, documentNumber |
DOCUMENT_APPROVAL_PENDING | Document Approval Pending | ownerId, title, documentNumber |
DOCUMENT_APPROVED | Document Approved | ownerId, title, documentNumber |
DOCUMENT_REJECTED | Document Rejected | ownerId, title, documentNumber |
DOCUMENT_EXPIRED | Document Expired | ownerId, title, documentNumber |
A "customized" chip marks events that have a locale-agnostic override. The API enumeration contains further event types (TENANT_PROVISIONING_COMPLETED, TENANT_PROVISIONING_FAILED, ACCOUNT_ACTIVATED, ACCOUNT_DEACTIVATED, ACCOUNT_UNLOCKED, ROLE_CHANGED) that this screen does not list.
Editor:
| Field | Type or values | Default | Required | Description |
|---|---|---|---|---|
| Locale select | "Locale-agnostic default", or a built-in language with the suffix "(built-in translation available)" | Locale-agnostic default | No | Which variant is being edited |
| Or a custom locale tag | Text, placeholder "e.g. pt-BR" | empty | No | Any other locale tag; stored as typed |
| Subject | Text | The override, else the built-in default | Yes in practice | Subject line |
| Body | Multi-line text | The override, else the built-in default | Yes in practice | Message body; line breaks are kept |
| Merge tags | Chips | none | No | Selecting a chip appends the tag to the Body |
| Origin chip | "Tenant override" or "Platform default" | derived | n/a | Shows whether an override exists for the selected event and locale |
Preview panel: shows the subject and body with each known merge tag displayed as the tag name between guillemets, for example the username tag appears as the word "username" in guillemets.
The built-in English text of Welcome Email, as an example of tag syntax:
text
Subject: Welcome to ERP, {{username}}
Body: Hi {{username}},
Your account has been created. You can now sign in with your username or email.
- ERPActions
| Action | Effect | API |
|---|---|---|
| Save | Creates or updates the override for the selected event and locale. Banner "Saved." | PUT /api/v1/notification-templates with {"eventType":"WELCOME_EMAIL","locale":null,"subject":"...","body":"..."} |
| Reset to Default | Deletes the override (enabled only when one exists). Banner "Saved." | DELETE /api/v1/notification-templates?eventType=WELCOME_EMAIL&locale= |
| Load defaults and overrides | Done on open | GET /api/v1/notification-templates/defaults, GET /api/v1/notification-templates?pageIndex=0&pageSize=500, GET /api/v1/notification-templates/defaults/{eventType}/locales |
Procedures
Customize the welcome e-mail in Hindi:
- Select Welcome Email. In the locale select choose
hi (built-in translation available). The editor shows the built-in Hindi text with the chip "Platform default". - Change the greeting in Body, keeping the tag for the user name. Use the username chip to insert it.
- Select Save. The chip becomes "Tenant override".
- To go back to the platform text, select Reset to Default.
Permissions
Reading defaults and overrides is open. Save and Reset require the AUTHOR capability on artifact type notification-template; otherwise the request is denied with a message of the form Actor "x" lacks AUTHOR on notification-template for tenant T.
API and CLI
bash
erp api get "/api/v1/notification-templates?pageIndex=0&pageSize=500"
erp api put /api/v1/notification-templates --body '{"eventType":"OTP_LOGIN","locale":"","subject":"Your sign-in code","body":"Your code is 123456"}'
erp api delete "/api/v1/notification-templates?eventType=OTP_LOGIN"Limits and behavior
- Substitution is a plain text replacement of the double-brace tag with the value supplied by the sender; a tag with no supplied value is left as written, and a null value becomes empty text.
- Delivery uses the configured channel. By default (
platform.notification.email.enabled=false) messages are written to the server log instead of being e-mailed; withplatform.notification.email.enabled=trueand a configured mail sender they are sent fromplatform.notification.email.from(defaultno-reply@erp.local). A failed send is logged and never fails the business action that triggered it. - An empty recipient address skips the notification silently.
Errors and troubleshooting
| Message or symptom | Cause | Fix |
|---|---|---|
| Reset to Default disabled | No override for this event and locale | Nothing to reset |
| Override not used for a user | The user's locale differs from the one edited | Create the locale variant, or edit the locale-agnostic default |
| Tag shows literally in the e-mail | The tag is not one the event supplies, or is misspelled | Use the merge-tag chips |
API rate limits
API rate limits overrides the platform-wide request ceiling for this tenant. The ceiling counts every call to /api/v1/** that carries the tenant header within the current one-minute window. Other tenants are unaffected. Tenant administrators use it to protect a tenant from runaway integrations or to give a busy tenant more headroom.
Where to find it: Workspace > Administration > API rate limits. Page key rate-limit-settings; the screen title reads "API Rate Limits".
Key concepts
| Term | Meaning |
|---|---|
| Platform default | The platform-wide ceiling, 1200 requests per minute by default. Set by the operator. |
| Platform switch | An operator setting, off by default. While it is false the tenant ceiling is not enforced for any tenant, whatever the tenant override says. |
| Tenant override | This tenant's own switch and limit per minute. |
Fields and options
Section Override:
| Field | Type | Default | Required | Description |
|---|---|---|---|---|
| Use this tenant's own limit instead of the platform default | Switch | Off (no override saved) | No | On applies the tenant value; off falls back to the platform default while keeping the saved row. A caption states "This tenant currently has a saved override row." or "This tenant has no override saved yet - it is using the platform default below." |
Section Ceiling:
| Field | Type | Default | Required | Description |
|---|---|---|---|---|
| Requests per minute for this tenant | Number | The platform default (shown in the helper text "Platform default: N/minute. Used automatically when the override switch above is off.") | Yes when the override is on | Disabled while the override switch is off. Must be greater than 0 |
Actions
| Action | Effect | API |
|---|---|---|
| Save Rate Limit Policy | Validates, saves, banner "Rate-limit policy saved." | PUT /api/v1/authoring/rate-limit/tenant-policy with {"enabled":true,"limitPerMinute":600} |
| Load | On open | GET /api/v1/authoring/rate-limit/tenant-policy |
Validation message on the screen: "Limit per minute must be a positive number." The server answers 400 for a limit of 0 or less.
Procedures
- Open API rate limits. The caption reads that no override is saved and the field shows 1200.
- Turn the switch on and enter
600. - Select Save Rate Limit Policy. The caption now states that an override row exists.
- When the tenant exceeds 600 requests in a minute, further calls receive HTTP 429 until the next minute starts.
Statuses and lifecycle
| State | Reads back as |
|---|---|
| No row | enabled: false, limitPerMinute = platform default, hasOverride: false |
Row with enabled: false | The platform default applies; the saved value is kept |
Row with enabled: true | The tenant value applies |
Permissions
Reading is open (it never returns 404). Saving requires rate-limit:tenant-policy / update; otherwise HTTP 403.
API and CLI
bash
erp api get /api/v1/authoring/rate-limit/tenant-policy
erp api put /api/v1/authoring/rate-limit/tenant-policy --body '{"enabled":true,"limitPerMinute":600}'Response:
json
{ "tenantId": 7, "enabled": true, "limitPerMinute": 600, "globalDefaultLimitPerMinute": 1200, "hasOverride": true }Limits and behavior
- The window is a fixed calendar minute (UTC, truncated to the minute); the counter is stored per tenant and window in
api_rate_limit_windowand shared by all server instances. - Only requests whose path starts with
/api/v1/and which carry a numericX-Tenant-Idare counted. - The rejection body is
{"code":"api-rate-limit-exceeded","message":"tenant 7 exceeded its 600/minute ceiling"}with status 429. - This ceiling is separate from per-rule limits attached to API access rules, which are enforced per actor and answer 429 with
Actor "x" exceeded the N/minute rate limit for PATH.
Errors and troubleshooting
| Message or symptom | Cause | Fix |
|---|---|---|
| "Limit per minute must be a positive number." | Empty, zero or negative input | Enter a whole number above 0 |
429 api-rate-limit-exceeded | Ceiling reached | Wait for the next minute, reduce call volume, or raise the ceiling |
| Saved limit has no effect | The platform switch enabled is false | Ask the operator to switch the platform ceiling on |
| 403 on save | Missing rate-limit:tenant-policy / update | Grant the permission |
Read replica routing
Read replica routing controls whether this tenant's eligible reads may be served by a healthy read replica instead of the primary database, and how a replica is chosen. A second block, Operational Config, tunes the health monitor and connection pools for the whole replica pool. Tenant administrators use the first block; platform operators use the second.
Where to find it: Workspace > Administration > Read replica routing. Page key read-replica-settings; the screen title reads "Read Replica Routing".
Key concepts
| Term | Meaning |
|---|---|
| Primary | The writable database. Writes, reads inside the read-after-write window (default 2 seconds after a write) and reads that explicitly require the primary always go to the primary, whatever this screen says. |
| Replica state | STARTING, HEALTHY, DEGRADED, LAGGING, UNAVAILABLE, DRAINING. Only HEALTHY replicas receive normal read traffic. |
| Feature switch | An operator setting. When off the module does not load: the tenant-policy endpoints answer 503 and the operational-config endpoints do not exist (404). |
| Platform default | An operator setting, off by default. A tenant is routed to replicas only when its own setting is On, or Inherit with the platform default on. |
| Load balancing | Set by the operator, health and latency weighted by default. Not editable on this screen. |
Fields and options
Tenant policy, section Enablement:
| Field | Type or values | Default | Required | Description |
|---|---|---|---|---|
| Enabled for this tenant | Select: "Inherit platform default" (null), "On - route eligible reads to a replica" (true), "Off - always route to primary" (false) | Inherit | No | An explicit On or Off always wins for this tenant |
Section Routing:
| Field | Type or values | Default | Required | Description |
|---|---|---|---|---|
| Read policy | Select | Local replica | No | See the next table |
| Home region | Text | default | No | Matched against each replica's region for the Local replica policy. Single-region deployments leave it as default |
Read policies:
| Value | Label | Behavior |
|---|---|---|
LOCAL_REPLICA | Local replica | Prefer a healthy replica in this tenant's home region, then any region, then the primary |
ANY_REPLICA | Any replica | Prefer any healthy replica regardless of region, then the primary |
PRIMARY_ONLY | Primary only | Never route this tenant's reads to a replica |
Operational Config (platform-wide, applied to every tenant, effective without a restart):
| Field | Type | Default (seeded from application.yml) | Minimum | Description |
|---|---|---|---|---|
| Health-check interval (seconds) | Integer | 5 | 1 | How often each replica is polled for connectivity and lag |
| Failure threshold | Integer | 3 | 1 | Consecutive failed pings before a replica is marked UNAVAILABLE rather than DEGRADED |
| Max replica lag (seconds) | Integer | 5 | 0 | A replica lagging beyond this is demoted to LAGGING and leaves the eligible pool |
| Per-tenant max pool size | Integer | 3 | 1 | Maximum pool size of each (replica, tenant) connection pool; applied live to pools already open |
The operational values are seeded once from configuration; later changes are made on this screen. The health monitor picks up new values at its next scheduled tick.
Actions
| Action | Effect | API |
|---|---|---|
| Save Read Replica Policy | Saves the tenant policy. Banner "Read-replica policy saved." | PUT /api/v1/authoring/read-replica/tenant-policy with {"enabled":true,"homeRegion":"in-east","readPolicy":"LOCAL_REPLICA"} |
| Save Operational Config | Saves the four values. Banner "Operational config saved - the health monitor picks up the new values on its next scheduled tick." | PUT /internal/read-replica/operational-config |
| Load | On open | GET /api/v1/authoring/read-replica/tenant-policy, GET /internal/read-replica/operational-config |
When the feature is off, the screen shows the warning "Read-replica routing is disabled platform-wide (switched off by the operator), so this tenant's policy can't be read or changed right now. Ask a platform operator to enable the feature first." and, for the operational block, a similar warning.
Procedures
Route a reporting tenant's reads to its regional replica:
- Open Read replica routing. Set Enabled for this tenant to "On - route eligible reads to a replica".
- Set Read policy to Local replica and Home region to
in-east. - Select Save Read Replica Policy.
- Reads that are not in a recent-write window and do not require the primary are now eligible for a healthy
in-eastreplica.
Make failover detection quicker (operators): set Health-check interval to 2 and Failure threshold to 2, then select Save Operational Config.
Permissions
Reading the tenant policy is open (an unset tenant reads back enabled: null, homeRegion: "default", readPolicy: LOCAL_REPLICA). Saving it requires read-replica:tenant-policy / update; otherwise HTTP 403. The operational-config endpoints do not check a permission; restrict this screen to platform operators.
API and CLI
bash
erp api get /api/v1/authoring/read-replica/tenant-policy
erp api put /api/v1/authoring/read-replica/tenant-policy --body '{"enabled":true,"homeRegion":"in-east","readPolicy":"LOCAL_REPLICA"}'
erp api get /internal/read-replica/operational-config
erp api get /internal/read-replica/statusGET /internal/read-replica/status returns the feature flags, the load-balancing strategy, each replica with its state, lagSeconds and lastLatencyMillis, and all tenant policies. Operators can also force a replica state with POST /internal/read-replica/{name}/force-state/{state} and clear it with POST /internal/read-replica/{name}/clear-force.
Limits and behavior
- The operational PUT merges: a field that is omitted keeps its current value.
- Replica selection never changes what a user is authorized to see; it only chooses the physical connection.
- A read that must be answered by a replica and finds none available fails instead of falling back to the primary.
Errors and troubleshooting
| Message or symptom | Cause | Fix |
|---|---|---|
| Warning "Read-replica routing is disabled platform-wide" | Feature switch off (HTTP 503 or 404) | Operator switches read-replica routing on |
healthCheckIntervalSeconds must be >= 1 | Value below minimum | Enter 1 or more |
healthCheckFailureThreshold must be >= 1 | Same | Enter 1 or more |
maxReplicaLagSeconds must be >= 0 | Negative value | Enter 0 or more |
perTenantMaxPoolSize must be >= 1 | Value below minimum | Enter 1 or more |
| 403 on tenant policy save | Missing read-replica:tenant-policy / update | Grant the permission |
| Policy saved but reads stay on the primary | Replicas not HEALTHY, lag above the budget, or the read is inside the write window | Check /internal/read-replica/status |
Chat flows
Chat flows defines branching conversation trees made of message, question, condition and end nodes, and tests them in a live conversation. A chat flow is a versioned artifact; a session is a real run of the flow against the execution engine, stored with its transcript. Bot designers and support administrators use it. For voice and phone bots built on agents see the AI Studio designer pages.
Where to find it: Workspace > Administration > Chat flows. Page key chat-flows.
Key concepts
| Term | Meaning |
|---|---|
| Flow definition | A JSON document startNodeId plus nodes. |
| Node | One step with a unique id and a type. |
| Session | One conversation (chat_session), status active or ended, with its variables and message transcript. |
| Variable | A value captured by a question node; substituted into later texts. |
| Artifact lifecycle | draft, published, deprecated, archived; a published version is immutable. |
Fields and options
List panel "Chat flows": each row shows the name and "vN - status". New opens a blank editor.
Editor:
| Field | Type | Default | Required | Description |
|---|---|---|---|---|
| Name | Text (create only) | empty | Yes | Artifact name; the error "Name is required" appears otherwise. Names are unique per version |
| Flow definition JSON | Multi-line JSON | A greeting flow (below) | Yes | Must be valid JSON ("Definition must be valid JSON") |
| Status chip | "vN - status" | n/a | n/a | Current version and status |
Node types and properties:
| Type | Properties | Behavior |
|---|---|---|
message | id, text, next | Emits text after variable substitution, then continues to next. If next is missing the conversation ends. |
question | id, text, variable, next | Emits text and waits for the user. The reply is stored in variable, then the flow continues to next (the conversation ends if next is missing). |
condition | id, expression, trueNext, falseNext | Evaluates expression against the variables with the platform formula evaluator; only the supported formula subset and whitelisted formula functions are accepted. Continues to trueNext when the result is true, otherwise falseNext; ends when the chosen target is missing. A failing expression stops with the message Condition node "id" expression failed: .... |
end | id, optional text | Emits the optional text and ends the conversation. |
Variable substitution replaces each double-brace variable name in a text with the stored value. Variables hold the text the user typed.
Default definition offered for a new flow:
json
{
"startNodeId": "greet",
"nodes": [
{ "id": "greet", "type": "message", "text": "Hi! What's your name?", "next": "askName" },
{ "id": "askName", "type": "question", "variable": "name", "next": "thanks" },
{ "id": "thanks", "type": "message", "text": "Nice to meet you, {{name}}!", "next": "done" },
{ "id": "done", "type": "end" }
]
}Conversation panel ("Conversation", with an "ended" chip when the session has ended): bot messages on the left, user messages on the right, a reply box with placeholder "Type a reply..." and Send (Enter also sends). The box is hidden once the session has ended.
Actions
| Action | Effect | API |
|---|---|---|
| Create | Creates a draft | POST /api/v1/authoring/chat-flows with name, definitionJson |
| Save draft | Saves the draft using the revision shown (optimistic locking) | PUT /api/v1/authoring/chat-flows/{id}/draft with expectedRevision |
| Publish | Publishes the draft (shown for drafts only) | POST /api/v1/authoring/chat-flows/{id}/publish |
| Try it | Starts a session and shows the first bot messages | POST /api/v1/chat-flows/{id}/sessions |
| Send | Sends a reply | POST /api/v1/chat-flows/sessions/{sessionId}/messages with {"message":"Ana"} |
| (API only) transcript | Full message history | GET /api/v1/chat-flows/sessions/{sessionId}/transcript |
The screen does not offer clone, new version, deprecate, archive, delete or history. These exist on the API (POST .../{id}/clone, /new-version, /deprecate, /archive, DELETE .../{id}, GET .../name/{name}/history) and through the CLI.
Procedures
Build and test a lead-qualification flow:
- Select New, enter Name
lead-qualifier. - Replace the JSON with a flow that asks for a plan and branches:
json
{
"startNodeId": "ask",
"nodes": [
{ "id": "ask", "type": "question", "text": "Which plan are you interested in: basic or enterprise?", "variable": "plan", "next": "check" },
{ "id": "check", "type": "condition", "expression": "plan == \"enterprise\"", "trueNext": "big", "falseNext": "small" },
{ "id": "big", "type": "end", "text": "Thanks, a specialist will call you." },
{ "id": "small", "type": "end", "text": "Thanks, see our self-service plans." }
]
}- Select Create, then Try it. The bot asks for the plan; reply
enterprise; the conversation ends with the specialist message. - Select Publish when satisfied.
Statuses and lifecycle
Flow artifact: draft to published to deprecated or archived. Editing a non-draft is rejected with chat_flow definition N is PUBLISHED - published definitions are immutable; create a new version instead. Sessions: active while waiting on a question, ended after an end node or a missing next.
Permissions
Authoring calls need AUTHOR (create, save, clone, new version) or PUBLISH (publish, deprecate, archive, delete) on artifact type chat_flow. Starting a session, replying and reading a transcript are not permission-checked in this release.
API and CLI
bash
erp artifact list --type chat-flows
erp artifact create --type chat-flows --name lead-qualifier --file lead-qualifier.json
erp artifact publish --type chat-flows --id 12
erp api post /api/v1/chat-flows/12/sessions
erp api post /api/v1/chat-flows/sessions/301/messages --body '{"message":"enterprise"}'erp plugin op artifact-create --type chat-flows ... performs the same through the plugin-builder operations (artifact-list, artifact-get, artifact-history, artifact-create, artifact-update, artifact-publish, artifact-delete).
Limits and behavior
- One turn follows at most 50 automatic steps (messages and conditions). Exceeding it returns
Chat flow exceeded 50 auto-advance steps - likely a cycle with no question/end node. - The definition is stored as written; structure is checked when the flow runs, not when it is saved.
- Sessions record each bot and user message with the node that produced it.
Errors and troubleshooting
| Message or symptom | Cause | Fix |
|---|---|---|
Chat flow has no valid startNodeId | startNodeId missing or not a node id | Correct the id |
Chat flow references unknown node id: x | A next, trueNext or falseNext points nowhere | Fix the id |
Unknown node type "x" at node "id" | Type not one of the four | Use message, question, condition or end |
Session N has already ended | Reply sent to an ended session | Start a new session with Try it |
Session is not currently waiting on a question node | Reply sent while no question is pending | Start a new session |
No such chat session: N | Wrong session ID | Use the ID returned by start |
Definition must be valid JSON | Syntax error in the editor | Fix the JSON |
