Skip to main content
Sevrel
Documentation menu

API Reference

The Sevrel REST API powers all platform functionality. All endpoints are served from https://api.sevrel.com and require authentication unless noted otherwise.

Base URL

https://api.sevrel.com

Request and response bodies are JSON except where noted — the chat stream is Server-Sent Events and the file routes return binary streams. Authenticated endpoints accept any one of three credential transports: a session cookie, an Authorization: Bearer header carrying a first-party JWT or a Microsoft Entra ID access token, or an X-API-Key header. Sessions can be issued by the Entra ID flow below, by email and password, or by the Google flow. API keys are the non-browser path: only member-scoped keys reach the endpoints on this page (skill-scoped keys are confined to /api/chat/skill/* and get 403 elsewhere), and an MFA-enabled account cannot authenticate with an API key at all. Unsafe methods sent from a browser with a non-allowlisted Origin header and no bearer token are rejected with 403.

This reference covers the core platform surfaces. Other mounted endpoint groups — work orders, email, calendar, notifications, the rest of the uploads group, API keys, MFA, billing, integrations, and the /v1 skill API — are not documented here; contact us for their contracts.

Authentication

Sevrel uses cookie-based sessions. Authenticate via Microsoft Entra ID, then use the returned session cookie for all subsequent requests. Cookies are HttpOnly, Secure, SameSite=Lax.

POST/api/auth/azureBearer (Azure token)

Exchange a Microsoft Entra ID access token for a Sevrel session cookie. The backend validates the Azure token against Microsoft JWKS (signature, audience, issuer, expiry). On success, sets a signed HttpOnly session cookie.

Request Body

// Headers
Authorization: Bearer <azure_access_token>

Response

// Set-Cookie: the session cookie is set on the success path
// 200 OK
{
  "user": {
    "user_id": "uuid",
    "email": "[email protected]",
    "display_name": "Jane Doe",
    "role": "member",
    "organization_id": "uuid",
    "organization_name": "Acme CRE"
  }
}

// If the account has MFA enabled, no session cookie is set and the body is instead:
// { "requires_mfa": true, "mfa_session": "..." } — complete the challenge under /api/auth/mfa
// 503 when Azure auth is not configured on the backend.
GET/api/auth/meCookie

Returns the currently authenticated user's profile, including role and organization membership. Accepts a session cookie or a Bearer token.

Response

{
  "user_id": "uuid",
  "email": "[email protected]",
  "display_name": "Jane Doe",
  "role": "member",
  "organization_id": "uuid",
  "organization_name": "Acme CRE"
}
POST/api/auth/logoutCookie

Invalidates the session cookie and clears the server-side session.

GET/api/auth/sessionsCookie

List all active sessions for the current user, including IP address, user agent, and timestamps. Useful for security auditing.

Response

{
  "sessions": [
    {
      "session_id": "uuid",
      "ip_address": "203.0.113.1",
      "user_agent": "Mozilla/5.0...",
      "created_at": "2026-08-11T08:00:00Z",
      "expires_at": "2026-08-18T08:00:00Z"
    }
  ]
}
// Newest first, capped at 50. Pass "session_id" to DELETE /api/auth/sessions/{id}.
DELETE/api/auth/sessions/{id}Cookie

Revoke a specific session by ID. The user is signed out of that device or browser.

POST/api/auth/logout-allCookie

Revoke all active sessions for the current user across all devices AND revoke every API key that user owns. Also clears the caller's own session cookie and sends Clear-Site-Data. Rate limited to 10 calls per minute.

Response

// 200 OK
{ "ok": true, "revokedSessions": 3, "revokedApiKeys": 2 }

// 503 when session revocation succeeded but API-key revocation could not be confirmed:
{
  "ok": false,
  "partial": true,
  "revokedSessions": 3,
  "revokedApiKeys": 0,
  "error": "Sessions were revoked, but API-key revocation could not be confirmed. Sign in again and retry."
}

Chat

The core intelligence layer. Send questions about your documents and receive AI-generated answers with source citations. Supports both streaming (SSE) and synchronous modes.

POST/api/chat/streamCookie

Send a message and receive a streaming response via Server-Sent Events. The AI searches your connected documents, retrieves relevant sections, and streams an answer with inline citations. The request body rejects unknown fields with a 422; the example below shows the common ones rather than every accepted field.

Request Body

{
  "messages": [
    { "role": "user", "content": "What is the CAM for Cedar Point 2025?" }
  ],
  "conversation_id": "uuid (optional, must be a UUID)",

  // Attachments — three separate optional fields, not one "attachments" array:
  "attachment_contents": [ { "name": "notes.txt", "text": "..." } ],           // max 20
  "attachment_images":   [ { "filename": "photo.png",
                             "mime_type": "image/png",                        // jpeg|png|gif|webp
                             "content_base64": "..." } ],                     // max 10
  "attachment_file_ids": [ { "file_id": "doc_id from POST /api/uploads",
                             "filename": "budget.xlsx",
                             "content_type": "application/vnd.ms-excel" } ]    // max 20
}
// attachment_file_ids is only honoured when the spreadsheet-editing feature is
// enabled for your organization.

Response

// SSE stream — frames are discriminated by which key is present, not by a "type" field
data: {"status": "Searching documents..."}
data: {"token": "The CAM charges for..."}
data: {"evidence": [ { "document_name": "Lease_TenantXYZ_2023.pdf", "snippet": "..." } ]}
data: {"thinking_progress": {"iteration": 2, "max_iterations": 8, "last_tool": "doc_search"}}
data: [DONE]
// A {"conversationTitle": "..."} frame may arrive AFTER [DONE] — keep reading the stream.
POST/api/chatCookie

Synchronous (non-streaming) chat endpoint. Returns the complete response with evidence in a single JSON payload.

Request Body

{
  "messages": [
    { "role": "user", "content": "List all lease expirations in 2026." }
  ],
  "conversation_id": "uuid (optional)"
}

Response

{
  "conversationId": "uuid",
  "assistantMessage": {
    "id": "uuid",
    "role": "assistant",
    "content": "Based on your documents, the following...",
    "createdAt": 1754870400000,
    "evidence": [ ... ]
  },
  "sources": [
    {
      "document_name": "Lease_TenantXYZ_2023.pdf",
      "snippet": "Lease term expires December 31, 2026...",
      "path": "/Shared/All/Property1/Leases/Lease_TenantXYZ_2023.pdf"
    }
  ],
  "answer": "Based on your documents, the following...",
  "evidence": [ ... ]
}
// "answer" is omitted on an idempotent replay (same client_message_id) —
// read "assistantMessage.content" instead.
POST/api/uploadsCookie

Upload a file and extract its text so it can be referenced from chat. Multipart form data with the field file; optional query parameters folder_path, folder_id, and work_order_id (the last requires the workorders:write permission). Rate limited on the chat bucket at 60 requests per minute per user. Returns 201.

Request Body

// Content-Type: multipart/form-data
// Field: file (binary)
// Optional query: ?folder_path=/&folder_id=<uuid>&work_order_id=<uuid>

Response

// 201 Created
{
  "doc_id": "uuid",
  "filename": "report.pdf",
  "original_filename": "report.pdf",
  "file_size": 245760,
  "file_type": "pdf",
  "folder_path": "/",
  "has_text": true,
  "text_extracted": true,
  "created_at": "...",
  "updated_at": "..."
}
// When text extraction fails an extra "warning" string is added.
// Pass the returned doc_id back to chat as attachment_file_ids[].file_id.

Conversations

Manage chat conversation history. Conversations persist messages and associated evidence for later review and sharing. There is no explicit create endpoint — a conversation is created implicitly on the first chat turn: send POST /api/chat or POST /api/chat/stream with a client-generated conversation_id (UUID), or omit it and the server generates one. The synchronous POST /api/chat returns that id as conversationId; the SSE stream does not emit it, so send your own conversation_id when you need to address the conversation afterwards. Use PATCH /api/conversations/{id} to set a title.

GET/api/conversationsCookie

List conversations for the authenticated user, newest first. Evidence is stripped for performance — use the single conversation endpoint for full details. Supplying a limit query parameter switches to cursor pagination and changes the response shape.

Response

// Default (no limit)
{
  "conversations": [
    {
      "id": "uuid",
      "title": "CAM Analysis Q4 2025",
      "messages": [ /* evidence-stripped stubs */ ],
      "createdAt": 1754870400000,
      "updatedAt": 1754874000000,
      "folderId": null,
      "tags": []
    }
  ]
}

// With ?limit=50&cursor=...
{ "items": [ /* same item shape */ ], "next_cursor": "...", "has_more": true }
GET/api/conversations/{id}Cookie

Retrieve a single conversation with full message history and evidence citations.

Response

{
  "id": "uuid",
  "title": "CAM Analysis Q4 2025",
  "messages": [
    {
      "id": "uuid",
      "role": "assistant",
      "content": "...",
      "timestamp": 1754870400000,
      "evidence": [ ... ]
    }
  ],
  "createdAt": 1754870400000,
  "updatedAt": 1754874000000,
  "folderId": null,
  "tags": [],
  "compactSummary": null,
  "isAssistant": false,
  "ownerUserId": "uuid"
}
// Citations are attached per-message; there is no top-level "evidence" array.
DELETE/api/conversations/{id}Cookie

Soft-delete a conversation. Returns 200 with an ok:true body, not 204 No Content. The row stays recoverable for 7 days — list soft-deleted conversations with GET /api/conversations/deleted, undo with POST /api/conversations/{id}/restore, or remove it immediately with DELETE /api/conversations/{id}/permanent.

Documents & Storage Providers

Browse and search the document provider connected to your organization. Provider reads can happen on demand; semantic retrieval also uses derived chunks and embeddings, so some newly added or changed files may require a sync or re-index.

GET/api/documents/browse?path={folder_path}Cookie

Browse the active provider's folder contents. The path parameter is optional and defaults to the provider root. Returns 409 when the organization has no storage provider connected, and 502 on a provider failure. On Google Drive and SharePoint the path field carries the provider's own file ID rather than a human-readable path.

Response

{
  "path": "/Shared/All/Property1/Leases",
  "folders": [
    { "name": "Leases", "path": "/Shared/All/Property1/Leases", "is_folder": true }
  ],
  "files": [
    {
      "name": "Lease_TenantA_2024.pdf",
      "path": "/Shared/All/Property1/Leases/Lease_TenantA_2024.pdf",
      "is_folder": false,
      "size": 524288,
      "last_modified": "2026-01-15T08:00:00Z",
      "type": "pdf",
      "supported": true
    }
  ]
}
// Fields vary by provider: a text-listing fallback emits only name/path/is_folder.
GET/api/documents/search?query={search_terms}Cookie

Search the active document provider. The query parameter is named query (not q), is required, and must be 1–500 characters. Returns 409 when the organization has no storage provider connected, and 502 on a provider failure.

Response

{
  "results": [
    {
      "name": "OpEx_Budget_2025.xlsx",
      "path": "/Shared/All/Property1/Financials/OpEx_Budget_2025.xlsx",
      "type": "xlsx",
      "supported": true,
      "snippet": "Total CAM charges for 2025: $1,245,000..."   // Egnyte only
    }
  ]
}
// Fields vary by connected provider: the Egnyte path returns name/path/snippet/type/supported
// (plus size and last_modified when available); other providers return
// name/path/is_folder/size/modified with no snippet. No relevance score is returned.
POST/api/documents/provider/uploadCookie (requires the documents:ingest permission)

Save an uploaded file into the organization's connected storage provider. Returns 409 when no storage provider is connected, 413 above the 50 MB cap, and 400 on an empty file. For a local upload that is not written back to the provider, use POST /api/uploads instead.

Request Body

// Content-Type: multipart/form-data
// Field: file (binary)
// Field: clientRequestId (UUID, required — idempotency key)
// Field: parent (optional destination folder)

Agents

Sevrel's three active proactive agents run as Celery tasks and persist structured findings that surface through notifications and briefings. The former in-process agent-pipeline API is retired; its run and artifact endpoints remain read-only so historical records are still available.

GET/api/assistant/v2/agents/statusCookie

List the active proactive agents — morning briefing, email triage, and lease expiry — with latest-run health. Requires the agents:list permission. Scheduling is organization-wide; there is no per-user toggle.

Response

{
  "agents": [
    {
      "slug": "lease-expiry",
      "name": "Lease Expiration Watchdog",
      "description": "...",
      "schedule": "Nightly at 02:07 UTC",
      "available": true,
      "last_run_at": "2026-08-11T02:07:00Z",
      "last_run_status": "completed",
      "last_run_health": "healthy",
      "last_run_findings": 2,
      "last_run_tokens": 18450
    }
  ]
}
// The last_run_* fields are null for an agent this organization has never run.
POST/api/assistant/v2/agents/{slug}/run-nowCookie

Dispatch one active proactive agent for the authenticated user's organization through Celery. Requires the agents:run permission. Returns 410 for a retired slug, 404 for an unknown slug, 409 for a catalog entry that is not yet available, and 403 when the caller has no organization.

Response

{ "status": "dispatched", "task_id": "celery-task-uuid" }

// Fails soft with HTTP 200 when the Celery broker is unreachable —
// always check "status", not just the status code:
{ "status": "dispatch_failed", "task_id": null }
GET/api/agentsCookie

Retired compatibility endpoint. It returns an empty agent list and points clients to /api/assistant/v2/agents/status; it never advertises or launches legacy pipelines.

Response

{
  "agents": [],
  "runtime": "retired",
  "replacement": "/api/assistant/v2/agents/status"
}
GET/api/agents/runsCookie

Read the authenticated user's historical legacy AgentRun rows and artifact metadata. Creating, stopping, approving, or messaging legacy runs is no longer supported.

Portfolio

Manage properties, tenants, and leases. The portfolio layer provides structured data for supported portfolio and dashboard analysis. The standalone critical-date feature is retired and is not implied by these endpoints.

GET/api/portfolio/overviewCookie

Every property in the organization with its active tenants nested underneath. An optional property_id query parameter narrows the result to one property. Built from properties rather than leases, so properties and tenants without rent data still appear. Portfolio-level roll-ups live under /api/dashboard/summary and /api/dashboard/health-score.

Response

{
  "properties": [
    {
      "propertyId": "uuid",
      "name": "Maple Crossing Plaza",
      "address": "...",
      "squareFootage": 250000,
      "tenantCount": 42,
      "leasedSf": 240000,
      "totalAnnualRent": 10200000,
      "tenants": [
        /* tenantId, name, suite, squareFootage, status, leaseStart, leaseEnd, ... */
      ]
    }
  ]
}
GET/api/portfolio/propertiesCookie

List the organization's properties. Optional limit query parameter (1–500, default 100). Returns a bare JSON array — no envelope. Tenants and occupancy are not included here; use /api/portfolio/overview for properties with nested active tenants.

Response

[
  {
    "property_id": "uuid",
    "name": "Maple Crossing Plaza",
    "address": "...",
    "city": "...",
    "state": "...",
    "zip_code": "...",
    "asset_class": "retail",
    "square_footage": 250000,
    "year_built": 1998,
    "ownership_entity": "...",
    "purchase_price": 0,
    "purchase_date": null,
    "current_valuation": 0,
    "notes": null,
    "manager_user_id": null,
    "manager_name": null
  }
]

Critical Dates (Historical — Not Mounted)

Reference only — requests return 404

The standalone critical-date router is intentionally not mounted. The contracts below are retained only to identify the retired API and must not be used as current integration documentation. When enabled, the separate lease-expiry agent checks recorded lease end dates inside 30 days; it does not derive notice clauses.

These historical routes represented expirations, renewal deadlines, rent escalations, and option exercise dates. They are unavailable in the current application.

GET/api/critical-datesNot mounted

Historical contract only; this route is not mounted and returns 404. The listing below is partial — the retired router also carried GET /api/critical-dates/summary, GET /api/critical-dates/briefing, GET /api/critical-dates/{id}, and POST /api/critical-dates/sync.

Response

[
  {
    "id": "uuid",
    "critical_date_id": "uuid",
    "lease_id": "uuid",
    "tenant_id": "uuid",
    "property_id": "uuid",
    "tenant_name": "Luxury Brand Co",
    "property_name": "Cedar Point Center",
    "date_type": "expiration",
    "date_value": "2026-06-30T00:00:00+00:00",
    "status": "upcoming",
    "notes": null,
    "urgency": { "days_remaining": 28, "tier": "critical", "severity": "critical",
                 "label": "28 days remaining" }
  }
]
// Bare array, not an envelope. Optional query params were property_id, tenant_id,
// date_type, status, severity, within_days, and limit (1-500).
PATCH/api/critical-dates/{id}Not mounted

Historical contract only; this route is not mounted and returns 404.

Request Body

{
  "status": "acknowledged",
  "notes": "Renewal negotiation in progress with tenant."
}
POST/api/critical-datesNot mounted

Historical contract only; this route is not mounted and returns 404.

Request Body

{
  "date_type": "renewal_option",
  "date_value": "2026-09-15",
  "property_id": "uuid",
  "tenant_id": "uuid",
  "notes": "Tenant has 5-year renewal option"
}
GET/api/critical-dates/export?format=csvNot mounted

Historical contract only; this route is not mounted and returns 404.

DELETE/api/critical-dates/{id}Not mounted

Historical contract only; this route is not mounted and returns 404.

Query Templates (Historical — Not Mounted)

Reference only — requests return 404

No query-template router or custom-template panel is currently mounted. Slash-command catalog entries are ordinary one-turn prompt shortcuts, not these historical saved template resources or dedicated executable workflows.

These historical contracts represented pre-built and user-saved templates. They are unavailable in the current application.

GET/api/query-templates?category={category}Not mounted

Historical contract only; this route is not mounted and returns 404. The listing below is partial — the retired router also carried POST /api/query-templates/{id}/use and DELETE /api/query-templates/{id}.

Response

[
  {
    "id": "uuid",
    "title": "Compare CAM Charges",
    "template_text": "Compare CAM charges for {property} across {year1} and {year2}.",
    "category": "financial",
    "is_system": true,
    "usage_count": 45
  }
]
// Bare array, not an envelope. Optional query params were category, include_system,
// and limit (1-500).
POST/api/query-templatesNot mounted

Historical contract only; this route is not mounted and returns 404.

Request Body

{
  "title": "Tenant Renewal Status",
  "template_text": "What is the renewal status for {tenant} at {property}?",
  "category": "lease"
}
GET/api/query-templates/parameters/optionsNot mounted

Historical contract only; this route is not mounted and returns 404.

Dashboard Analysis API

Mounted API; legacy Dashboard UI retired

These authenticated routes are mounted, but their output depends on available organization-scoped records and may be empty or partial. The legacy Dashboard screen is retired. Standalone critical dates are not mounted, automated anomaly agents are dormant, and corresponding fields or stored-alert views must not be read as continuous monitoring.

On-demand portfolio and operational analysis over the records currently available to the authenticated organization. Scores and flags are decision-support heuristics, not predictions or guarantees.

GET/api/dashboard/overviewCookie

On-demand overview of available health, portfolio-summary, and rent-escalation data. The retired critical-date urgency field is empty.

GET/api/dashboard/summaryCookie

Portfolio summary including property-level occupancy, revenue, and tenant metrics.

GET/api/dashboard/tenant-riskCookie

Heuristic tenant scores from available structured portfolio fields. This is on-demand decision support, not continuous external risk monitoring; retired critical-date inputs may be absent.

Response

{
  "generated_at": "2026-08-11T00:00:00Z",
  "tenants": [
    {
      "tenant_id": "uuid",
      "tenant_name": "Anchor Retail Inc",
      "property_name": "Cedar Point Center",
      "risk_score": 72,
      "risk_tier": "high",
      "factors": [
        { "factor": "expiry_imminent",    "points": 25, "detail": "Expires in 61 days" },
        { "factor": "high_concentration", "points": 25, "detail": "32.4% of portfolio rent" }
      ],
      "lease_end_date": "2026-10-11T00:00:00Z",
      "annual_rent": 1240000,
      "leased_sqft": 42000,
      "rent_concentration_pct": 32.4
    }
  ],
  "summary": { ... }
}
// Returns { generated_at, tenants: [], summary: {} } when the organization has
// no active leases.
GET/api/dashboard/lease-rolloverCookie

Rent expiring by quarter for rollover analysis. Shows quarterly exposure with the peak quarter highlighted.

GET/api/dashboard/concentrationCookie

Tenant, property, and industry concentration metrics with auto-generated alerts when thresholds are exceeded.

GET/api/dashboard/morning-briefingCookie

On-demand dashboard briefing aggregation from available records. This is distinct from the configurable proactive Morning Briefing agent, and retired critical-date inputs may be empty.

GET/api/dashboard/health-scoreCookie

Composite portfolio health score 0–100 with an A–F grade, from four 25-point sub-scores: occupancy, lease stability (WALT and 90-day expirations), revenue quality (rent per square foot and top-tenant concentration), and operational readiness (lease end-date completeness and indexed document count). The critical-date inputs to operational readiness are inert — that feature is retired — so those terms contribute a fixed value.

GET/api/dashboard/weekly-summaryCookie

Weekly report with per-property occupancy, revenue, upcoming expirations, and rent escalations.

GET/api/dashboard/stale-dataCookie

Data quality indicators. Flags stale documents, missing lease data, and properties without recent updates.

GET/api/dashboard/rent-escalationsCookie

Upcoming rent escalation calendar with monthly increase amounts.

GET/api/dashboard/activity-feedCookie

Recent stored activity available to the organization. Retired critical-date activity is not continuously generated.

GET/api/dashboard/property/{id}Cookie

Single-property deep dive: occupancy and financial metrics, the tenant roster with per-lease rent and escalation terms, WALT, and leases expiring in the next 90 days. The critical_dates array and its summary counters are retained for wire compatibility but are always empty — the critical-date feature is retired.

GET/api/dashboard/tenant/{name}Cookie

Tenant deep dive across all properties: lease terms, rent details, risk factors, and related documents.

GET/api/dashboard/compare/tenantsCookie

Side-by-side tenant comparison on rent, lease terms, risk scores, and space utilization.

GET/api/dashboard/compare/propertiesCookie

Side-by-side property comparison on occupancy, revenue, tenant mix, and data quality.

GET/api/dashboard/rent-roll/{id}Cookie

Rent roll for a specific property showing all tenants, suite numbers, lease terms, and monthly rent.

GET/api/dashboard/financial-anomaliesCookie

On-demand view over available stored anomaly-style alerts and portfolio records. Automated financial-anomaly agents are dormant; this is not continuous monitoring.

GET/api/dashboard/rent-reconciliationCookie

Legacy-named lease data-quality check for missing fields and unusual escalation values. It does not compare an independent rent roll or payment source, and the route is marked deprecated in the API schema.

Scheduled Reports

Save definitions for a fixed set of report types and run them on demand. Although each definition stores a cadence and next-run timestamp, no current background dispatcher automatically executes due reports or delivers recurring email. Do not rely on these routes as a notification or deadline-control system.

GET/api/reportsCookie

List all scheduled reports for the authenticated user.

POST/api/reportsCookie

Create a new scheduled report.

Request Body

{
  "report_type": "weekly_summary",
  "name": "Weekly portfolio summary",
  "cadence": "weekly",
  "recipients": ["[email protected]"],
  "parameters": { "property_ids": ["uuid"] }
}
PATCH/api/reports/{id}Cookie

Update a saved report definition's status, cadence, name, or recipients. Updating cadence does not create an automatic dispatcher.

POST/api/reports/{id}/runCookie

Execute a saved report definition on demand. This is the currently implemented execution path.

DELETE/api/reports/{id}Cookie

Delete a scheduled report. Returns 204 No Content.

Organizations (Admin)

Multi-tenant organization management — reading organizations, managing their members, and monitoring usage. Every route below requires the admin role except /api/organizations/me/usage, which any role may call for its own organization. Organizations are not created here: provisioning runs through the admin console, which also applies the plan template, guardrail policies, owner assignment, and audit logging.

GET/api/organizationsCookie (admin)

List organizations with user counts. A platform super-admin sees every organization (capped at 200, newest first); an organization admin sees only their own.

Response

{
  "organizations": [
    {
      "organization_id": "uuid",
      "name": "Acme CRE Partners",
      "slug": "acme-cre",
      "subscription_tier": "professional",
      "auth_provider": "entra_id",
      "is_active": true,
      "user_count": 8,
      "created_at": "2026-01-15T00:00:00Z",
      "updated_at": "2026-08-01T00:00:00Z"
    }
  ],
  "total": 5
}
GET/api/organizations/{org_id}/usersCookie (admin)

List users in an organization with their roles and active status. Paged with limit (1–500, default 100) and offset. Returns a bare JSON array.

POST/api/organizations/{org_id}/usersCookie (admin)

Assign a user to an organization.

Request Body

{ "user_id": "uuid" }
PATCH/api/organizations/{org_id}/users/{user_id}/roleCookie (admin)

Update a user's role within an organization.

Request Body

{ "role": "admin" }
DELETE/api/organizations/{org_id}/users/{user_id}Cookie (admin)

Remove a user from an organization.

GET/api/organizations/{org_id}/usage?days=30Cookie (admin)

Per-organization usage. The days parameter (1–365, default 30) bounds the chat-query window: total_queries and active_users cover that window, while total_users, total_conversations, and total_properties are all-time counts. Returns 403 unless the caller is an admin of this organization or a platform super-admin.

Response

{
  "organization_id": "uuid",
  "name": "Acme CRE Partners",
  "period_days": 30,
  "total_users": 8,
  "active_users": 5,
  "total_queries": 412,
  "total_conversations": 96,
  "total_properties": 12
}
GET/api/organizations/usage/summaryCookie (platform super-admin)

All-organizations usage overview for platform monitoring. Organization admins who are not platform super-admins receive 403 Platform admin only.

GET/api/organizations/me/usageCookie

The caller's organization tier plus current usage. Returns tier, queries, users, and properties blocks, each carrying used, limit (or the string unlimited), and remaining. Monthly query and seat allowances are enforced; the property figure is plan sizing agreed with the account team, not an enforced boundary. Available to every role.

Health

GET/api/healthNone (per-IP rate limited)

Public liveness endpoint. Returns only the overall status — the per-service breakdown is intentionally not exposed to anonymous clients and lives at /api/health/detail behind an admin role check.

Response

{ "status": "ok" }   // or "degraded"

Rate Limits & Errors

Rate Limits

  • Auth endpoints: Per-IP rate limiting to prevent brute-force attacks.
  • Chat endpoints: two independent limits apply. First, a per-user burst throttle of 60 chat requests per minute (read endpoints 300/min; admin writes 10/min). Second, a per-organization monthly query quota set by subscription tier — Free trial 100, Starter 500, Professional 2,000, Enterprise unlimited. The monthly quota counts chat_query audit events across every user in the organization, so a single user can exhaust the whole tenant's allowance.
  • When rate limited, the API returns 429 Too Many Requests with the body {"detail": "Too many requests"}. Per-minute limits reset on a rolling one-minute window and the monthly quota resets at the start of each calendar month. /api/* responses do not currently carry a Retry-After header, so clients should back off exponentially. Login throttling returns a distinct message (“Too many login attempts. Try again in a minute.”).

Error Responses

Most errors carry a detail string. Two shapes differ: request-validation failures return 422 with detail as an array of error objects, and an unhandled 500 adds an error discriminator.

{
  "detail": "Human-readable error message"
}

// 422 — request validation: detail is an ARRAY
{ "detail": [ { "type": "missing", "loc": ["query", "query"], "msg": "Field required" } ] }

// 500 — unhandled server error
{ "detail": "Internal server error", "error": "internal_error" }

// Status codes you should expect:
// 400 — Bad Request (invalid input)
// 401 — Unauthorized (missing or invalid credential)
// 402 — Payment Required (trial grace or paid plan fully expired)
// 403 — Forbidden (insufficient role, or a disallowed Origin on an unsafe method)
// 404 — Not Found
// 409 — Conflict (no storage provider connected; agent catalog entry not yet available)
// 410 — Gone (retired endpoint or retired agent slug)
// 413 — Payload Too Large (request body over the per-route cap)
// 422 — Unprocessable Entity (schema validation; unknown fields are rejected)
// 429 — Rate Limited (burst throttle or monthly quota)
// 500 — Internal Server Error
// 503 — Service Unavailable (dependency unconfigured, or access could not be verified)

Correlation IDs

Every request receives a unique correlation ID, returned in the X-Correlation-ID response header. Include this ID when contacting support about specific request failures.

Questions about the API?

Contact our team for integration support, custom endpoint needs, or enterprise API access.

Last updated: August 11, 2026