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
Section titled “Global search”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.
Opening search
Section titled “Opening search”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.
Query behavior
Section titled “Query behavior”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.
Searchable entity types
Section titled “Searchable entity types”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
Keyboard navigation
Section titled “Keyboard navigation”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 |
Recent searches
Section titled “Recent searches”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.
Bookmarkable search URLs
Section titled “Bookmarkable search URLs”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,groupsSearch API
Section titled “Search API”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", "matched_field": "display_name", "url": "/admin/users/usr_01JXXXXXXXXXX" } ] }, { "type": "api_keys", "count": 1, "items": [ { "id": "key_01JXXXXXXXXXX", "title": "Alice's automation key", "matched_field": "name", "url": "/admin/api-keys/key_01JXXXXXXXXXX" } ] } ]}Analytics dashboard
Section titled “Analytics dashboard”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.
Overview cards
Section titled “Overview cards”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).
Chart tabs
Section titled “Chart tabs”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
A stacked bar chart showing input tokens vs output tokens by model, aggregated per time bucket.
Each bar is split into two segments:
- Input tokens (bottom) — tokens sent in the prompt
- Output tokens (top) — tokens generated in the response
Models are color-coded. A legend below the chart identifies each model. Toggle individual models on and off by clicking their legend entry.
CSV export fields: date, model_id, model_name, input_tokens, output_tokens, total_tokens
A horizontal bar chart ranking models by total request count for the selected period.
Each bar shows the model name on the Y axis and request count on the X axis. The chart is sorted descending by request count. A percentage-of-total label appears at the end of each bar.
This view is useful for understanding which models are driving the most load and cost.
CSV export fields: model_id, model_name, provider, request_count, pct_of_total, input_tokens, output_tokens, estimated_cost_usd
A bar chart showing the top 10 users by request count for the selected period.
Each bar is labeled with the user’s display name and email. The Y axis shows request count. Clicking a bar navigates to that user’s profile in the admin panel.
CSV export fields: user_id, display_name, email, request_count, input_tokens, output_tokens, estimated_cost_usd
A donut chart showing the breakdown of DLP actions taken across all requests in the selected period.
| Segment | Color | Meaning |
|---|---|---|
| Allowed | Green | Request passed DLP without modification |
| Redacted | Amber | One or more DLP rules matched; content was redacted before forwarding |
| Blocked | Red | Request was blocked by DLP before reaching the model |
The center of the donut shows the total number of DLP evaluations. Hover over any segment for exact count and percentage.
CSV export fields: action, count, pct_of_total, period_start, period_end
Period selector
Section titled “Period selector”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.
Auto-refresh
Section titled “Auto-refresh”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.
CSV export
Section titled “CSV export”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.
Loading and error states
Section titled “Loading and error states”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.
Data source
Section titled “Data source”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:
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.
Dark mode
Section titled “Dark mode”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.
Theme modes
Section titled “Theme modes”| 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 |
Switching themes
Section titled “Switching themes”- Click the theme icon (sun/moon) in the top-right header bar.
- A dropdown menu opens with three options: Light, Dark, System.
- 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.
Persistence and sync
Section titled “Persistence and sync”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.
Implementation details (admin context)
Section titled “Implementation details (admin context)”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.
Custom branding and theme interaction
Section titled “Custom branding and theme interaction”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.
Accessibility features
Section titled “Accessibility features”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.
Keyboard navigation
Section titled “Keyboard navigation”All interactive elements in the portal are reachable and operable via keyboard alone.
Tab order and focus
Section titled “Tab order and focus”- 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.
Focus indicators
Section titled “Focus indicators”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.
Skip-to-content link
Section titled “Skip-to-content link”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.
Modal focus trap
Section titled “Modal focus trap”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.
Dropdown and menu navigation
Section titled “Dropdown and menu navigation”| 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 |
Data table navigation
Section titled “Data table navigation”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 |
Global keyboard shortcuts
Section titled “Global keyboard shortcuts”| 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 |
Screen reader support
Section titled “Screen reader support”ARIA landmarks
Section titled “ARIA landmarks”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).
Dynamic content announcements
Section titled “Dynamic content announcements”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 |
Forms and inputs
Section titled “Forms and inputs”- Every
<input>,<select>, and<textarea>has an associated<label>element connected viaid/forattributes. - Required fields are marked with
aria-required="true"in addition to any visual asterisk. - Validation error messages are linked to their input via
aria-describedbyso screen readers announce the error when the field is focused. - Placeholder text is not used as a substitute for a label.
Images and icons
Section titled “Images and icons”- All informational images have descriptive
alttext. - Decorative images have
alt=""so screen readers skip them. - Icon-only buttons (such as the search icon and theme toggle) have
aria-labelattributes that describe their action. - Status indicator icons (for example, active/inactive badges) include visually hidden text for their state.
Tables
Section titled “Tables”- Column headers use
<th scope="col">. - Row headers (where present) use
<th scope="row">. - Complex tables with multi-level headers use
aria-labelledbyto associate header cells with data cells.
Analytics charts (alternative views)
Section titled “Analytics charts (alternative views)”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.
Color contrast
Section titled “Color contrast”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.
Motion and animation
Section titled “Motion and animation”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.
Focus management on navigation
Section titled “Focus management on navigation”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.
Administrative workflows
Section titled “Administrative workflows”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.
Portal settings page
Section titled “Portal settings page”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 |
Session management
Section titled “Session management”You can view and manage all active sessions for your organization from Admin → Settings → Security → Active Sessions.
Viewing active sessions
Section titled “Viewing 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.
Force logout
Section titled “Force logout”To terminate a specific session:
- Find the session in the Active Sessions list.
- Click the Revoke button at the end of the session row.
- 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.
Announcement banner
Section titled “Announcement banner”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.
Setting an announcement
Section titled “Setting an announcement”- Navigate to Admin → Settings → Notifications.
- In the Announcement Banner section, enter your message in the text field. Markdown is not supported — plain text only.
- Select a severity level from the dropdown: Info (blue), Warning (amber), or Critical (red).
- Optionally set an expiry date and time. The banner will automatically clear at the specified time.
- 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.
Clearing an announcement
Section titled “Clearing an announcement”To remove the banner before its expiry:
- Navigate to Admin → Settings → Notifications.
- In the Announcement Banner section, click Clear Banner.
- The banner is removed immediately for all users (including users who have not yet seen it).
Maintenance mode
Section titled “Maintenance mode”Maintenance mode disables the portal and API for all non-admin users, displaying a customizable maintenance page.
Enabling maintenance mode
Section titled “Enabling maintenance mode”- Navigate to Admin → Settings → Maintenance.
- Enter a message in the Maintenance Message field. This message is displayed on the maintenance page and in the 503 response body.
- Optionally set an Expected End Time. This is shown on the maintenance page as an estimated resumption time.
- Click Enable Maintenance Mode.
- 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.
Disabling maintenance mode
Section titled “Disabling maintenance mode”- Navigate to Admin → Settings → Maintenance.
- Click Disable Maintenance Mode.
- The portal becomes available to all users immediately.
Behavior during maintenance mode
Section titled “Behavior during maintenance mode”| 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 |
Portal version
Section titled “Portal version”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 |
Tenant lifecycle management
Section titled “Tenant lifecycle management”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.
Organization status model
Section titled “Organization status model”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 |
Plan tiers
Section titled “Plan tiers”| 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 |
Creating an organization
Section titled “Creating an organization”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.
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 '.'Suspending an organization
Section titled “Suspending an organization”PUT /v1/admin/orgs/{org_id}/suspendSets 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.
curl -s -X PUT "https://platform.example.com/v1/admin/orgs/${ORG_ID}/suspend" \ -H "X-API-Key: ${ADMIN_KEY}" | jq '.'Activating a suspended organization
Section titled “Activating a suspended organization”PUT /v1/admin/orgs/{org_id}/activateSets status to active. API access is restored immediately. Returns 409 if the organization is not currently suspended. Writes an org_activated audit event.
curl -s -X PUT "https://platform.example.com/v1/admin/orgs/${ORG_ID}/activate" \ -H "X-API-Key: ${ADMIN_KEY}" | jq '.'Deleting an organization (admin)
Section titled “Deleting an organization (admin)”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.
curl -s -X DELETE "https://platform.example.com/v1/admin/orgs/${ORG_ID}" \ -H "X-API-Key: ${ADMIN_KEY}" | jq '.'Self-service deletion (org owner)
Section titled “Self-service deletion (org owner)”An organization owner can request deletion without admin intervention:
POST /v1/orgs/{org_id}/request-deletionAuthenticates 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.
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 '.'Lifecycle response schema
Section titled “Lifecycle response schema”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 |
Usage quotas
Section titled “Usage quotas”Arbitex enforces quotas at two distinct layers: plan-level enforcement in Platform and display limits shown in the billing dashboard.
Plan-level enforcement limits
Section titled “Plan-level enforcement limits”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) |
Billing dashboard display limits
Section titled “Billing dashboard display limits”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 alert thresholds
Section titled “Usage alert thresholds”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 |
Quota API
Section titled “Quota API”Check the current quota status for an organization:
GET /v1/orgs/{org_id}/usage/quotacurl -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% |
Per-user and per-group quotas
Section titled “Per-user and per-group quotas”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:
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
Section titled “Organization settings”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}/settingsPUT /v1/orgs/{org_id}/settingsBoth endpoints require an org-scoped JWT. PUT requires the org_admin role.
Settings fields
Section titled “Settings fields”| 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:
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 enforcement policy
Section titled “MFA enforcement policy”MFA policy is managed separately from general settings via the Platform admin API:
GET /api/v1/admin/org/mfa-policyPUT /api/v1/admin/org/mfa-policyenforcement_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 |
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 allowlist
Section titled “IP allowlist”IP restrictions are managed through the Platform IP allowlist API:
GET /v1/org/ip-allowlistPOST /v1/org/ip-allowlistPUT /v1/org/ip-allowlist/{rule_id}DELETE /v1/org/ip-allowlist/{rule_id}POST /v1/org/ip-allowlist/bulkPOST /v1/org/ip-allowlist/testEach 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:
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:
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:
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 '.'Billing portal
Section titled “Billing portal”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.
Billing summary
Section titled “Billing summary”GET /v1/orgs/{org_id}/billing/summarycurl -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 pricing
Section titled “Plan pricing”| Plan | Monthly price |
|---|---|
devfree_saas |
Free |
devpro_saas |
$49 |
team_saas |
$199 |
enterprise_saas |
$499 |
enterprise_outpost |
$999 |
Plan information
Section titled “Plan information”GET /v1/orgs/{org_id}/planReturns a PlanInfoResponse with the full feature list for the active plan:
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) |
Invoice history
Section titled “Invoice history”GET /v1/orgs/{org_id}/billing/portal-invoicescurl -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 |
Usage data
Section titled “Usage data”Retrieve usage aggregates for the current billing period (proxied from Platform):
GET /v1/orgs/{org_id}/billing/usagecurl -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 |
The full usage dashboard includes plan limits, overage alerts, and the current warning level:
GET /v1/orgs/{org_id}/billing/usage/dashboardcurl -s "https://platform.example.com/v1/orgs/${ORG_ID}/billing/usage/dashboard" \ -H "Authorization: Bearer ${TOKEN}" | jq '.'Retrieve a daily breakdown for a specific calendar month:
GET /v1/orgs/{org_id}/billing/usage/history?month=YYYY-MMcurl -s \ "https://platform.example.com/v1/orgs/${ORG_ID}/billing/usage/history?month=2026-03" \ -H "Authorization: Bearer ${TOKEN}" | jq '.'Export the monthly breakdown as CSV:
GET /v1/orgs/{org_id}/billing/usage/history/export?month=YYYY-MMcurl -s \ "https://platform.example.com/v1/orgs/${ORG_ID}/billing/usage/history/export?month=2026-03" \ -H "Authorization: Bearer ${TOKEN}" \ -o "usage-2026-03.csv"Related documentation
Section titled “Related documentation”- 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