Skip to content

Portal administration

The Arbitex cloud portal provides a unified administrative interface for managing users, policies, models, and organizational settings. This guide covers the cross-cutting features that affect every part of the portal: global search, the analytics dashboard, theme and dark mode settings, accessibility features, and day-to-day administrative workflows. For feature-specific docs, follow the cross-references at the end of each section.


Global search lets you find any entity in your organization without navigating through individual admin sections. It searches across all entity types simultaneously and returns results grouped by category.

You can open global search in three ways:

Method Shortcut
Keyboard shortcut (Mac) Cmd+K
Keyboard shortcut (Windows / Linux) Ctrl+K
Mouse Click the search icon in the header bar

The search overlay opens immediately. Your cursor is placed in the query field.

Search begins after you have typed at least 2 characters. Results update automatically as you type, with a 300 ms debounce on keystrokes to avoid excessive API calls while you are still forming your query.

When you open search with an empty query, the overlay displays your recently viewed items — up to 10 items from your navigation history — so you can quickly return to entities you were working on.

The following entity types are searched simultaneously. Results for each type appear in a labeled group with a count badge.

Entity type Icon Searchable fields Result subtitle
Users Person Display name, email address, user ID Email address
Groups People Group name, description Member count
Models Cube Model name, provider, model ID Provider name
Conversations Chat bubble Title, first message content User display name
Audit Events Shield Event type, resource ID, IP address Timestamp
DLP Rules Lock Rule name, pattern name, description Rule category
Policy Rules Scales Rule name, conditions summary, description Policy group name
API Keys Key Key name, prefix, description Created by
Webhooks Webhook Webhook name, endpoint URL, description Event types
Organizations Building Org name, slug, domain (multi-tenant) Org ID

Each result item shows:

  • Icon — entity type icon (color-coded by type)
  • Title — primary identifier (name, email, or ID)
  • Subtitle — secondary context field (entity type label and matched value)
  • Highlight — the matched portion of text is highlighted in the result

You do not need to use the mouse once search is open:

Key Action
/ Move between results
Enter Open the highlighted result
Esc Close search overlay
Tab Move to the next result group header
Shift+Tab Move to the previous result group header

Your last 10 search queries are saved to localStorage and displayed beneath the search field when the overlay opens. Click a recent search term to re-run it immediately. Recent searches are per-browser and are not synced to your user profile.

When you open a result or press Enter on a query, the portal navigates to:

/admin/search?q={query}

You can bookmark or share this URL. Opening it loads the full search results page with the query pre-filled. The search results page supports adding a types parameter to filter by entity type:

/admin/search?q=alice&types=users,groups

The portal search UI calls:

GET /v1/portal/search?q={query}&types={comma-separated}&limit=20
Parameter Type Description
q string Search query (minimum 2 characters)
types string Comma-separated entity types to include; omit for all types
limit integer Maximum results per type (default 20, max 50)

The response groups results by entity type, each with a count and items array. See Portal Search API for the full response schema and filtering options.

An example response for the query alice:

{
"query": "alice",
"total_results": 4,
"groups": [
{
"type": "users",
"count": 2,
"items": [
{
"id": "usr_01JXXXXXXXXXX",
"title": "Alice Johnson",
"subtitle": "[email protected]",
"matched_field": "display_name",
"url": "/admin/users/usr_01JXXXXXXXXXX"
}
]
},
{
"type": "api_keys",
"count": 1,
"items": [
{
"id": "key_01JXXXXXXXXXX",
"title": "Alice's automation key",
"subtitle": "Created by [email protected]",
"matched_field": "name",
"url": "/admin/api-keys/key_01JXXXXXXXXXX"
}
]
}
]
}

The analytics dashboard gives you a consolidated view of your organization’s AI usage across all models, users, and time periods. Navigate to Admin → Analytics in the sidebar.

The top row shows four summary cards. All four load in parallel on page open; a failure in one card does not block the others.

Card Metric Notes
Total Requests Count of all API requests in the selected period Includes retries; does not deduplicate
Total Tokens Sum of input + output tokens Displayed as combined total with breakdown on hover
Active Users Distinct users who made at least one request Unique by user ID
Estimated Cost USD cost estimate based on model pricing Uses model list prices; does not include negotiated discounts

Each card shows the current period value and a delta badge comparing it to the previous equivalent period (for example, previous 7 days when 7-day period is selected).

Below the overview cards, a tabbed chart section provides five different views of your usage data.

A line chart showing requests per time bucket across the selected period.

Period Granularity
7 days Per day
30 days Per day
90 days Per week
Custom (≤ 31 days) Per day
Custom (> 31 days) Per week

The X axis is time. The Y axis is request count. Hover over any point to see the exact date and request count in a tooltip.

CSV export fields: date, request_count, period_granularity

Use the period selector at the top-right of the dashboard to change the time range. Changes apply to all cards and charts simultaneously.

Option Range
7d Last 7 days (rolling)
30d Last 30 days (rolling)
90d Last 90 days (rolling)
Custom Date picker for any start and end date

For the custom range, click the start date then the end date in the calendar picker. The maximum custom range is 365 days.

Enable the Auto-refresh toggle (top-right of the dashboard) to poll for updated data every 60 seconds. Auto-refresh automatically pauses when the browser tab is hidden (for example, when you switch to another tab), preventing unnecessary API calls. It resumes when the tab becomes visible again.

The last-refreshed timestamp displays next to the toggle and updates after each successful refresh.

Each chart has an Export CSV button that downloads the underlying data for that chart. The export reflects the currently selected period and any active filters. Exports include a header row and are encoded as UTF-8 with CRLF line endings for Excel compatibility.

Each card and chart section has independent loading and error handling:

  • Loading state: A skeleton placeholder (grey animated shimmer) replaces the content while data is fetching.
  • Error state: An inline error message with a Retry button replaces the content if the request fails. Retrying one section does not reload others.

The dashboard is responsive — charts resize and reflow when the browser window width changes. On narrow viewports (< 768 px), the overview cards stack vertically and chart tabs scroll horizontally.

All analytics data is sourced from:

GET /v1/orgs/{org_id}/analytics/dashboard?period={period}&start={start}&end={end}
Parameter Description
period 7d, 30d, or 90d
start ISO 8601 date (custom range only)
end ISO 8601 date (custom range only)

Example request for a 30-day rolling period:

Terminal window
curl -s -X GET \
"https://platform.example.com/v1/orgs/${ORG_ID}/analytics/dashboard?period=30d" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
| jq '.overview'

Example response excerpt:

{
"overview": {
"total_requests": 1234567,
"total_tokens": 987654321,
"active_users": 142,
"estimated_cost_usd": 2481.50
},
"period": {
"label": "30d",
"start": "2026-02-12",
"end": "2026-03-14"
}
}

For detailed usage dashboard documentation including current billing period and alert history, see Usage Dashboard.


The portal supports three theme modes to match your visual preferences and environment. Theme selection is available from the theme icon in the top-right header bar.

Mode Behavior
Light Portal always displays with a light background and dark text
Dark Portal always displays with a dark background and light text
System Follows your operating system’s prefers-color-scheme setting, switching automatically when you change your OS appearance
  1. Click the theme icon (sun/moon) in the top-right header bar.
  2. A dropdown menu opens with three options: Light, Dark, System.
  3. Click your preferred option. The change applies immediately — no page reload required.

The dropdown is fully keyboard-accessible: Tab to the icon button, press Enter or Space to open the dropdown, then use Tab or arrow keys to navigate the options.

Your theme selection is saved in two places:

Storage Scope Description
localStorage Current browser Immediate persistence; survives page reloads
profile_settings.theme User profile (server-side) Syncs across devices when you log in on a new browser or device

The stored values are "light", "dark", or "system". When you log in, the profile value is applied on first render if no localStorage value is present for that browser.

The theme is applied by setting the dark class on the root <html> element and the data-theme attribute. All color tokens are defined as CSS custom properties (--color-bg, --color-text, etc.) and swap values based on these selectors. Tailwind’s dark mode variant (dark:) responds to the dark class on <html>.

This means:

  • All components automatically adapt to the active theme without per-component logic.
  • Custom CSS injected via organization branding must use the same CSS custom property tokens to participate in theme switching correctly.

Organization-level branding overrides (primary color, logo, favicon) apply in both light and dark modes. The logo is displayed as-is; we recommend providing logos with transparent backgrounds that work on both light and dark surfaces. See Theme Configuration for full details on org-level branding, CSS custom properties, and the 22 built-in brand themes.

The key CSS custom properties that change between light and dark mode are:

/* Light mode defaults */
:root {
--color-bg: #ffffff;
--color-bg-subtle: #f8fafc;
--color-border: #e2e8f0;
--color-text: #0f172a;
--color-text-muted: #64748b;
--color-primary: #4f46e5; /* overridden by org branding */
}
/* Dark mode overrides (applied when html.dark or [data-theme="dark"]) */
html.dark {
--color-bg: #0f172a;
--color-bg-subtle: #1e293b;
--color-border: #334155;
--color-text: #f1f5f9;
--color-text-muted: #94a3b8;
--color-primary: #818cf8; /* lighter tint for dark bg contrast */
}

If your organization uses a custom CSS injection (configured in Admin → Settings → Appearance → Custom CSS), reference these variables rather than hardcoding hex values so that your styles adapt correctly to both themes.


The Arbitex portal targets WCAG 2.1 Level AA compliance across all pages. This section documents the keyboard navigation model, screen reader support, and other accessibility behaviors so that administrators can verify and communicate accessibility capabilities to their teams.

All interactive elements in the portal are reachable and operable via keyboard alone.

  • The Tab key moves focus forward through interactive elements in the order they appear visually on the page.
  • Shift+Tab moves focus backward.
  • The tab order matches the visual layout — you will not experience unexpected focus jumps between sections.

All focused elements display a 2 px solid ring focus indicator. The ring color provides sufficient contrast against both light and dark backgrounds:

  • Light mode: dark indigo ring (#4338CA)
  • Dark mode: sky blue ring (#38BDF8)

Focus indicators are never hidden with outline: none or outline: 0 — if focus is invisible on an element, report it as a bug.

Every page includes a skip-to-content link as the first focusable element. It is visually hidden until it receives focus, at which point it appears in the top-left corner. Pressing Enter on the link moves focus directly to the main content area, bypassing the navigation sidebar and header.

When a modal dialog or drawer is open, Tab and Shift+Tab cycle only through focusable elements inside the modal. Focus cannot leave the modal while it is open. Press Esc to close the modal and return focus to the element that opened it.

Context Key Action
Dropdown menu Enter / Space Open menu
Dropdown menu / Move between menu items
Dropdown menu Enter Activate selected item
Dropdown menu Esc Close menu, return focus to trigger
Combobox / select / Move through options
Combobox / select Home / End Jump to first / last option

Tables in the portal support arrow key cell navigation when a table cell is focused:

Key Action
Move to next cell in row
Move to previous cell in row
Move to same column in next row
Move to same column in previous row
Home Move to first cell in row
End Move to last cell in row
Ctrl+Home Move to first cell in table
Ctrl+End Move to last cell in table
Shortcut Action
Cmd+K / Ctrl+K Open global search
Esc Close modal, overlay, or dropdown
? Open keyboard shortcuts reference overlay
G then H Navigate to Home / Dashboard
G then U Navigate to Users
G then P Navigate to Policy Rules
G then D Navigate to DLP Rules
G then A Navigate to Audit Log

Each page is structured with the following ARIA landmark regions:

Landmark Role Contains
banner role="banner" Portal header, logo, search, user menu
navigation role="navigation" Sidebar nav with aria-label="Main navigation"
main role="main" Page content area
complementary role="complementary" Context panels, filter sidebars
contentinfo role="contentinfo" Footer with version and legal links

Screen reader users can jump directly between landmarks using their screen reader’s landmark navigation shortcut (typically D in NVDA, F6 in JAWS, W in VoiceOver).

The portal uses aria-live regions to announce content changes that happen without a page reload:

Region aria-live value What it announces
Status messages polite Success and info toasts
Error messages assertive Error toasts and validation failures
Loading states polite “Loading…” when async content starts fetching
Search results polite Result count after each search query
Auto-refresh polite “Dashboard updated” after auto-refresh completes
  • Every <input>, <select>, and <textarea> has an associated <label> element connected via id / for attributes.
  • Required fields are marked with aria-required="true" in addition to any visual asterisk.
  • Validation error messages are linked to their input via aria-describedby so screen readers announce the error when the field is focused.
  • Placeholder text is not used as a substitute for a label.
  • All informational images have descriptive alt text.
  • Decorative images have alt="" so screen readers skip them.
  • Icon-only buttons (such as the search icon and theme toggle) have aria-label attributes that describe their action.
  • Status indicator icons (for example, active/inactive badges) include visually hidden text for their state.
  • Column headers use <th scope="col">.
  • Row headers (where present) use <th scope="row">.
  • Complex tables with multi-level headers use aria-labelledby to associate header cells with data cells.

Because charts are inherently visual, each chart in the analytics dashboard has a companion data table view accessible via a “View as table” toggle below the chart. The table presents the same underlying data in a format that screen readers can navigate. The CSV export button is also accessible and provides an equivalent data download.

All text in the portal meets the WCAG 2.1 AA minimum contrast ratio:

Context Minimum ratio Portal target
Normal text (< 18 pt or < 14 pt bold) 4.5 : 1 ≥ 4.5 : 1
Large text (≥ 18 pt or ≥ 14 pt bold) 3 : 1 ≥ 4.5 : 1
UI components and focus indicators 3 : 1 ≥ 3 : 1

Status badge colors (green for active, red for blocked, amber for warning) are not the sole indicator of state — each badge also includes a text label.

The portal respects the prefers-reduced-motion CSS media query. When a user has enabled the “Reduce motion” setting in their operating system:

  • Animated skeleton loaders are replaced with static placeholders.
  • Chart entry animations are disabled.
  • Toast notifications appear and disappear without sliding animations.
  • Page transition effects are disabled.

Hover and focus state transitions (color changes, underlines) are not affected, as these are sub-100 ms changes that do not cause vestibular issues.

When the portal navigates to a new page (via the sidebar, breadcrumbs, or in-app links), focus is automatically moved to the page’s <h1> heading. This ensures screen reader users are immediately oriented to the new page context rather than remaining focused on the navigation link they clicked.


The Admin → Settings page provides a set of portal-wide controls that affect all users in your organization. This section documents the key administrative tasks available from the Settings page and other admin surfaces.

Navigate to Admin → Settings to access organization-wide configuration. Settings are organized into sections:

Section Description
General Organization name, slug, support email
Security Session timeout, MFA enforcement, IP allowlist
Appearance Logo upload, favicon, primary color override
Notifications Announcement banner, email notification preferences
Maintenance Maintenance mode toggle and message
Danger Zone Delete organization, transfer ownership

You can view and manage all active sessions for your organization from Admin → Settings → Security → Active Sessions.

The session list shows:

Column Description
User Display name and email
IP Address Source IP with anonymous IP flags (if enabled)
Browser / Device User agent parsed to browser and OS
Location GeoIP-resolved city and country
Last Active Timestamp of most recent API call
Session Start When the session was created

Sessions are sorted by Last Active descending (most recent first). Use the search box above the table to filter by user name or email.

To terminate a specific session:

  1. Find the session in the Active Sessions list.
  2. Click the Revoke button at the end of the session row.
  3. Confirm the action in the confirmation dialog.

The session token is immediately invalidated. The user’s next API call or page load will return a 401 and redirect them to the login page.

To force logout all sessions for a specific user, navigate to Admin → Users, open the user’s profile, and click Revoke All Sessions in the Security section of the user detail panel.

You can display an organization-wide notification banner at the top of every portal page. This is useful for communicating scheduled maintenance, policy changes, or urgent notices to all users.

  1. Navigate to Admin → Settings → Notifications.
  2. In the Announcement Banner section, enter your message in the text field. Markdown is not supported — plain text only.
  3. Select a severity level from the dropdown: Info (blue), Warning (amber), or Critical (red).
  4. Optionally set an expiry date and time. The banner will automatically clear at the specified time.
  5. Click Save Banner. The banner becomes visible to all users immediately.

The banner appears below the header bar on every page. Users can dismiss the banner individually — their dismissal is stored per-session and does not affect other users.

To remove the banner before its expiry:

  1. Navigate to Admin → Settings → Notifications.
  2. In the Announcement Banner section, click Clear Banner.
  3. The banner is removed immediately for all users (including users who have not yet seen it).

Maintenance mode disables the portal and API for all non-admin users, displaying a customizable maintenance page.

  1. Navigate to Admin → Settings → Maintenance.
  2. Enter a message in the Maintenance Message field. This message is displayed on the maintenance page and in the 503 response body.
  3. Optionally set an Expected End Time. This is shown on the maintenance page as an estimated resumption time.
  4. Click Enable Maintenance Mode.
  5. Confirm in the dialog that you understand all non-admin users will be blocked.

While maintenance mode is active, a red banner appears at the top of all admin portal pages to remind administrators that maintenance mode is on.

  1. Navigate to Admin → Settings → Maintenance.
  2. Click Disable Maintenance Mode.
  3. The portal becomes available to all users immediately.
Request type Response
Admin users (portal UI) Allowed — full portal access
Admin API calls (with admin token) Allowed
Non-admin portal UI Redirected to maintenance page
Non-admin API calls 503 Service Unavailable with maintenance message in response body
Health check endpoint (/health) 200 OK — always available

The portal version number is displayed in the footer at the bottom of every portal page. The version follows the format cloud-NNNN (for example, cloud-0042).

When contacting Arbitex support, include the portal version from the footer in your ticket. This allows the support team to check the changelog for your version and identify whether your issue is a known defect or expected behavior.

The API version in use is separately visible in Admin → Settings → General under the Platform Information section, which lists:

Field Description
Portal version Cloud portal release identifier
API version Platform API version string
Org ID Your organization’s unique identifier
Region Cloud region where your data is stored
Outpost connected Whether a self-hosted outpost is registered

Super-admins can create, suspend, activate, and delete tenant organizations via the Admin API. All mutating operations require an X-API-Key header carrying a platform admin key.

API key format: arb_ prefix followed by 32 random bytes encoded as base64url. Keys are SHA-256 hashed before storage — the plaintext is only shown once at creation time.

An organization moves through the following statuses over its lifetime:

Status Description
pending Provisioning in progress; API access not yet available
active Fully operational; all API access enabled
suspended API access disabled; data preserved
cancelled Soft-deleted; data retained for 90 days before permanent removal
Tier identifier Description
devfree_saas Free developer tier (default on create)
devpro_saas Pro developer tier
team_saas Team tier
enterprise_saas Enterprise cloud tier
enterprise_outpost Enterprise self-hosted outpost tier
POST /v1/admin/orgs
Field Type Required Notes
name string Yes 1–128 characters
slug string Yes Pattern ^[a-z0-9-]{2,64}$; 409 if already taken
plan_tier string No Defaults to devfree_saas
contact_email string Yes Primary contact address
billing_email string No Separate billing address; falls back to contact_email

A portal_activity audit record is created automatically. The response includes a provisioning_status field that reflects whether background provisioning tasks (DNS, key generation) have completed.

Terminal window
curl -s -X POST "https://platform.example.com/v1/admin/orgs" \
-H "X-API-Key: ${ADMIN_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"slug": "acme-corp",
"plan_tier": "team_saas",
"contact_email": "[email protected]",
"billing_email": "[email protected]"
}' | jq '.'
PUT /v1/admin/orgs/{org_id}/suspend

Sets status to suspended. All API access is immediately disabled. Data and configuration are preserved. Returns 409 if the organization is not currently active. Writes an org_suspended audit event.

Terminal window
curl -s -X PUT "https://platform.example.com/v1/admin/orgs/${ORG_ID}/suspend" \
-H "X-API-Key: ${ADMIN_KEY}" | jq '.'
PUT /v1/admin/orgs/{org_id}/activate

Sets status to active. API access is restored immediately. Returns 409 if the organization is not currently suspended. Writes an org_activated audit event.

Terminal window
curl -s -X PUT "https://platform.example.com/v1/admin/orgs/${ORG_ID}/activate" \
-H "X-API-Key: ${ADMIN_KEY}" | jq '.'
DELETE /v1/admin/orgs/{org_id}

Soft-deletes the organization by setting status to cancelled. Data is retained for 90 days before permanent removal. Returns 409 if the organization is already cancelled. Writes an org_deleted audit event.

Terminal window
curl -s -X DELETE "https://platform.example.com/v1/admin/orgs/${ORG_ID}" \
-H "X-API-Key: ${ADMIN_KEY}" | jq '.'

An organization owner can request deletion without admin intervention:

POST /v1/orgs/{org_id}/request-deletion

Authenticates with an org-scoped JWT (not an admin key). The request body must include the organization name spelled exactly (case-insensitive) as a confirmation step.

Terminal window
curl -s -X POST "https://platform.example.com/v1/orgs/${ORG_ID}/request-deletion" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"org_name": "Acme Corp"}' | jq '.'

All lifecycle endpoints return an AdminOrgLifecycleResponse object:

Field Type Description
org_id string Organization identifier
status string New status after the operation
action string Operation performed (e.g., suspended, activated, deleted)
message string Human-readable summary
performed_at string ISO 8601 timestamp of when the action was applied

Arbitex enforces quotas at two distinct layers: plan-level enforcement in Platform and display limits shown in the billing dashboard.

These limits are enforced by the Platform API. Requests that exceed the monthly limit receive a 429 Too Many Requests response with a Retry-After header.

Plan Monthly request limit
devfree_saas 100,000
devpro_saas 1,000,000
team_saas 1,000,000
enterprise_saas 1,000,000 (overridable via enterprise_entitlements.custom_request_limit)
enterprise_outpost 1,000,000 (overridable via enterprise_entitlements.custom_request_limit)

The values shown in the portal billing dashboard differ from the enforcement limits. These are the figures displayed to users in their quota gauges:

Plan API calls DLP scans Data (GB) Seats
devfree_saas 10,000 5,000 1 1
devpro_saas 250,000 100,000 10 3
team_saas 2,000,000 1,000,000 100 20
enterprise_saas Unlimited Unlimited Unlimited Unlimited
enterprise_outpost Unlimited Unlimited Unlimited Unlimited

Usage alerts fire at 50%, 80%, 95%, and 100% of the monthly limit. The default thresholds are [50, 80, 95, 100] and can be overridden via the USAGE_ALERT_THRESHOLDS environment variable on the Platform service.

Alerts are de-duplicated per (org_id, threshold_pct) per billing period — you will receive each alert at most once per period. Alert type identifiers map to thresholds as follows:

Alert type Threshold Warning level
warning_50 50% warning
warning_80 80% warning
warning_95 95% critical
limit_reached 100% blocked

Check the current quota status for an organization:

GET /v1/orgs/{org_id}/usage/quota
Terminal window
curl -s "https://platform.example.com/v1/orgs/${ORG_ID}/usage/quota" \
-H "Authorization: Bearer ${TOKEN}" | jq '.'

The response fields:

Field Type Description
plan_tier string Active plan identifier
api_calls_used integer Requests consumed in the current billing period
api_calls_limit integer Monthly request cap for this plan
api_calls_percent number api_calls_used / api_calls_limit * 100
seats_used integer Active seat count
seats_limit integer Seat cap for this plan
seats_percent number seats_used / seats_limit * 100
warning_level string normal | warning | critical | blocked
billing_period_start string ISO 8601 start of current billing period
billing_period_end string ISO 8601 end of current billing period

Warning level thresholds:

Level Condition
normal Usage below 75%
warning Usage 75%–89%
critical Usage 90%–99%
blocked Usage at or above 100%

Individual users and groups can have granular quota policies applied. A value of null means unlimited.

Field Description
daily_token_limit Maximum tokens per calendar day
monthly_token_limit Maximum tokens per billing month
daily_request_limit Maximum API requests per calendar day
monthly_request_limit Maximum API requests per billing month
daily_cost_limit_usd Maximum estimated cost per calendar day (USD)
monthly_cost_limit_usd Maximum estimated cost per billing month (USD)

Set a quota policy on a specific user:

Terminal window
curl -s -X PUT \
"https://platform.example.com/v1/orgs/${ORG_ID}/users/${USER_ID}/quota" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"daily_request_limit": 500,
"monthly_request_limit": 10000,
"monthly_token_limit": 5000000,
"daily_cost_limit_usd": 2.00,
"monthly_cost_limit_usd": 25.00
}' | jq '.'

Organization settings control security policies, session behavior, IP access rules, DLP fail mode, audit retention, and custom request headers. Org admins can read and update settings via the API or through Admin → Settings in the portal.

GET /v1/orgs/{org_id}/settings
PUT /v1/orgs/{org_id}/settings

Both endpoints require an org-scoped JWT. PUT requires the org_admin role.

Field Type Default Description
org_name string Display name of the organization
org_slug string URL-safe slug (read-only after creation)
contact_email string | null Primary contact address
timezone string UTC IANA timezone identifier (e.g., America/New_York)
date_format string ISO 8601 Date display format in the portal UI
session_timeout_minutes integer 60 Idle session expiry in minutes
require_mfa boolean false Enforce MFA for all org members
allowed_ip_ranges list[string] [] CIDR blocks or plain IPs. Empty list = all IPs allowed (opt-in model)
dlp_fail_mode string closed open (allow on DLP error) or closed (block on DLP error)
audit_retention_days integer 90 Days to retain audit log entries; valid values: 30, 60, 90, 180, 365
custom_headers object {} Key-value pairs injected into outbound model requests
data_residency_region string us Read-only; current region where org data is stored

Update settings example:

Terminal window
curl -s -X PUT "https://platform.example.com/v1/orgs/${ORG_ID}/settings" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"session_timeout_minutes": 30,
"require_mfa": true,
"dlp_fail_mode": "closed",
"audit_retention_days": 180,
"timezone": "America/Chicago"
}' | jq '.'

MFA policy is managed separately from general settings via the Platform admin API:

GET /api/v1/admin/org/mfa-policy
PUT /api/v1/admin/org/mfa-policy
enforcement_level Behavior
off MFA is optional for all users (default)
optional Users are prompted to enable MFA but can skip
required Users must complete MFA setup before accessing the org
Terminal window
curl -s -X PUT "https://platform.example.com/api/v1/admin/org/mfa-policy" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"enforcement_level": "required"}' | jq '.'

IP restrictions are managed through the Platform IP allowlist API:

GET /v1/org/ip-allowlist
POST /v1/org/ip-allowlist
PUT /v1/org/ip-allowlist/{rule_id}
DELETE /v1/org/ip-allowlist/{rule_id}
POST /v1/org/ip-allowlist/bulk
POST /v1/org/ip-allowlist/test

Each rule has a rule_type of cidr, range, or single. The API rejects 0.0.0.0/0 and ::/0 to prevent accidentally opening access to the entire internet.

Add an IP allowlist rule:

Terminal window
curl -s -X POST "https://platform.example.com/v1/org/ip-allowlist" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"rule_type": "cidr",
"value": "203.0.113.0/24",
"description": "Corporate VPN egress range"
}' | jq '.'

Test whether a given IP address would be allowed under the current allowlist:

Terminal window
curl -s -X POST "https://platform.example.com/v1/org/ip-allowlist/test" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"ip_address": "203.0.113.45"}' | jq '.'

Bulk import rules from a list:

Terminal window
curl -s -X POST "https://platform.example.com/v1/org/ip-allowlist/bulk" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"rules": [
{"rule_type": "cidr", "value": "10.0.0.0/8", "description": "Internal network"},
{"rule_type": "single", "value": "198.51.100.22", "description": "Office static IP"}
]
}' | jq '.'

The billing portal surfaces plan details, invoice history, usage against plan limits, and payment method information for each organization. Navigate to Admin → Billing in the portal sidebar.

GET /v1/orgs/{org_id}/billing/summary
Terminal window
curl -s "https://platform.example.com/v1/orgs/${ORG_ID}/billing/summary" \
-H "Authorization: Bearer ${TOKEN}" | jq '.'

BillingSummaryResponse fields:

Field Type Description
plan_name string Human-readable plan label (e.g., “Team”)
plan_tier string Plan identifier (e.g., team_saas)
billing_period_start string ISO 8601 start of current billing period
billing_period_end string ISO 8601 end of current billing period
seats_used integer Active seat count
seats_included integer Seats included in plan
api_calls_this_period integer Requests made in the current billing period
api_calls_included integer Requests included in plan
overage_rate_cents integer Per-request overage charge in cents (currently 0)
next_invoice_date string ISO 8601 date of next invoice
payment_method_last4 string | null Last four digits of payment card on file
payment_method_brand string | null Card brand (e.g., visa)
status string Billing account status (e.g., active)
Plan Monthly price
devfree_saas Free
devpro_saas $49
team_saas $199
enterprise_saas $499
enterprise_outpost $999
GET /v1/orgs/{org_id}/plan

Returns a PlanInfoResponse with the full feature list for the active plan:

Terminal window
curl -s "https://platform.example.com/v1/orgs/${ORG_ID}/plan" \
-H "Authorization: Bearer ${TOKEN}" | jq '.'
Field Type Description
plan_name string Human-readable plan name
plan_tier string Plan tier identifier
billing_cycle string Always monthly
renewal_date string ISO 8601 date of next renewal
features list[string] Feature entitlements for this plan (models, seats, DLP capabilities, SSO, outposts, support level, compliance)
GET /v1/orgs/{org_id}/billing/portal-invoices
Terminal window
curl -s "https://platform.example.com/v1/orgs/${ORG_ID}/billing/portal-invoices" \
-H "Authorization: Bearer ${TOKEN}" | jq '.invoices'

Each InvoiceItem in the response:

Field Type Description
id string Invoice ID in format inv_{org_id[:8]}_{YYYYMM}
date string ISO 8601 invoice date
amount_cents integer Invoice total in cents
status string Payment status (e.g., paid)
pdf_url string | null Download URL for the PDF invoice (null pending Stripe integration)
description string Invoice description

Retrieve usage aggregates for the current billing period (proxied from Platform):

GET /v1/orgs/{org_id}/billing/usage
Terminal window
curl -s "https://platform.example.com/v1/orgs/${ORG_ID}/billing/usage" \
-H "Authorization: Bearer ${TOKEN}" | jq '.'

BillingUsageResponse fields:

Field Type Description
total_requests integer Total API requests in the period
total_tokens integer Total tokens consumed (input + output)
total_blocked integer Requests blocked by DLP or policy
total_redacted integer Requests where content was redacted
cost_estimate_usd number Estimated cost at $0.000003 per token
requests_by_provider object Request counts keyed by provider name
tokens_by_model object Token counts keyed by model ID
data_unavailable boolean true if Platform returned an error; all counts default to zero

  • Cloud portal new features — changelog of recently shipped portal capabilities
  • Usage Dashboard — detailed guide to the billing-period usage dashboard, including sparklines, model breakdown, and alert history
  • Theme Configuration — organization-level brand themes, CSS custom properties, and the 22 built-in themes
  • Portal Search API — REST API reference for the global search endpoint
  • Billing and plans — plan comparison, upgrade and downgrade paths, and invoice management
  • MFA and authentication — configuring multi-factor authentication requirements and supported MFA methods
  • IP allowlist — step-by-step guide to configuring IP access restrictions and testing rules
  • Audit log — interpreting audit events for org lifecycle changes, suspension, and deletion