openapi: 3.0.0 info: title: PipesHub API description: | Unified API documentation for PipesHub services. PipesHub is an enterprise-grade platform providing: - User authentication and management - Document storage and version control - Knowledge base management - Enterprise search and conversational AI - Third-party integrations via connectors - System configuration management - Crawling job scheduling - Email services ## Authentication Most endpoints require JWT Bearer token authentication. Some internal endpoints use scoped tokens for service-to-service communication. **OAuth 2.0 Bearer tokens** from `POST /oauth2/token` use the same `Authorization: Bearer` header. For **`client_credentials`**, machine tokens may encode `userId === client_id` in the JWT; the **Node API gateway** resolves the application's **token identity** — the service account it is pointed at (`tokenIdentityUserId`), or its creator for an application registered before that was required and never bound, and sets the authenticated user accordingly. It reads the application record on every request, and a token minted for a different identity than the app's current one is refused with `401`. See the **OAuth Provider** tag for full behavior. ## Behaviour common to every route App-level middleware runs before any router, so the following applies to every operation below, before authentication, unless an exception is named. **Markup filter.** A query value, or any string in a JSON or form-encoded body, that looks like markup is refused with `400 HTTP_BAD_REQUEST`. The message names where the value was, for example `HTML tags, scripts, and XSS content are not allowed in the field "name".` or `... in the query parameter "search".` (a field name that is not a plain identifier path reads `the request`), and says what counted: text in angle brackets such as `` counts as an HTML tag, an encoded script tag counts, or script content such as `onclick=`, `javascript:` or `data:text/html` counts. A string over 100,000 characters is refused as too long to check instead: `The value of the field "name" is longer than 100,000 characters, which is too long to check for HTML tags and scripts. Please shorten it and try again.` It looks for an HTML tag, a script tag, `` as `>`. POST and PUT under `/agents/`, `/conversations/`, `/connectors` and `/api-toolsets`, and POST, PUT and PATCH under `/skills`, skip the filter. So does the question of a search: the `query` string of a JSON body sent to `POST /search` or `POST /retrieve` is neither checked nor rewritten, unless it is longer than 100,000 characters. **Body parsers.** `application/json` bodies (up to 10 MB, 5 MB on `/mcp`) and `application/x-www-form-urlencoded` bodies (up to 100 KB and 1000 fields) are parsed for every method. A JSON body that does not parse, or a body shorter or longer than its `Content-Length`, is answered `400 HTTP_BAD_REQUEST` (`The request body is not valid JSON.`, `The request body does not match its Content-Length.`), a body past these limits `413 HTTP_PAYLOAD_TOO_LARGE`, and an unsupported content encoding or character set `415 HTTP_UNSUPPORTED_MEDIA_TYPE`; a JSON body over 5 MB on `/mcp` gets its `413` as a JSON-RPC error (see `POST /mcp`). Form values arrive as text and the request validators do not convert them, so a number, a boolean or a free-form object sent form-encoded is refused (`400 VALIDATION_ERROR`) or read as a string. A list sent form-encoded is read as a list only when its key repeats, so a single value or an empty list cannot be sent. The form media type is therefore listed only on operations whose body is a flat object of text fields: every value a string (string enums included), with no lists or nested objects. **Request size.** On every route with a request validator, the body, query and path values are measured before the schema runs. An array over 10,000 items (` can have at most 10000 items.`), an object over 10,000 keys (` can have at most 10000 fields.`), more than 100,000 array items and object keys in the whole request (`The request holds too many items in total (the limit is 100000); the limit is reached at .`) or nesting deeper than 64 levels (` is nested more than 64 levels deep.`) is refused with `400 VALIDATION_ERROR` (detail code `TOO_BIG`, or `TOO_DEEP` for nesting; the path reads like `body.items`). The deployment can change the first three limits with `VALIDATION_MAX_ARRAY_LENGTH`, `VALIDATION_MAX_OBJECT_KEYS` and `VALIDATION_MAX_TOTAL_ITEMS`. **OAuth scopes.** On an operation whose `security` names `oauth2` scopes, an OAuth access token holding none of them gets `403 HTTP_FORBIDDEN` ("Insufficient scope. Required: ...") right after authentication. Session tokens are never scope-checked. **No such route.** A method and path that no route handles get Express's own `404` page, `text/html`, reading `Cannot `. Such pairs are not listed as operations. ## Base URLs All endpoints use the `/api/v1` prefix unless otherwise noted. ## Query parameter names On every `/api/v1` and `/mcp` endpoint, a query parameter whose name starts with `$` or contains `.` (including a nested name such as `filter[$ne]` or `a.b`) is refused with `400` and error code `VALIDATION_ERROR`. Only names are checked: values may contain dots, such as file names. version: 1.0.0 contact: name: API Support email: support@pipeshub.com servers: - url: '{instance_url}/api/v1' description: Base API URL variables: instance_url: default: https://app.pipeshub.com description: Base server URL (without /api/v1) - url: '{instance_url}' description: Root URL (used for MCP endpoints mounted at /mcp) variables: instance_url: default: https://app.pipeshub.com description: Base server URL tags: - name: User Account description: User authentication including multi-step MFA, password reset, OTP login, and token management - name: Organization Auth Config description: Admin configuration of authentication methods including MFA steps and allowed providers - name: OAuth Provider description: | PipesHub OAuth 2.0 Authorization Server implementing RFC 6749, RFC 7636 (PKCE), and OpenID Connect. **Supported Grant Types:** - `authorization_code` - Standard OAuth flow with PKCE support - `client_credentials` - Machine-to-machine authentication - `refresh_token` - Token refresh for long-lived access - `urn:ietf:params:oauth:grant-type:device_code` - Device authorization grant (RFC 8628) for devices and CLIs without a browser; started with `POST /oauth2/device_authorization` **Dynamic Client Registration (RFC 7591):** `POST /oauth2/register`, only when the instance enables it. **Security Features:** - PKCE (Proof Key for Code Exchange) for public clients - State parameter for CSRF protection - Configurable token lifetimes - Token revocation and introspection **OpenID Connect:** - ID tokens with standard claims - UserInfo endpoint for profile data - Discovery endpoint for automatic configuration **Machine tokens (`client_credentials`) — gateway and downstream identity:** Access tokens may encode **`userId === client_id`**. The **Node.js API gateway** resolves the effective user to the application's **token identity**: the service account it is pointed at (**`tokenIdentityUserId`**), or its creator for an application registered before a service account was required and never bound. It loads the application by **`client_id`** on every such token and takes the identity from that record. The JWT's **`createdBy`** claim is a cross-check rather than a source: despite its name it carries the identity, not who registered the application, and a token whose claim disagrees with the record is refused with **401** ("This token was issued for an identity the application no longer acts as"), and so is a token whose app was deleted ("OAuth app not found or revoked"). Pointing an application at a different service account also revokes the tokens it has already issued, so a stale claim should not arrive in the first place. **Python services:** Validate `Authorization: Bearer` as today and use the JWT payload’s **`userId`** as-is for scopes and user-scoped logic. Where a machine token carries **`userId === client_id`** and a **`createdBy`** claim, Python copies that claim onto **`userId`** first, so both halves of the product agree on the identity. **Operational note:** Prefer tokens whose JWT already carries the identity as **`userId`**; use **`POST /oauth-clients/{appId}/revoke-all-tokens`** and obtain new tokens from **`POST /oauth2/token`** when rotating integrations. - name: OAuth Apps description: | Manage OAuth 2.0 client applications registered with PipesHub. OAuth apps allow third-party applications to access PipesHub APIs on behalf of users or organizations. Each app receives a client ID and secret for authentication. **Who can see which apps** - **Everyone (including org admins)** sees and manages only OAuth apps **they created** (`createdBy`). Other members' apps are hidden (not listed; individual operations return not found). **Session only** - Every `/oauth-clients/*` route requires the user's interactive session JWT. OAuth access tokens and personal access tokens (`phpat_...`) are rejected with `403`, so a token issued to a client can never register, reconfigure, or revoke clients on its own. **Who authorizes vs. client credentials** - **Authorization code:** Any authenticated user in the workspace may complete consent for a valid `client_id`; issued tokens represent **that user**. - **Client credentials:** Access tokens represent the **service account the app is pointed at**, never the caller. An app that allows this grant must name one at creation, and only an administrator may choose it. Apps created before that rule, and never bound, still represent the **OAuth app creator** — the fallback this rule exists to remove. **Scopes** - `GET /oauth-clients/scopes` returns scopes grouped by category for the **signed-in user's role**. - **Org admins** may register apps that request additional **admin-only** scopes; non-admins cannot select those scopes when creating or updating an app. **App Types:** - **Confidential clients**: Server-side apps that can securely store secrets - **Public clients**: Browser/mobile apps that cannot securely store secrets (use PKCE) **App Lifecycle:** - Create apps with name, redirect URIs, allowed scopes, and optional URLs (homepage, privacy, terms) - Regenerate secrets if compromised - Suspend/activate apps to control access - Revoke all tokens for emergency access removal - name: Personal Access Tokens description: | Self-service, long-lived, scoped, revocable credentials that act as their creator — unlike an OAuth app's `client_credentials` flow, which acts as the app. **Who can create one** - **Any authenticated org member** — unlike OAuth apps, this is deliberately not admin-gated. **Session only** - Every `/personal-access-tokens/*` route requires the user's interactive session JWT. OAuth access tokens and personal access tokens (`phpat_...`) are rejected with `403`. `scopes` is capped at the instance's `MCP_SCOPES`, not at the caller's own token, so a narrowly scoped token could otherwise mint itself a full-scope, non-expiring PAT. **How it's issued** - Minted through the same OAuth access-token machinery as `/oauth2/token`, against one lazily-created, per-org synthetic OAuth app (`clientId: pat-system:`) that every PAT in that org shares. That app is hidden from `/oauth-clients/*` — it never appears in your own OAuth app list and can't be managed through those routes. - The raw token is prefixed `phpat_` ahead of the underlying JWT (see the `bearerAuth` security scheme) so it's recognizable to secret scanners. It's shown exactly once, at creation. **Expiry and scopes** - `expiryDays`: `30` (default), `90`, `365`, or `never`. - Scopes default to the org's full configured `MCP_SCOPES` set if none are requested; `GET /personal-access-tokens/scopes` lists what's available. **Admin visibility** - Regular members only ever see and revoke their own tokens. - Org admins can list and revoke *any* member's token via `/personal-access-tokens/admin*` — for incident response (a departed employee, a compromised laptop) — without needing the OAuth app CRUD access described above. - name: OpenID Connect description: | OpenID Connect 1.0 endpoints for identity federation and discovery. **Discovery:** - `/.well-known/openid-configuration` - Authorization server metadata - `/.well-known/oauth-authorization-server` - Authorization server metadata (RFC 8414) - `/.well-known/oauth-protected-resource/mcp` - Protected resource metadata (RFC 9728) - `/.well-known/jwks.json` - Public keys for token verification **UserInfo:** - `/oauth2/userinfo` - Get authenticated user's profile information **Supported Claims:** - `user_id` - User identifier - `email`, `email_verified` - Email information - `name`, `given_name`, `family_name` - Name information - name: Organizations description: Organization management operations - name: Knowledge Base description: Knowledge base management operations - name: Knowledge Hub description: Unified browse API for root and child nodes (apps, record groups, folders, records) with filtering and search - name: Connector description: Connector-related operations - name: Conversations description: AI-powered conversational chat management with citations and follow-up questions - name: Semantic Search description: Enterprise semantic search across all indexed knowledge with relevance scoring - name: Agents description: Custom AI agents with specialized capabilities and tool integrations - name: Connector Registry description: Browse available connector types and their configuration schemas - name: Connector Instances description: Create, manage, and delete connector instances for your organization - name: Connector Configuration description: Configure authentication, sync settings, and filters for connectors - name: Connector Control description: Enable/disable connectors for sync or agent functionality - name: Connector OAuth description: OAuth 2.0 authorization flow for connectors requiring user consent - name: Connector Filters description: Dynamic filter options for selecting which data to sync - name: OAuth Configuration description: Admin management of OAuth credentials for connector types - name: AI Models Providers description: Manage individual AI model providers - add, update, delete, and set defaults. - name: Web Search description: Manage web search providers (DuckDuckGo, Serper, Tavily, Exa) and settings for internet search. components: parameters: RevealStoredSecrets: name: reveal in: query required: false description: | Set to `true` to receive stored secrets instead of placeholders. Honoured only for an admin's own configuration where the deployment allows it (see `GET /configurationManager/secretReveal`); ignored for OAuth-app tokens and any other value. schema: type: string enum: - 'true' headers: X-Conversation-Id: description: | Id of the conversation this turn wrote to. Sent on success and on failure, so a caller whose first turn failed can still fetch or continue that conversation. schema: type: string format: objectId securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT description: | JWT Bearer token for authenticated requests. A personal access token (see the **Personal Access Tokens** tag) is a `phpat_`-prefixed variant of this same JWT — e.g. `phpat_eyJhbGci...`. The prefix is display-only, added for secret-scanner detectability; the gateway strips it before verifying the token, so send it exactly as issued, prefix included. scopedToken: type: http scheme: bearer bearerFormat: JWT description: | Scoped JWT token for service-to-service authentication. Format: "Bearer {scoped_token}" Required scopes vary by endpoint. oauth2: type: oauth2 description: | OAuth 2.0 authentication with fine-grained scopes. Supports authorization_code (with PKCE) and client_credentials flows. OAuth tokens are Bearer JWTs — use the same Authorization header as regular tokens. For **client_credentials**, machine JWTs may use `userId === client_id`; the Node gateway resolves the application's token identity — the service account it is pointed at, or its creator for an application registered before that was required — see **OAuth Provider** tag. flows: authorizationCode: authorizationUrl: /api/v1/oauth2/authorize tokenUrl: /api/v1/oauth2/token refreshUrl: /api/v1/oauth2/token scopes: openid: OpenID Connect authentication profile: User profile information email: User email address offline_access: Offline access (refresh tokens) org:read: Read organization information org:write: Update organization settings org:admin: Full organization administration user:read: Read user profiles user:write: Update user profiles user:invite: Invite new users user:delete: Delete users usergroup:read: Read user groups usergroup:write: Create and manage user groups team:read: Read team information team:write: Create and manage teams kb:read: Read knowledge bases and records kb:write: Create and update knowledge bases kb:delete: Delete knowledge bases and records kb:upload: Upload files to knowledge bases semantic:read: Read semantic search results and history semantic:write: Execute semantic search semantic:delete: Delete semantic search history conversation:read: Read conversations conversation:write: Create and manage conversations conversation:chat: Send messages in conversations project:read: Read projects and their conversations project:write: Create and manage projects project:delete: Delete projects agent:read: Read AI agents agent:write: Create and manage AI agents agent:execute: Execute AI agents toolset:read: Read toolsets and their authentication status toolset:write: Add, configure and authenticate toolsets toolset:delete: Remove toolsets and their credentials connector:read: Read connector configurations connector:write: Create and update connectors connector:sync: Trigger connector synchronization connector:delete: Delete connectors code:read: Read indexed code repositories and their code graph config:read: Read system configuration config:write: Update system configuration crawl:read: Read crawling jobs crawl:write: Create and manage crawling jobs crawl:delete: Delete crawling jobs skill:read: Read custom agent skills skill:write: Create and manage custom agent skills mcp:read: Read MCP servers and their tools mcp:write: Add, configure and authenticate MCP servers mcp:delete: Remove MCP servers and their credentials clientCredentials: tokenUrl: /api/v1/oauth2/token scopes: openid: OpenID Connect authentication profile: User profile information email: User email address offline_access: Offline access (refresh tokens) org:read: Read organization information org:write: Update organization settings org:admin: Full organization administration user:read: Read user profiles user:write: Update user profiles user:invite: Invite new users user:delete: Delete users usergroup:read: Read user groups usergroup:write: Create and manage user groups team:read: Read team information team:write: Create and manage teams kb:read: Read knowledge bases and records kb:write: Create and update knowledge bases kb:delete: Delete knowledge bases and records kb:upload: Upload files to knowledge bases semantic:write: Execute semantic search semantic:read: Read semantic search results and history semantic:delete: Delete semantic search history conversation:read: Read conversations conversation:write: Create and manage conversations conversation:chat: Send messages in conversations project:read: Read projects and their conversations project:write: Create and manage projects project:delete: Delete projects agent:read: Read AI agents agent:write: Create and manage AI agents agent:execute: Execute AI agents toolset:read: Read toolsets and their authentication status toolset:write: Add, configure and authenticate toolsets toolset:delete: Remove toolsets and their credentials connector:read: Read connector configurations connector:write: Create and update connectors connector:sync: Trigger connector synchronization connector:delete: Delete connectors code:read: Read indexed code repositories and their code graph config:read: Read system configuration config:write: Update system configuration crawl:read: Read crawling jobs crawl:write: Create and manage crawling jobs skill:read: Read custom agent skills skill:write: Create and manage custom agent skills mcp:read: Read MCP servers and their tools mcp:write: Add, configure and authenticate MCP servers mcp:delete: Remove MCP servers and their credentials schemas: AuthMethod: type: object additionalProperties: false description: Authentication method configuration properties: type: type: string enum: - otp - password - google - microsoft - github description: | Type of authentication method: - `password`: Email/password authentication - `otp`: One-time password via email (6-digit, expires in 10 minutes) - `google`: Google OAuth 2.0 - `microsoft`: Microsoft OAuth 2.0 - `github`: accepted and stored, but no sign-in flow implements it: `/userAccount/initAuth` never lists it and `/userAccount/authenticate` answers `400` `samlSso`, `oauth` (removed in 1.0) and `azureAd` (now `microsoft`) are refused when a policy is saved. A stored policy that still names one is read without it (`azureAd` reads as `microsoft`), with `password` added to the first step. required: - type AuthStep: type: object additionalProperties: false description: A single step in multi-factor authentication flow properties: order: type: number description: | Label of the step; must be unique across steps. Only checked to be a number: `0`, negative, fractional and values above 3 are accepted and stored as sent. Sign-in runs the steps in ascending `order`, and `GET /orgAuthConfig/authMethods` returns them renumbered 1, 2, 3 in that order. allowedMethods: type: array items: $ref: '#/components/schemas/AuthMethod' description: List of allowed authentication methods for this step. User can choose any one method from this list. A method may appear only once in the whole policy. minItems: 1 uniqueItems: true required: - order - allowedMethods AuthConfig: type: object additionalProperties: false description: | Organization authentication configuration. Supports 1-3 authentication steps for multi-factor authentication. **Validation Rules:** - Minimum 1 step, maximum 3 steps - Each step must have unique order - No duplicate methods within the same step - No method can appear in multiple steps properties: authMethods: type: array items: $ref: '#/components/schemas/AuthStep' description: List of authentication steps in order minItems: 1 maxItems: 3 required: - authMethods UpdateAuthMethodResponse: type: object additionalProperties: false description: Response after updating organization authentication methods properties: message: type: string example: Auth method updated authMethod: type: array description: Updated authentication steps (same shape as request body) items: $ref: '#/components/schemas/AuthStep' required: - message - authMethod OrgAuthConfigSetupResponse: type: object additionalProperties: false description: Response from setting up organization auth configuration properties: message: type: string required: - message InitAuthRequest: type: object additionalProperties: false description: | Optional JSON body for `/userAccount/initAuth`. Valid shapes include: omitting the body entirely, sending `{}` (empty object), or `{ "email": "
" }`. Neither the body nor `email` is required. When `email` is omitted, the session is still created and `allowedMethods` / `authProviders` are returned as usual; clients typically supply `email` later on `/userAccount/authenticate`. The `email` property remains supported mainly for legacy clients and backward compatibility. Fields other than `email` are dropped by the validator, not refused. properties: email: type: string format: email maxLength: 254 pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9-]*\.)+[A-Za-z]{2,}$ description: | Optional; retained for legacy reasons. When set, stored on the auth session for correlation with later `/authenticate` calls (RFC 5321 compliant address). When the property is present it must be a well-formed address of at most 254 characters: an empty string, `null` or a malformed address is answered `400`. example: user@example.com InitAuthResponse: type: object additionalProperties: false description: Response containing available authentication methods and session info properties: currentStep: type: integer description: Current authentication step (0-indexed). Always 0 for initial response. example: 0 allowedMethods: type: array items: type: string enum: - otp - password - google - microsoft description: List of allowed authentication methods for the current step example: - password - google - otp message: type: string description: Response message example: Authentication initialized authProviders: $ref: '#/components/schemas/AuthProviders' jitEnabled: type: boolean description: True when at least one allowed external provider has JIT provisioning enabled required: - currentStep - allowedMethods - message - authProviders - jitEnabled AuthProviderGooglePublicConfig: type: object additionalProperties: false description: Public Google OAuth settings returned to clients properties: clientId: type: string description: Google OAuth client ID enableJit: type: boolean description: Whether just-in-time user provisioning is enabled for Google AuthProviderMicrosoftPublicConfig: type: object additionalProperties: false description: Public Microsoft OAuth settings returned to clients properties: tenantId: type: string description: Microsoft tenant ID clientId: type: string description: Microsoft OAuth client ID authority: type: string description: Sign-in authority URL, built on save as `https://login.microsoftonline.com/{tenantId}` enableJit: type: boolean description: Whether just-in-time user provisioning is enabled for Microsoft AuthProviders: type: object additionalProperties: false description: | Settings of the external sign-in providers among the allowed methods, keyed by provider. A provider is listed whenever its settings can be read, which is also the case when nothing has been saved for it yet: its entry is then an empty object. Only `google` and `microsoft` have a sign-in flow. properties: google: $ref: '#/components/schemas/AuthProviderGooglePublicConfig' microsoft: $ref: '#/components/schemas/AuthProviderMicrosoftPublicConfig' AuthenticateRequest: type: object additionalProperties: false description: | Request to authenticate using specified method. **Credential format varies by method:** - `password`: `{ password: "string" }` - `otp`: `{ otp: "123456" }` (6-digit code) - `google`: `"google-id-token-string"`, or `{ credential: "google-id-token-string" }` - `microsoft`: `{ idToken: "..." }`; an `accessToken` may be sent alongside but is not read SAML (`samlSso`), generic OAuth (`oauth`) and Azure AD (`azureAd`) sign-in were removed in 1.0 and are answered `400`. Azure AD accounts sign in with `microsoft`. Any top-level field not listed here is refused with `400`. properties: method: type: string enum: - otp - password - google - microsoft description: | Authentication method to use. It must be one the session's current step allows; any other value, known or not, is answered `400`. credentials: anyOf: - $ref: '#/components/schemas/PasswordCredentials' - $ref: '#/components/schemas/OtpCredentials' - $ref: '#/components/schemas/GoogleCredentials' - $ref: '#/components/schemas/MicrosoftCredentials' - type: string minLength: 1 description: Google ID token (for google method) description: | Credentials based on the authentication method; the shape each method needs is listed above. The validator only checks that this is a non-empty string or an object whose `password`, `otp`, `token`, `code`, `accessToken` and `idToken`, when present, are strings; other fields of the object are passed through. An object without the field its method needs is refused later as a failed sign-in, not as a validation error: `microsoft` without `idToken` answers `400`, and a Google token that is not a JWT answers `500`. email: type: string format: email maxLength: 254 pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9-]*\.)+[A-Za-z]{2,}$ description: | The account's email. Used when the session was started without one (`/initAuth` with no `email`); when neither carries an email, password and code sign-in answer `500`. Ignored by the external-provider methods, which take the email from the provider. cf-turnstile-response: type: string description: Cloudflare Turnstile CAPTCHA token (optional, if CAPTCHA is enabled) required: - method - credentials PasswordCredentials: type: object additionalProperties: false description: Credentials for password authentication properties: password: type: string description: User password format: password required: - password OtpCredentials: type: object additionalProperties: false description: Credentials for OTP authentication properties: otp: type: string description: 6-digit one-time password pattern: ^\d{6}$ example: '123456' required: - otp AuthenticateMultiStepResponse: type: object additionalProperties: false description: Current authentication step succeeded; additional MFA steps remain properties: status: type: string enum: - success description: Step completion status nextStep: type: integer description: Next authentication step index allowedMethods: type: array items: type: string description: Allowed method types for the next step authProviders: $ref: '#/components/schemas/AuthProviders' required: - status - nextStep - allowedMethods - authProviders AuthenticateFinalResponse: type: object additionalProperties: false description: All authentication steps complete; JWT tokens returned properties: message: type: string description: Success message example: Fully authenticated accessToken: type: string description: JWT access token (1 hour expiry) refreshToken: type: string description: JWT refresh token (7 days expiry) required: - message - accessToken - refreshToken AuthenticateResponse: oneOf: - $ref: '#/components/schemas/AuthenticateMultiStepResponse' - $ref: '#/components/schemas/AuthenticateFinalResponse' description: | Either the next step in a multi-factor flow (`status`, `nextStep`, `allowedMethods`, `authProviders`) or final tokens (`message`, `accessToken`, `refreshToken`). AuthenticatedPasswordResetResponse: type: object additionalProperties: false description: Response after authenticated user changes password (new access token issued) properties: data: type: string example: password reset accessToken: type: string description: New JWT access token after password change required: - data - accessToken RefreshTokenResponse: type: object additionalProperties: false description: Response with new access token properties: user: $ref: '#/components/schemas/RefreshTokenUser' accessToken: type: string description: New JWT access token (24 hour default expiry, configurable via ACCESS_TOKEN_EXPIRY) required: - user - accessToken RefreshTokenUser: type: object additionalProperties: false description: User record returned with a refreshed access token properties: _id: type: string description: User ID orgId: type: string description: Organization ID email: type: string format: email fullName: type: string firstName: type: string lastName: type: string middleName: type: string mobile: type: string description: Mobile number (10-15 digits with optional +) pattern: ^$|^\+?[0-9]{10,15}$ designation: type: string kind: type: string enum: - human - service description: | Whether this account is a person who signs in (`human`) or a machine identity that automation authenticates as (`service`). Absent on records written before service accounts existed, which are all human. description: type: string description: Free text explaining what a service account is for. Unused for people. isDisabled: type: boolean default: false description: | The account is suspended: its tokens stop working and no session can be issued, but the record, its group memberships and its permission-graph node survive so it can be switched back on. restoreOpId: type: string description: | Internal and transient. Present only while a deleted service account is being brought back, and cleared when that finishes either way. It lets the steps that complete or undo a restore tell their own attempt from a later one. Not something to depend on. role: type: string enum: - admin - member description: Organization role stored on the user document (`admin` or `member`) address: $ref: '#/components/schemas/Address' dataCollectionConsent: type: boolean description: Whether user has consented to data collection hasLoggedIn: type: boolean isDeleted: type: boolean deletedBy: type: string description: ID of user who deleted this user profilePicture: type: string nullable: true description: Base64-encoded data URI of the user's display picture slug: type: string createdAt: type: string updatedAt: type: string __v: type: integer required: - _id - orgId - email - fullName - hasLoggedIn - isDeleted - slug - createdAt - updatedAt - __v OrgAuthConfigCreateRequest: type: object additionalProperties: false description: Request to create initial organization auth configuration properties: contactEmail: type: string format: email description: Organization contact email registeredName: type: string description: Organization registered name adminFullName: type: string description: Admin user full name sendEmail: type: boolean description: Whether to send welcome email default: false required: - contactEmail - registeredName - adminFullName ToolApprovalAnswer: type: object description: | The person's answer to a tool approval card. The web app sends it with the query `Tool approval: allow` or `Tool approval: deny`, and the request carries on the turn that stopped on the card: the saved call runs (when allowed) and its result answers the call the model made, in the same reply. The call is the one saved server-side when the card was shown (answerable for 15 minutes, settled once, bound to the asking user and conversation); only its id and the answer travel here. A reply that answered a card can't be regenerated. required: - approvalId - decision properties: approvalId: type: string maxLength: 64 decision: type: string enum: - allow - deny remember: type: boolean description: | "Don't ask again for this tool": the answer becomes the person's own rule for the tool (Pre-approved for allow, Deny for deny). ErrorResponse: type: object additionalProperties: false description: | Standard error envelope returned by all errors routed through `ErrorMiddleware`. Applies to all `BaseError` subclasses including `HttpError`, `ValidationError`, and others. The `code` field is a machine-readable string identifying the error type (e.g. `HTTP_UNAUTHORIZED`, `HTTP_NOT_FOUND`, `VALIDATION_ERROR`, `INTERNAL_ERROR`). properties: error: type: object additionalProperties: false required: - code - message properties: requestId: type: string description: | Identifier for this request, echoed so a bug report can quote it. Absent when the request never reached the middleware that assigns one. code: type: string description: | Machine-readable error code. For application errors it takes the form `HTTP_` For unhandled runtime errors (e.g. database unavailable) it is `INTERNAL_ERROR`. example: HTTP_FORBIDDEN message: type: string description: Human-readable description of the error example: You need admin access to do this. Ask an admin in your organisation. metadata: type: object description: Additional context (only present in development environments) additionalProperties: true required: - error StreamRecordErrorResponse: type: object additionalProperties: false description: | Error payload returned by the legacy record-stream proxy when the downstream streaming request fails after route middleware has passed. required: - error properties: error: type: string description: Human-readable error message from the gateway or downstream stream service KnowledgeLimit: type: object additionalProperties: false description: | What a connected app may see: an OAuth app's limit is picked by its creator and holds for every user who connects it; a personal access token's by its owner. Search, chat, agent chat and every record read by id stay inside it, and inside the connecting user's own permissions. Omit it for everything the user can see. Sent, it must name at least one id and at most 50 in all, since it is signed into every token the app gets. properties: apps: type: array items: type: string format: uuid description: Whole connectors and collections. recordGroups: type: array items: type: string format: uuid description: Record groups (a drive, space or project), with the groups nested in them. records: type: array items: type: string format: uuid description: Folders or records, with everything under them. OAuthTokenRequest: type: object description: | OAuth 2.0 Token Request (RFC 6749 Section 4.1.3). Request body for exchanging authorization code or credentials for tokens. properties: grant_type: type: string enum: - authorization_code - client_credentials - refresh_token - urn:ietf:params:oauth:grant-type:device_code - urn:ietf:params:oauth:grant-type:token-exchange description: | OAuth grant type: - `authorization_code`: Exchange auth code for tokens - `client_credentials`: Machine-to-machine auth - `refresh_token`: Get new access token using refresh token - `urn:ietf:params:oauth:grant-type:device_code`: Poll for the tokens of a device authorization (RFC 8628) started with `POST /oauth2/device_authorization` - `urn:ietf:params:oauth:grant-type:token-exchange`: Service-account delegation (RFC 8693). Exchanges a service token for a short-lived token that acts as one named person. Enterprise edition; kept in this enum because the request shape is the same wherever the grant is offered. code: type: string description: Authorization code (required for authorization_code grant) redirect_uri: type: string format: uri description: Redirect URI (required for authorization_code grant) device_code: type: string minLength: 1 description: | The `device_code` from `POST /oauth2/device_authorization` (required for the device_code grant; without it the answer is `400 invalid_grant`) client_id: type: string minLength: 1 description: Client ID (can also be sent via Basic auth header, which then takes precedence) client_secret: type: string description: Client secret (can also be sent via Basic auth header) refresh_token: type: string description: Refresh token (required for refresh_token grant) scope: type: string description: Requested scopes (optional, defaults to original grant scopes) code_verifier: type: string description: | PKCE code verifier (RFC 7636). Required if code_challenge was used. Must be 43-128 characters from [A-Za-z0-9-._~] pattern: ^[A-Za-z0-9\-._~]{43,128}$ subject_token: type: string minLength: 1 maxLength: 8192 description: | The service token being exchanged (token-exchange grant only). This is the only authentication the grant has: no `client_id` or `client_secret` is sent, because the service token already carries a verified identity and the scopes that bound it. subject_token_type: type: string maxLength: 128 description: | Optional. Must be `urn:ietf:params:oauth:token-type:access_token` when sent (token-exchange grant only). requested_subject: type: string minLength: 1 maxLength: 320 description: | The person to act as, by user id or email address (token-exchange grant only). Must be on the service account's allow-list, and must not be an administrator. required: - grant_type OAuthTokenResponse: type: object description: | OAuth 2.0 Token Response (RFC 6749 Section 5.1). Contains the access token and optional refresh/ID tokens. properties: access_token: type: string description: The access token for API requests token_type: type: string example: Bearer description: Token type (always "Bearer") expires_in: type: integer description: Access token lifetime in seconds example: 3600 refresh_token: type: string description: | Refresh token for obtaining new access tokens. Issued only when the token acts as a user (`authorization_code`, `refresh_token` and `device_code` grants) and the granted scopes include `offline_access`; never for `client_credentials`. scope: type: string description: | Granted scopes, space-separated (may differ from requested). Empty for a `client_credentials` token whose app holds only user-bound scopes (`openid`, `profile`, `email`, `offline_access`), which are dropped from that grant. issued_token_type: type: string description: | The type of the issued token (RFC 8693 Section 2.2.1). Returned by the token-exchange grant, where it is always `urn:ietf:params:oauth:token-type:access_token`. example: urn:ietf:params:oauth:token-type:access_token OAuthRevokeRequest: type: object description: | OAuth 2.0 Token Revocation Request (RFC 7009). Revokes an access or refresh token. properties: token: type: string minLength: 1 description: The token to revoke token_type_hint: type: string enum: - access_token - refresh_token description: Hint about token type (optional, improves performance) client_id: type: string minLength: 1 description: Client ID client_secret: type: string description: | Client secret. Required for a confidential client (missing or empty: `401 invalid_client`); a public client, such as the first-party device app, is identified by `client_id` alone. A wrong secret is `401 invalid_client`. required: - token - client_id OAuthIntrospectRequest: type: object description: | OAuth 2.0 Token Introspection Request (RFC 7662). Check if a token is active and get its metadata. properties: token: type: string minLength: 1 description: The token to introspect token_type_hint: type: string enum: - access_token - refresh_token description: Hint about token type client_id: type: string minLength: 1 description: Client ID client_secret: type: string description: | Client secret. Not checked by the validator, but client authentication cannot succeed without it: when it is missing the route answers `{active: false}` even for a live token, for public and confidential clients alike. A wrong secret is `401 invalid_client`. required: - token - client_id OAuthIntrospectResponse: type: object description: | OAuth 2.0 Token Introspection Response (RFC 7662). Contains token metadata if active, or just `active: false` if not. properties: active: type: boolean description: Whether the token is currently active scope: type: string description: Scopes granted to the token client_id: type: string description: Client ID the token was issued to username: type: string description: User identifier (if user-based token) token_type: type: string description: Token type exp: type: integer description: Token expiration timestamp (Unix epoch) iat: type: integer description: Token issuance timestamp (Unix epoch) nbf: type: integer description: Token not-before timestamp (Unix epoch) user_id: type: string description: User ID aud: type: string description: Audience (client ID) iss: type: string description: Issuer URL jti: type: string description: Unique token identifier required: - active OAuthErrorResponse: type: object description: | OAuth 2.0 Error Response (RFC 6749 Section 5.2). Standard error format for OAuth endpoints. properties: error: type: string description: | Error code. Common values: - `invalid_request` - Missing or invalid parameter - `invalid_client` - Client authentication failed - `invalid_grant` - Invalid authorization code or refresh token - `unauthorized_client` - Client not authorized for this grant type - `unsupported_grant_type` - Grant type not supported - `invalid_scope` - Requested scope is invalid or exceeds allowed - `access_denied` - User denied authorization - `invalid_target` - The requested subject cannot be acted as (RFC 8693). Returned by the token-exchange grant for every refusal about the person asked for, with one wording, so a service token cannot tell "no such person" from "not on your list". The real cause is in the delegation log. enum: - invalid_request - invalid_client - invalid_grant - invalid_target - unauthorized_client - unsupported_grant_type - invalid_scope - access_denied - server_error - authorization_pending - slow_down - expired_token error_description: type: string description: Human-readable error description error_uri: type: string format: uri description: URI with more information about the error state: type: string description: State parameter from the authorization request required: - error OAuthUserInfoResponse: type: object description: | OpenID Connect UserInfo Response. Contains claims about the authenticated user. properties: user_id: type: string description: User ID name: type: string description: Full name given_name: type: string description: First name family_name: type: string description: Last name email: type: string format: email description: Email address email_verified: type: boolean description: Whether email has been verified picture: type: string format: uri description: Profile picture URL updated_at: type: integer description: Last profile update timestamp (Unix epoch) required: - user_id CreateOAuthAppRequest: type: object description: | Request to create a new OAuth app (`createAppSchema` in `oauth.validators.ts`). **Required:** `name`, `allowedScopes`. **Optional:** `description`, `redirectUris`, `allowedGrantTypes`, `homepageUrl`, `privacyPolicyUrl`, `termsOfServiceUrl`, `isConfidential`, `accessTokenLifetime`, `refreshTokenLifetime`, `tokenIdentityUserId`. **Redirect rule (Zod refine):** If the effective grant list includes `authorization_code` (including when `allowedGrantTypes` is omitted — defaults to `authorization_code` + `refresh_token`), at least one redirect URI is required. If grants exclude `authorization_code`, `redirectUris` may be omitted. **Service account rule:** If the effective grant list includes `client_credentials`, `tokenIdentityUserId` is **required** and the caller must be an organisation administrator. That grant issues tokens with no person in them, so without a service account the app would act as whoever created it. Omitting it answers `400`; a non-administrator answers `403`. properties: name: type: string description: App name (displayed to users during authorization) minLength: 1 maxLength: 100 example: My Integration App tokenIdentityUserId: type: string description: | The service account this app's own tokens act as. Required when `allowedGrantTypes` includes `client_credentials`, ignored otherwise. Must name a service account in the same organisation that is not disabled; an ordinary user's id is refused. Administrators only. example: 65f0c0ffee00000000000000 description: type: string description: App description maxLength: 500 example: Integrates PipesHub with our internal tools redirectUris: type: array items: type: string format: uri pattern: ^(?:cursor://anysphere\.cursor-mcp/oauth/callback|[\x00-\x20]*(?:[Hh][Tt][Tt][Pp][Ss]:[/\\]*[^/\\?#\x00-\x20][^#]*#?|[A-Za-z][A-Za-z0-9+.-]*://(?:[^/?#@]*@)?(?:[Ll][Oo][Cc][Aa][Ll][Hh][Oo][Ss][Tt]|127\.0\.0\.1)(?::[0-9]*)?(?:[/?][^#]*)?#?)[\x00-\x20]*)$ description: | Allowed redirect URIs (max 10). Required when an effective grant list includes `authorization_code` (including the default when `allowedGrantTypes` is omitted). Each URI must parse as a URL (`VALIDATION_ERROR` otherwise), and the service then refuses with `400 OAUTH_INVALID_REDIRECT_URI` any URI that is not `https:` and whose host is not `localhost` or `127.0.0.1` (any scheme is let through on those two hosts), or that carries a fragment. The one exception is the literal `cursor://anysphere.cursor-mcp/oauth/callback`. Both checks use WHATWG `new URL()`, so the scheme and a `localhost` host match in any case, `https:` may be followed by any number of `/` or `\` (or none), and surrounding whitespace is allowed and trimmed before the URI is stored; the URI is otherwise stored as sent. `https://` with no host does not parse. The pattern approximates these rules. maxItems: 10 example: - https://myapp.com/callback - http://localhost:3000/callback allowedGrantTypes: type: array items: type: string enum: - authorization_code - client_credentials - refresh_token description: | Allowed grant types. Defaults to `["authorization_code", "refresh_token"]` if omitted (applied by the service, not Zod). example: - authorization_code - refresh_token allowedScopes: type: array items: type: string description: Scopes the app can request (non-empty) minItems: 1 example: - openid - profile - read:records homepageUrl: type: string format: uri pattern: '^[A-Za-z][A-Za-z0-9+.-]*:' description: App homepage URL (shown during authorization). Must parse as a URL (`z.string().url()`); the pattern is the closest JSON Schema can say. privacyPolicyUrl: type: string format: uri pattern: '^[A-Za-z][A-Za-z0-9+.-]*:' description: Privacy policy URL. Must parse as a URL (`z.string().url()`); the pattern is the closest JSON Schema can say. termsOfServiceUrl: type: string format: uri pattern: '^[A-Za-z][A-Za-z0-9+.-]*:' description: Terms of service URL. Must parse as a URL (`z.string().url()`); the pattern is the closest JSON Schema can say. isConfidential: type: boolean description: | Whether the app can securely store secrets. - `true`: Server-side app (secret required for token requests) - `false`: Browser/mobile app (must use PKCE) default: true accessTokenLifetime: type: integer description: Access token lifetime in seconds (300–86400) minimum: 300 maximum: 86400 default: 3600 example: 3600 refreshTokenLifetime: type: integer description: Refresh token lifetime in seconds (3600–31536000) minimum: 3600 maximum: 31536000 default: 2592000 example: 2592000 knowledgeLimit: $ref: '#/components/schemas/KnowledgeLimit' required: - name - allowedScopes UpdateOAuthAppRequest: type: object description: | Request to update an OAuth app (`updateAppSchema` in `oauth.validators.ts`). All fields are optional — include only fields that should change. URL fields (`homepageUrl`, `privacyPolicyUrl`, `termsOfServiceUrl`) accept `null` to clear them (nullable in Zod). **Redirect rule (Zod refine):** If `allowedGrantTypes` includes `authorization_code` and `redirectUris` is present in the body, `redirectUris` must contain at least one URI. properties: name: type: string description: App name minLength: 1 maxLength: 100 description: type: string description: App description maxLength: 500 redirectUris: type: array items: type: string format: uri pattern: ^(?:cursor://anysphere\.cursor-mcp/oauth/callback|[\x00-\x20]*(?:[Hh][Tt][Tt][Pp][Ss]:[/\\]*[^/\\?#\x00-\x20][^#]*#?|[A-Za-z][A-Za-z0-9+.-]*://(?:[^/?#@]*@)?(?:[Ll][Oo][Cc][Aa][Ll][Hh][Oo][Ss][Tt]|127\.0\.0\.1)(?::[0-9]*)?(?:[/?][^#]*)?#?)[\x00-\x20]*)$ description: | Allowed redirect URIs (up to 10). Required when `authorization_code` grant type is enabled. Preserved in the database even if `authorization_code` is removed from grant types. Each URI must parse as a URL (`VALIDATION_ERROR` otherwise), and the service then refuses with `400 OAUTH_INVALID_REDIRECT_URI` any URI that is not `https:` and whose host is not `localhost` or `127.0.0.1` (any scheme is let through on those two hosts), or that carries a fragment. The one exception is the literal `cursor://anysphere.cursor-mcp/oauth/callback`. Both checks use WHATWG `new URL()`, so the scheme and a `localhost` host match in any case, `https:` may be followed by any number of `/` or `\` (or none), and surrounding whitespace is allowed and trimmed before the URI is stored; the URI is otherwise stored as sent. `https://` with no host does not parse. The pattern approximates these rules. maxItems: 10 allowedGrantTypes: type: array items: type: string enum: - authorization_code - client_credentials - refresh_token allowedScopes: type: array items: type: string minItems: 1 homepageUrl: type: string format: uri pattern: '^[A-Za-z][A-Za-z0-9+.-]*:' nullable: true description: '`null` clears it. Must parse as a URL (`z.string().url()`); the pattern is the closest JSON Schema can say.' privacyPolicyUrl: type: string format: uri pattern: '^[A-Za-z][A-Za-z0-9+.-]*:' nullable: true description: '`null` clears it. Must parse as a URL (`z.string().url()`); the pattern is the closest JSON Schema can say.' termsOfServiceUrl: type: string format: uri pattern: '^[A-Za-z][A-Za-z0-9+.-]*:' nullable: true description: '`null` clears it. Must parse as a URL (`z.string().url()`); the pattern is the closest JSON Schema can say.' accessTokenLifetime: type: integer minimum: 300 maximum: 86400 refreshTokenLifetime: type: integer minimum: 3600 maximum: 31536000 knowledgeLimit: description: | `null` lifts the limit. A change that takes anything away revokes the app's outstanding tokens, so users connect again. A change that only adds (a lift included) keeps them: each token, and each refresh of it, stays at the limit its user agreed to, and the new items reach a user once they connect again. nullable: true allOf: - $ref: '#/components/schemas/KnowledgeLimit' OAuthAppResponse: type: object description: | OAuth app details (without secret). Fields under `required:` always appear in `toAppResponse` (`oauth.app.service.ts`); optional URL/description fields are only present when set by the caller. required: - id - slug - clientId - name - redirectUris - allowedGrantTypes - allowedScopes - status - isConfidential - accessTokenLifetime - refreshTokenLifetime - createdAt - updatedAt properties: id: type: string description: App ID slug: type: string description: URL-friendly app slug clientId: type: string description: OAuth client ID name: type: string description: App name description: type: string description: App description redirectUris: type: array items: type: string format: uri description: Allowed redirect URIs (always returned; may be empty) allowedGrantTypes: type: array items: type: string description: Allowed grant types allowedScopes: type: array items: type: string description: Allowed scopes status: type: string enum: - active - suspended - revoked description: App status homepageUrl: type: string format: uri description: App homepage privacyPolicyUrl: type: string format: uri description: Privacy policy URL termsOfServiceUrl: type: string format: uri description: Terms of service URL isConfidential: type: boolean description: Whether app is a confidential client accessTokenLifetime: type: integer description: Access token lifetime in seconds refreshTokenLifetime: type: integer description: Refresh token lifetime in seconds knowledgeLimit: $ref: '#/components/schemas/KnowledgeLimit' tokenIdentityUserId: type: string description: | The service account this app's own `client_credentials` tokens act as. Absent means the app has no service account and its tokens act as whoever created it. Apps registered since a service account became required always have one; an older app may not, and those are the ones an administrator still has to move. example: 65f0c0ffee00000000000000 createdAt: type: string format: date-time description: Creation timestamp updatedAt: type: string format: date-time description: Last update timestamp OAuthAppWithSecret: allOf: - $ref: '#/components/schemas/OAuthAppResponse' - type: object properties: clientSecret: type: string description: | Client secret (only shown on creation and secret regeneration). Store this securely - it cannot be retrieved later. required: - clientSecret CreateOAuthAppResponse: type: object description: | Response body for `POST /oauth-clients` (`oauth.app.controller.ts` `createApp`). The new app (including one-time `clientSecret`) is nested under `app`. required: - message - app properties: message: type: string example: OAuth app created successfully app: $ref: '#/components/schemas/OAuthAppWithSecret' RegenerateOAuthAppSecretResponse: type: object description: | Response body for `POST /oauth-clients/{appId}/regenerate-secret` (`regenerateSecret`). required: - message - clientId - clientSecret properties: message: type: string example: Client secret regenerated successfully clientId: type: string description: OAuth client ID (unchanged) clientSecret: type: string description: New client secret (store securely; previous secret is invalidated) UpdateOAuthAppResponse: type: object description: | Response body for `PUT /oauth-clients/{appId}` (`oauth.app.controller.ts` `updateApp`). Updated app (never includes `clientSecret`) is nested under `app`. required: - message - app properties: message: type: string example: OAuth app updated successfully app: $ref: '#/components/schemas/OAuthAppResponse' SuspendOAuthAppResponse: type: object description: | Response body for `POST /oauth-clients/{appId}/suspend` (`oauth.app.controller.ts` `suspendApp`). Suspended app (never includes `clientSecret`) is nested under `app`. required: - message - app properties: message: type: string example: OAuth app suspended successfully app: $ref: '#/components/schemas/OAuthAppResponse' ActivateOAuthAppResponse: type: object description: | Response body for `POST /oauth-clients/{appId}/activate` (`oauth.app.controller.ts` `activateApp`). Re-activated app (never includes `clientSecret`) is nested under `app`. required: - message - app properties: message: type: string example: OAuth app activated successfully app: $ref: '#/components/schemas/OAuthAppResponse' OAuthAppListResponse: type: object description: Paginated list of OAuth apps required: - data - pagination properties: data: type: array items: $ref: '#/components/schemas/OAuthAppResponse' description: List of OAuth apps pagination: type: object required: - page - limit - total - totalPages properties: page: type: integer description: Current page number limit: type: integer description: Items per page total: type: integer description: Total number of items totalPages: type: integer description: Total number of pages OAuthScopeInfo: type: object description: Information about an OAuth scope properties: name: type: string description: Scope identifier example: openid description: type: string description: Human-readable scope description example: OpenID Connect authentication category: type: string description: Scope category for grouping (matches the key under `scopes` on list responses) example: Identity requiresUserConsent: type: boolean description: Whether end-user consent is required when this scope is requested example: false required: - name - description - category - requiresUserConsent OAuthScopesGroupedResponse: type: object description: | OAuth scopes available to the signed-in user for app registration, grouped by category label. Category keys are UI labels (e.g. `Identity`, `Knowledge Base`); each maps to a list of scopes in that group. Categories defined in server config may appear with an **empty array** when every scope in that category is restricted for the caller's role (e.g. non–org-admin users never receive admin-only scopes). properties: scopes: type: object description: Map of category display name to scopes in that category additionalProperties: type: array items: $ref: '#/components/schemas/OAuthScopeInfo' required: - scopes OAuthClientManagementRateLimitError: type: object description: JSON body when OAuth client management routes exceed the per-minute rate limit (same limiter as other `/oauth-clients/*` routes). required: - error properties: error: type: object required: - code - message properties: code: type: string example: TOO_MANY_REQUESTS message: type: string example: Too many OAuth client requests. Please try again later. retryAfter: type: integer nullable: true description: Seconds until the limit window resets (when `Retry-After` is present); may be null. ApplicationJsonErrorResponse: type: object description: | Standard JSON error envelope from `ErrorMiddleware` for `BaseError` subclasses (`error.middleware.ts`). Returned for most API 4xx errors (unauthorized, forbidden, not found, validation failures, etc.). required: - error properties: error: type: object required: - code - message properties: requestId: type: string description: | Identifier for this request, echoed so a bug report can quote it. Absent when the request never reached the middleware that assigns one. code: type: string description: Machine-readable code (e.g. `HTTP_UNAUTHORIZED`, `HTTP_FORBIDDEN`). message: type: string metadata: type: object description: Optional; may appear in non-production for some errors. additionalProperties: true RateLimitErrorResponse: type: object description: Rate-limit error payload (shape differs from standard error responses). properties: error: type: object properties: code: type: string description: Error code. message: type: string retryAfter: type: integer nullable: true description: Suggested seconds to wait before retrying. required: - error OAuthTokenListItem: type: object description: | Information about an issued token (one element returned by `listTokensForApp` in `oauth_token.service.ts`). `userId` is omitted for client-credentials access tokens; all other fields are always populated. required: - id - tokenType - scopes - createdAt - expiresAt - isRevoked properties: id: type: string description: Token ID tokenType: type: string enum: - access - refresh description: Type of token userId: type: string description: User ID (omitted for client-credentials access tokens) scopes: type: array items: type: string description: Granted scopes createdAt: type: string format: date-time description: Token creation time expiresAt: type: string format: date-time description: Token expiration time isRevoked: type: boolean description: Whether token has been revoked OAuthAppTokensListResponse: type: object description: | Response body for `GET /oauth-clients/{appId}/tokens` (`listAppTokens`). required: - tokens properties: tokens: type: array items: $ref: '#/components/schemas/OAuthTokenListItem' description: Active access and refresh tokens for the app CreatePatRequest: type: object description: | Request to create a personal access token (`createPatTokenSchema` in `pat.validators.ts`). Fields not listed here are stripped by the validator and ignored. required: - name properties: name: type: string description: Label to help you recognize the token later minLength: 1 maxLength: 100 example: Claude Desktop scopes: type: array items: type: string minItems: 1 description: | Scopes to grant, validated against the org's configured `MCP_SCOPES` (not the full role-aware OAuth-app scope set). Defaults to every scope in `MCP_SCOPES` if omitted. example: - kb:read - semantic:write maxItems: 100 expiryDays: oneOf: - type: integer enum: - 30 - 90 - 365 - type: string enum: - never description: | Token lifetime. Defaults to `30` if omitted — a token minted without an explicit choice shouldn't default to the longest lifetime. `"never"` is stored as a ~100-year expiry (the underlying schema field is required and TTL-indexed, so there's no literal null option). Any other value, including `null` and the string `"30"`, is a `400` validation error. default: 30 example: 30 knowledgeLimit: $ref: '#/components/schemas/KnowledgeLimit' PatListItem: type: object description: | A personal access token as seen by its own creator (one element of `GET /personal-access-tokens`'s `tokens` array, or the `token` object returned by `POST /personal-access-tokens` before `accessToken` is added). required: - id - name - scopes - createdAt - expiresAt properties: id: type: string description: Token ID name: type: string description: Token name scopes: type: array items: type: string description: Granted scopes createdAt: type: string format: date-time expiresAt: type: string format: date-time description: | Expiry timestamp. A `"never"`-expiry token is stored as a ~100-year-out date, not a literal null — treat anything decades out as "never" rather than a real deadline. lastUsedAt: type: string format: date-time description: | Last time this token successfully authenticated a request. Throttled server-side to update at most once per 5 minutes per token; absent if the token has never been used. knowledgeLimit: $ref: '#/components/schemas/KnowledgeLimit' PatWithSecret: allOf: - $ref: '#/components/schemas/PatListItem' - type: object required: - accessToken properties: accessToken: type: string description: | The raw token, `phpat_`-prefixed. Returned **only** in this creation response — only its hash is stored server-side, so it cannot be retrieved again later. example: phpat_eyJhbGciOiJIUzI1NiIs... CreatePatResponse: type: object description: Response body for `POST /personal-access-tokens` (`pat.controller.ts` `createToken`). required: - message - token properties: message: type: string example: Personal access token created successfully token: $ref: '#/components/schemas/PatWithSecret' ListPatResponse: type: object description: | Response body for `GET /personal-access-tokens` (`listTokens`) — the caller's own active tokens, capped at 100 most-recent server-side. Unlike the admin list, this is a flat array with no pagination envelope and no owner fields (it's implicitly scoped to the caller). required: - tokens properties: tokens: type: array items: $ref: '#/components/schemas/PatListItem' RevokePatRequest: type: object description: | Optional request body for `DELETE /personal-access-tokens/{tokenId}` and `DELETE /personal-access-tokens/admin/{tokenId}`. The body itself is optional; `reason`, if present, is stored on the revocation for auditing. properties: reason: type: string description: | Stored on the revocation for auditing. There is no body validator: a `reason` that is not a string is ignored rather than refused, and the revocation goes ahead with the default reason. example: rotated RevokePatResponse: type: object required: - message properties: message: type: string example: Personal access token revoked successfully PatScopesListResponse: type: object description: | Response body for `GET /personal-access-tokens/scopes` (`listScopes`). Unlike `GET /oauth-clients/scopes`, this is a **flat array**, not grouped by category, and reflects the org's configured `MCP_SCOPES` rather than the full role-aware OAuth-app scope catalog. required: - scopes properties: scopes: type: array items: $ref: '#/components/schemas/OAuthScopeInfo' AdminPatListItem: description: | A personal access token as seen by an org admin — extends `PatListItem` with who it belongs to, since an admin is looking across every member's tokens rather than just their own. allOf: - $ref: '#/components/schemas/PatListItem' - type: object required: - userId - ownerDeleted properties: userId: type: string description: ID of the user who created this token ownerEmail: type: string format: email description: | Owner's email. Populated even when `ownerDeleted` is true (last-known value), for auditing. ownerFullName: type: string description: Owner's full name (same last-known-value behavior as `ownerEmail`) ownerDeleted: type: boolean description: | True if the owning user has been removed from the org (or no longer resolves at all). The token still appears — a deleted user's tokens stop authenticating automatically, but stay visible here so an admin can audit/clean them up. AdminPatListResponse: type: object description: | Response body for `GET /personal-access-tokens/admin` (`adminListTokens`). Paginated — unlike the self-service `ListPatResponse` — since an org can have far more active tokens than a fixed-window cap's worth. required: - data - pagination properties: data: type: array items: $ref: '#/components/schemas/AdminPatListItem' pagination: type: object required: - page - limit - total - totalPages properties: page: type: integer limit: type: integer description: Items per page (max 100) total: type: integer totalPages: type: integer Address: type: object additionalProperties: false properties: _id: type: string format: ObjectId description: Optional address document id addressLine1: type: string description: Address line 1 city: type: string description: City state: type: string description: State/Province postCode: type: string description: Postal/ZIP code country: type: string description: Country Organization: type: object properties: _id: type: string format: ObjectId description: Unique organization identifier slug: type: string description: Unique slug for the organization registeredName: type: string description: Registered name shortName: type: string description: Short name or display name domain: type: string description: Organization domain contactEmail: type: string format: email description: Contact email address accountType: type: string enum: - individual - business description: Type of account permanentAddress: $ref: '#/components/schemas/Address' onBoardingStatus: type: string enum: - configured - notConfigured - skipped description: Onboarding status isDeleted: type: boolean description: Soft delete flag default: false __v: type: integer description: Document version (MongoDB) createdAt: type: string format: date-time description: Creation timestamp (ISO 8601) updatedAt: type: string format: date-time description: Last update timestamp (ISO 8601) signupChannel: type: string description: 'Where the signup came from: the `ref` tag it was made with, or `direct`. Absent on organizations created before it was recorded.' signupReferrerDomain: type: string description: For a `direct` signup, the referring site's domain, when there was one. required: - _id - slug - registeredName - domain - contactEmail - accountType - onBoardingStatus - isDeleted - createdAt - updatedAt - __v KnowledgeBaseCreateResponse: type: object additionalProperties: false description: Response returned when a knowledge base is created properties: id: type: string description: Knowledge base ID name: type: string description: Knowledge base name createdAtTimestamp: type: integer format: int64 description: Creation timestamp in milliseconds updatedAtTimestamp: type: integer format: int64 description: Last update timestamp in milliseconds userRole: type: string enum: - OWNER - WRITER - READER description: User's role in this knowledge base required: - id - name - createdAtTimestamp - updatedAtTimestamp - userRole FolderCreateResponseSchema: type: object additionalProperties: false description: Response returned when a folder is created (root or nested subfolder) properties: id: type: string description: Unique folder identifier name: type: string description: Name of the folder required: - id - name FolderUpdateResponseSchema: type: object additionalProperties: false description: Response returned by PUT /knowledgeBase/{kbId}/folder/{folderId} (updateFolder). required: - success - message properties: success: type: boolean example: true message: type: string example: Folder updated successfully FolderDeleteResponseSchema: type: object additionalProperties: false description: Response returned by DELETE /knowledgeBase/{kbId}/folder/{folderId} (deleteFolder). required: - success - message properties: success: type: boolean example: true message: type: string example: Folder deleted successfully KnowledgeBaseMoveRecordRequestBody: type: object description: Request body for PUT /knowledgeBase/{kbId}/record/{recordId}/move (moveRecord). Fields other than `newParentId` are dropped by the validator, not refused. required: - newParentId properties: newParentId: type: string nullable: true description: Target folder ID, or null to move the record to the knowledge base root KnowledgeBaseMoveRecordResponse: type: object additionalProperties: false description: Response returned by PUT /knowledgeBase/{kbId}/record/{recordId}/move (moveRecord). required: - success - message properties: success: type: boolean example: true message: type: string example: Record moved successfully GetAllKnowledgeBaseResponseSchema: type: object additionalProperties: false description: Response returned by GET /knowledgeBase (listKnowledgeBases). required: - knowledgeBases - pagination - filters properties: knowledgeBases: type: array items: type: object additionalProperties: false required: - id - name - connectorId - createdAtTimestamp - updatedAtTimestamp - createdBy - userRole - folders properties: id: type: string name: type: string connectorId: type: string nullable: true createdAtTimestamp: type: integer format: int64 updatedAtTimestamp: type: integer format: int64 createdBy: type: string userRole: type: string enum: - OWNER - WRITER - READER status: type: string nullable: true enum: - IDLE - QUEUED - SYNCING - FULL_SYNCING - DELETING description: | The knowledge base's processing state. `DELETING` while it is being deleted (writes are refused with 409). `IDLE`, `QUEUED`, `SYNCING` or `FULL_SYNCING` otherwise, or null on a knowledge base that has not been processed yet. folders: type: array items: type: object additionalProperties: false required: - id - name properties: id: type: string name: type: string createdAtTimestamp: type: integer format: int64 path: type: string pagination: type: object additionalProperties: false required: - page - limit - totalCount - totalPages - hasNext - hasPrev properties: page: type: integer limit: type: integer totalCount: type: integer totalPages: type: integer hasNext: type: boolean hasPrev: type: boolean filters: type: object additionalProperties: false required: - applied - available properties: applied: type: object additionalProperties: false description: | Active filters. Empty `{}` when defaults. Keys use snake_case for sort fields (backend convention in kb_service.py). properties: search: type: string permissions: type: array items: type: string enum: - OWNER - WRITER - READER sort_by: type: string enum: - name - createdAtTimestamp - updatedAtTimestamp - userRole sort_order: type: string enum: - asc - desc available: type: object additionalProperties: false required: - permissions - sortFields - sortOrders properties: permissions: type: array items: type: string enum: - OWNER - WRITER - READER sortFields: type: array items: type: string enum: - name - createdAtTimestamp - updatedAtTimestamp - userRole sortOrders: type: array items: type: string enum: - asc - desc GetKnowledgeBaseById: type: object additionalProperties: false description: Response returned by GET /knowledgeBase/{kbId} (getKnowledgeBase). required: - id - name - connectorId - createdAtTimestamp - updatedAtTimestamp - createdBy - userRole - folders properties: id: type: string description: Knowledge base ID name: type: string description: Knowledge base name connectorId: type: string nullable: true description: Associated connector ID (null for manual KBs) createdAtTimestamp: type: integer format: int64 description: Creation timestamp in milliseconds updatedAtTimestamp: type: integer format: int64 description: Last update timestamp in milliseconds createdBy: type: string description: User ID of the creator userRole: type: string enum: - OWNER - WRITER - READER description: User's role in this knowledge base status: type: string nullable: true enum: - IDLE - QUEUED - SYNCING - FULL_SYNCING - DELETING description: | `DELETING` while the knowledge base is being deleted: reads work, every write is refused with 409 (`error.code` `HTTP_KB_BEING_DELETED`), and deleting it again finishes the job. Otherwise its processing state (`IDLE`, `QUEUED`, `SYNCING`, `FULL_SYNCING`), or null before it is first processed. folders: type: array description: | Every folder in this knowledge base, nested ones included, with nothing that tells a nested folder from a root-level one (no parent id). Files are not listed. A folder moved to the trash is still listed. items: type: object additionalProperties: false required: - id - name properties: id: type: string description: Folder ID name: type: string description: Folder name createdAtTimestamp: type: integer format: int64 description: Creation timestamp in milliseconds UpdateKnowledgeBaseById: type: object additionalProperties: false description: Response returned by PUT /knowledgeBase/{kbId} (updateKnowledgeBase). required: - success - message properties: success: type: boolean example: true message: type: string example: Knowledge base updated successfully DeleteKnowledgeBaseById: type: object additionalProperties: false description: Response returned by DELETE /knowledgeBase/{kbId} (deleteKnowledgeBase). required: - success - message properties: success: type: boolean example: true message: type: string example: Knowledge base deleted successfully RecordTypeEnum: type: string description: | Type of content. Mirrors the backend `RecordType` enum (`backend/python/app/models/entities.py`); connector-sourced records may use any of the connector-specific types below. - FILE: Uploaded or synced documents (PDF, DOCX, etc.) - DRIVE: Drive/folder container (Google Drive, OneDrive, etc.) - WEBPAGE: Web pages crawled or bookmarked - DATABASE: Database object (e.g. Notion database) - DATASOURCE: Data source object - MESSAGE: Chat/messaging content (Slack, Teams) - MAIL: Email messages (Gmail, Outlook) - GROUP_MAIL: Group/shared mailbox email messages - TICKET: Support/issue tickets (Jira, ServiceNow) - COMMENT: Comments from collaboration tools - INLINE_COMMENT: Inline comments anchored to content (e.g. Confluence) - CONFLUENCE_PAGE: Confluence page - CONFLUENCE_BLOGPOST: Confluence blog post - SHAREPOINT_PAGE: SharePoint page - SHAREPOINT_LIST: SharePoint list - SHAREPOINT_LIST_ITEM: SharePoint list item - SHAREPOINT_DOCUMENT_LIBRARY: SharePoint document library - LINK: Web link / bookmark - PROJECT: Project entity (e.g. Jira project) - PULL_REQUEST: Source-control pull request - MEETING: Meeting record (e.g. Zoom) - PRODUCT: Product entity (CRM) - DEAL: Deal/opportunity entity (CRM) - CASE: Case entity (CRM/support) - TASK: Task entity - ARTIFACT: Generated/derived artifact - CODE_FILE: Source-code file - SQL_TABLE: SQL table object - SQL_VIEW: SQL view object - OTHERS: Miscellaneous content types enum: - FILE - DRIVE - WEBPAGE - DATABASE - DATASOURCE - MESSAGE - MAIL - GROUP_MAIL - TICKET - COMMENT - INLINE_COMMENT - CONFLUENCE_PAGE - CONFLUENCE_BLOGPOST - SHAREPOINT_PAGE - SHAREPOINT_LIST - SHAREPOINT_LIST_ITEM - SHAREPOINT_DOCUMENT_LIBRARY - LINK - PROJECT - PULL_REQUEST - MEETING - PRODUCT - DEAL - CASE - TASK - ARTIFACT - CODE_FILE - SQL_TABLE - SQL_VIEW - OTHERS example: FILE ConnectorNameEnum: type: string description: | Name of the source connector. Mirrors the values of the backend `Connectors` enum (`backend/python/app/config/constants/arangodb.py`); records store the enum value (e.g. Google Drive is `DRIVE`, SharePoint Online is `SHAREPOINT ONLINE`), not the enum member name. enum: - DRIVE - DRIVE WORKSPACE - GMAIL - GMAIL WORKSPACE - CALENDAR - ONEDRIVE - SHAREPOINT ONLINE - OUTLOOK - OUTLOOK PERSONAL - OUTLOOK CALENDAR - MICROSOFT TEAMS - NOTION - NOTION PERSONAL - SLACK - SLACK WORKSPACE - KB - CONFLUENCE - CONFLUENCE DATA CENTER - CONFLUENCE DATA CENTER PERSONAL - JIRA - JIRA PERSONAL - JIRA CLOUD PERSONAL - JIRA DATA CENTER - JIRA DATA CENTER PERSONAL - BOX - NEXTCLOUD - DROPBOX - DROPBOX PERSONAL - WEB - BOOKSTACK - DRUPAL WIKI - GITHUB - GITHUB TEAMS - SERVICENOW - SALESFORCE - S3 - MINIO - GCS - AZURE BLOB - AZURE FILES - LINEAR - ZAMMAD - ZENDESK - ZOOM - GITLAB - GITLAB PERSONAL - SNOWFLAKE - POSTGRESQL - MARIADB - UNKNOWN - RSS - LOCAL_FS - CODING_SANDBOX - DATABASE_SANDBOX - IMAGE_GENERATION - ATTACHMENTS - DEMO - SMB - CIFS example: DRIVE StageStateSummary: type: object additionalProperties: false description: | Status of one pipeline stage (e.g. `classify`) for the record's current content revision. required: - stage - status - reason - attempt - startedAtMs - finishedAtMs - updatedAtMs properties: stage: type: string description: Pipeline stage name. example: classify status: type: string description: Stage status (same values as `indexingStatus`). example: COMPLETED reason: type: string nullable: true description: Failure or skip reason, when there is one. attempt: type: integer description: Number of times this stage has run for this revision. startedAtMs: type: integer format: int64 nullable: true finishedAtMs: type: integer format: int64 nullable: true updatedAtMs: type: integer format: int64 nullable: true GetRecordByIdResponseSchema: type: object additionalProperties: false description: | Response returned by GET /knowledgeBase/record/{recordId}. For a connected app with a limit, a collection or folder the limit leaves out is not named: `knowledgeBase` and `folder` are `null` for it, as are the record's own `recordGroupId` (with `externalGroupId`), `rootRecordGroupId` and `parentNodeId` (with `externalParentId`). `externalParentId` is also `null` when there is no `parentNodeId` to check it by, unless the limit covers the record's whole app. `fileRecord.path` is `null` unless the limit covers the whole app or collection; `webUrl` is kept. required: - record - knowledgeBase - folder - metadata - permissions properties: record: type: object additionalProperties: false required: - id - orgId - recordName - externalRecordId - connectorId - connectorName - recordType - origin - version - createdAtTimestamp - updatedAtTimestamp - sourceCreatedAtTimestamp - sourceLastModifiedTimestamp - indexingStatus - extractionStatus - isDeleted - isArchived - isVLMOcrProcessed - mimeType - sizeInBytes - webUrl - fileRecord - mailRecord - ticketRecord properties: id: type: string orgId: type: string recordName: type: string externalRecordId: type: string externalRootGroupId: type: string nullable: true externalGroupId: type: string nullable: true externalParentId: type: string nullable: true externalRevisionId: type: string nullable: true recordGroupId: type: string nullable: true rootRecordGroupId: type: string nullable: true description: | Internal identifier of the top-most record group in this record's chain. A group with no parent is its own root, so this is null only for records written before the field existed, or by connectors that do not set it. accessRule: type: string enum: - OPEN - STRICT - RESTRICTED description: | How this record's own permissions combine with its ancestors'. OPEN: an accessible parent, or a direct grant, is enough. STRICT: every ancestor up to the connector must be accessible too. RESTRICTED: strict, and a grant on the record itself is required. Absent on records written before the field existed; read as OPEN. uploadedBy: type: string nullable: true description: | User id of whoever uploaded the file to the knowledge base. Absent for connector records and for files uploaded before the field existed. sourceTimestampOrigin: type: string description: | Knowledge-base uploads only: what set sourceCreatedAtTimestamp and sourceLastModifiedTimestamp. `upload`: the upload time (the file came without a last-modified time); `file`: the file's last-modified time sent with the upload; `content`: dates the document states in its header (CONTENT_DOCUMENT_DATES), stored at midnight UTC of each stated day. Absent for connector records, whose timestamps are the source system's, and for files uploaded before the field existed. uploadSourceCreatedAtTimestamp: type: integer format: int64 nullable: true description: | With sourceTimestampOrigin `content`: the sourceCreatedAtTimestamp the upload had set, kept so it can be restored. uploadSourceLastModifiedTimestamp: type: integer format: int64 nullable: true description: | With sourceTimestampOrigin `content`: the sourceLastModifiedTimestamp the upload had set. uploadSourceTimestampOrigin: type: string description: | With sourceTimestampOrigin `content`: what had set the upload's values (`upload` or `file`). connectorId: type: string connectorName: $ref: '#/components/schemas/ConnectorNameEnum' recordType: $ref: '#/components/schemas/RecordTypeEnum' origin: type: string version: type: integer isLatestVersion: type: boolean nullable: true createdAtTimestamp: type: integer format: int64 updatedAtTimestamp: type: integer format: int64 sourceCreatedAtTimestamp: type: integer format: int64 sourceLastModifiedTimestamp: type: integer format: int64 lastSyncTimestamp: type: integer format: int64 nullable: true lastIndexTimestamp: type: integer format: int64 lastExtractionTimestamp: type: integer format: int64 entityExtractionStatus: type: string pattern: ^[A-Z_]{1,32}$ description: | Where named-entity extraction (people, organizations, dates and other values found in the content) stands for this record: NOT_STARTED, IN_PROGRESS, COMPLETED, PARTIAL, FAILED or SKIPPED. Absent when no status has been stored on the record; read that as not extracted. New values may be added, so treat an unknown one as not finished. lastEntityExtractionTimestamp: type: integer format: int64 nullable: true description: When named-entity extraction last ran on the record (epoch milliseconds). Absent until it is written. entityExtractionStrategy: type: string nullable: true description: | How the named entities were found: `deterministic` (pattern recognizers only), `single_call` (one model call) or `agent`. Absent until it is written. entityExtractorVersion: type: string nullable: true description: Version of the named-entity extractor that produced the record's entities, for example `ner-1`. Absent until it is written. secretsRedacted: type: integer description: Credentials (keys, tokens, passwords) removed from a code repository file before it was indexed. Present once the file has been scanned, 0 when nothing was found. processingStartedAt: type: integer format: int64 nullable: true queuedAtTimestamp: type: integer format: int64 nullable: true retryNotBefore: type: integer format: int64 nullable: true description: | Set while the record is QUEUED because its source rate-limited the content fetch: the earliest time (epoch milliseconds) indexing reads it again. `reason` then says when indexing resumes. Null or absent once the source serves the record or indexing ends; a value on a record that is no longer QUEUED is left over from an earlier wait and means nothing. parsingStatus: type: string nullable: true indexingStatus: type: string hasIndexedCopy: type: boolean description: | True after the record is indexed (every COMPLETED write, including the upgrade backfill for records indexed earlier), and false once it is marked EMPTY. A later re-queue or a failed re-index leaves it as it is, so search keeps returning the previous indexed copy until the new version is indexed. Absent until the first COMPLETED or EMPTY write sets it. extractionStatus: type: string reason: type: string nullable: true isDeleted: type: boolean isArchived: type: boolean deletedByUserId: type: string nullable: true restoredAtTimestamp: type: integer format: int64 nullable: true description: | When a restore last brought this knowledge-base file back from the trash and queued it to be indexed again (epoch milliseconds). Absent on records never restored, on folders, and on files restored while their indexing was turned off. isDirty: type: boolean nullable: true isVLMOcrProcessed: type: boolean nullable: true mimeType: type: string nullable: true sizeInBytes: type: integer format: int64 nullable: true md5Checksum: type: string nullable: true virtualRecordId: type: string nullable: true contentRev: type: string nullable: true description: | Revision of the record's content, 16 lowercase hex characters: the start of the SHA-256 of the content the indexer received for the record. If a record still has no revision stored when its vectors are written, a digest of its parsed blocks is stored instead. Identical content gives the same value, and a copy whose content matches another record carries that record's value. `stageStates` describes this revision. Set when indexing starts; absent on records not indexed since pipeline stages were introduced. example: 78f3f276290c9f81 summaryDocumentId: type: string nullable: true storageDocumentId: type: string nullable: true webUrl: type: string nullable: true previewRenderable: type: boolean nullable: true isShared: type: boolean nullable: true isDependentNode: type: boolean nullable: true parentNodeId: type: string nullable: true hideWeburl: type: boolean nullable: true isInternal: type: boolean nullable: true isPlaceholder: type: boolean nullable: true description: True for placeholder/stub records standing in for an out-of-scope ancestor (rendered read-only, no content actions; excluded from search and indexing). fileRecord: type: object nullable: true additionalProperties: false required: - id - orgId - name - extension - isFile properties: id: type: string orgId: type: string name: type: string extension: type: string etag: type: string nullable: true ctag: type: string nullable: true md5Checksum: type: string nullable: true quickXorHash: type: string nullable: true crc32Hash: type: string nullable: true sha1Hash: type: string nullable: true sha256Hash: type: string nullable: true mimeType: type: string nullable: true sizeInBytes: type: integer format: int64 nullable: true isFile: type: boolean webUrl: type: string nullable: true description: | Where the file opens in its source. Kept for a connected app with a limit too; it can contain the names of the folders above the file. path: type: string nullable: true description: | The file's path in its source, which names every folder above it. `null` for a connected app whose limit does not cover the file's whole app or collection. localFsRelativePath: type: string nullable: true description: The same path on local storage; `null` in the same cases as `path`. mailRecord: type: object nullable: true additionalProperties: false properties: {} ticketRecord: type: object nullable: true additionalProperties: false properties: {} stageStates: type: array description: | Per-stage pipeline status for the record's current content revision. Empty for records indexed before pipeline stages existed. The `classify` stage's status is mirrored on `record.extractionStatus`. items: $ref: '#/components/schemas/StageStateSummary' knowledgeBase: type: object nullable: true additionalProperties: false required: - id - name - orgId properties: id: type: string name: type: string orgId: type: string folder: type: object nullable: true additionalProperties: false required: - id - name properties: id: type: string name: type: string metadata: type: object additionalProperties: false required: - languages - topics - subcategories1 - subcategories2 - subcategories3 - departments - categories properties: languages: type: array items: type: object additionalProperties: false required: - id - name properties: id: type: string name: type: string topics: type: array items: type: object additionalProperties: false required: - id - name properties: id: type: string name: type: string subcategories1: type: array items: type: object additionalProperties: false required: - id - name properties: id: type: string name: type: string subcategories2: type: array items: type: object additionalProperties: false required: - id - name properties: id: type: string name: type: string subcategories3: type: array items: type: object additionalProperties: false required: - id - name properties: id: type: string name: type: string departments: type: array items: type: object additionalProperties: false required: - id - name properties: id: type: string name: type: string categories: type: array items: type: object additionalProperties: false required: - id - name properties: id: type: string name: type: string permissions: type: array items: type: object additionalProperties: false required: - id - name - type - relationship - accessType properties: id: type: string name: type: string type: type: string relationship: type: string enum: - OWNER - WRITER - READER accessType: type: string GetRecordContentResponseSchema: type: object additionalProperties: false description: | Response returned by GET /connectors/record/{recordId}/content. `content` is the record's full parsed content flattened into a single plain-text string: a metadata header (title, source, key fields, and a short summary), then the record's block/table text in reading order, and finally a foreign-key related-tables footer for `SQL_TABLE` records. For `TICKET` records (e.g. Jira) the metadata header is refreshed with live ticket fields (status, assignee, comments) at request time rather than the last-indexed snapshot. `content` may be empty for an indexed record with no extractable text (e.g. image-only). A record that exists and that the caller may read, but whose content is not indexed yet (`indexingStatus` other than `COMPLETED`), has no parsed text. `content` is then a description of the record instead: its metadata header (name, link, dates, type-specific fields) followed by a paragraph that starts `Not indexed:` and names the status, e.g. `(status: AUTO_INDEX_OFF)`. It is not the record's text. `content` is the literal `No record found` when the caller may read an indexed record but its stored content could not be loaded. An id that does not exist, a record in the trash, and a record the caller may not read all answer `403` instead. An unbudgeted read returns at most 140,000 characters of `content`. A longer record is cut there, the content ends with a marker line, and the response carries `truncated: true` and `totalChars`. required: - content properties: content: type: string description: The record's full parsed content as a single plain-text string; for a record whose content is not indexed yet, a description of the record and its indexing status instead (see above). In a budgeted read, the part of the record (or thread) that fit. An unbudgeted read returns at most 140,000 characters; a longer record is cut there and the content ends with a truncation marker (see `truncated`). truncated: type: boolean description: Present and true only on an unbudgeted read whose content was cut to fit 140,000 characters, marker included. `totalChars` is then the length of the whole content. totalChars: type: integer description: Present only with `truncated`. The length of the record's whole content, in characters. mode: type: string enum: - record - thread description: Budgeted reads only. Which kind of read this was. recordId: type: string description: Budgeted record reads only. The record read. order: type: string enum: - in_order - relevance - extract description: 'Budgeted record reads only. `relevance` when `query` chose which blocks to show; the blocks are then not contiguous. `extract` when `extract=true` was asked and the reader model answered: `content` is the passages it copied out (told to copy word for word; see `extract.unverifiedLines`), not a page of the record. An extract is one response, so `complete` is true and `nextStartBlock` null; whether the reader saw the whole record is `extract.sourceComplete`.' extract: type: object description: 'Reads with `extract=true` only. With `order: extract`, the reader model''s call. On a plain read returned because the reader could not answer, `error` alone.' properties: model: type: string nullable: true description: The model that read the record. provider: type: string nullable: true nothingRelevant: type: boolean description: True when the reader found nothing that answers `query`; `content` is then `NOTHING RELEVANT`. truncated: type: boolean description: True when the reader's reply was cut to fit `maxChars` (or 12,000 characters, whichever is smaller). unverifiedLines: type: integer description: 'Lines of `content` with words not found in the text the reader was given, as written: a paraphrase, or a misreading. Check them with a plain read. Every fragment with a letter or digit is checked, and a match may not run into a longer word or number (`$1,250` is not found in `$1,250,000`, nor `$1,250.` in `$1,250.99`). A note the reader adds in square brackets beside a quote (where a passage sits) is not checked; a markdown link''s label, and a line that is only a bracketed note, are. 0 means every quoted fragment was found, not that `content` is complete or that it quotes the right passage.' sourceChars: type: integer description: Characters of the record the reader was given. sourceComplete: type: boolean description: 'True when the reader was given the whole record. False when the record was longer than the reader''s 120,000 characters, or had more than 5,000 blocks, and it was given part of it: the blocks most relevant to `query`, or the start of the record when the relevance search found nothing or failed.' inputTokens: type: integer outputTokens: type: integer error: type: string description: Why there is no extract (the failure's type, or `EmptyReply`); `content` is then the plain read at `maxChars`. startBlock: type: integer description: Budgeted reads only. Where this read started (in a thread read, within the record at `startRecord`). totalBlocks: type: integer description: Budgeted record reads only. Blocks in the record. shownBlocks: type: array description: Budgeted record reads only. The block ranges shown, as `[first, last]` pairs. items: type: array items: type: integer complete: type: boolean description: Budgeted reads only. True when nothing of the record (or thread) is left to read after this one. A block larger than the largest `maxChars` (140000) is returned cut and given up; when nothing follows it, `complete` is true and `truncatedBy` is `clipped`. truncatedBy: type: string nullable: true description: 'Budgeted reads. Why the read stopped short: `chars`, `selection`, or `clipped` (a block larger than `maxChars` was cut; it takes precedence over the others). Null when the read lost nothing. `clipped` can come with `complete: true`: a block given up at the largest `maxChars` with nothing after it.' givenUpBlock: type: integer nullable: true description: 'Budgeted record reads only. A block larger than the largest `maxChars` (140000) that this read cut and gave up: no read can return the rest of it. Set in the read that gave it up, even when more of the record follows.' givenUpBlocks: type: array description: Thread reads only. Every block this read gave up at the largest `maxChars`, by record. items: type: object properties: recordId: type: string block: type: integer nextStartBlock: type: integer nullable: true description: 'Pass as `startBlock` to read on; null when nothing is left. In a thread read it is the block of the record at `nextStartRecord` (0 unless that record was cut short). A block larger than `maxChars` holds it on that block: send it again with a larger `maxChars` (up to 140000) to read the block whole.' threadRecordCount: type: integer description: Thread reads only. Records in the thread the caller may see. startRecord: type: integer description: Thread reads only. Where this read started. nextStartRecord: type: integer nullable: true description: 'Thread reads only. Pass as `startRecord`, with `nextStartBlock` as `startBlock`, to read on. It stays on a record that was cut short until that record has been read to its end. A block larger than `maxChars` holds the cursor on that block; send the same cursor with a larger `maxChars` to read it. Null when nothing is left to read, and `complete` is then true. A block given up at the largest `maxChars` is named in the text, and its record''s entry in `records` has `truncatedBy: clipped`.' records: type: array description: Thread reads only. The records shown, oldest first. items: type: object properties: recordId: type: string recordName: type: string nullable: true startBlock: type: integer complete: type: boolean truncatedBy: type: string nullable: true givenUpBlock: type: integer nullable: true nextStartBlock: type: integer nullable: true unreadableRecordIds: type: array description: Thread reads only. Records of the thread the caller may see whose content could not be loaded. items: type: string threads: type: array description: Thread reads of a Slack record in no single thread only (several channel messages in one record, or a message without replies). The threads its messages started whose replies follow it in `records`, in the order their first messages were posted; empty when none of its messages has replies the caller may see. items: type: object additionalProperties: false properties: threadRecordGroupId: type: string description: The thread's record group. recordCount: type: integer description: Replies of this thread the caller may see. threadsNotRead: type: integer description: Thread reads of a Slack record in no single thread only. Threads of its messages left out because one read covers at most 10. chars: type: integer description: Budgeted reads only. Characters in `content`. maxChars: type: integer description: Budgeted reads only. The budget this read used. KnowledgeGraphNodeRefSchema: type: object additionalProperties: false description: Minimal identity reference for a node in the knowledge graph. required: - id - name - node_type - sub_type - is_record properties: id: type: string description: Node identifier. Pass it back as `nodeId` to open this node. When `is_record` is true it is also the Record ID accepted by GET /connectors/record/{recordId}/content. name: type: string node_type: type: string description: One of `app`, `recordGroup`, `folder`, `record`. sub_type: type: string nullable: true description: e.g. `TICKET`, `CONFLUENCE_PAGE`, `PROJECT`, `COLLECTION`. is_record: type: boolean description: True for record and folder nodes — the nodes whose content can be read via GET /connectors/record/{recordId}/content. KnowledgeGraphNodeRowSchema: type: object additionalProperties: false description: | One row in a navigate listing. Carries the identity fields of KnowledgeGraphNodeRefSchema (inlined rather than composed, so this object can stay closed) plus display detail. required: - id - name - node_type - sub_type - is_record - has_children - detail properties: id: type: string name: type: string node_type: type: string description: 'One of `recordGroup`, `folder`, `record`, or `app`. A listing holds `app` nodes only at the root of a connected app whose limit names spaces, folders or files as well as whole sources: there it lists those sources.' sub_type: type: string nullable: true is_record: type: boolean has_children: type: boolean description: Whether this node can itself be opened with `nodeId`. detail: type: string nullable: true description: e.g. size, indexing status, or relationship type. web_url: type: string nullable: true description: Link to the record in its source system. Records that carry a relative URL (uploaded/KB records) are resolved against the configured frontend public URL (see POST /configurationManager/frontendPublicUrl). The value is null when that host is not configured. indexing_status: type: string nullable: true source_created_at: type: integer format: int64 nullable: true description: Creation time in the source system, epoch milliseconds. Null when the underlying node did not carry one. source_modified_at: type: integer format: int64 nullable: true level: type: integer description: 1-based nesting depth from the navigated parent. Always 1 for a `depth=1` listing; 2 or 3 for deeper rows when `depth` > 1. context_summary: type: string nullable: true KnowledgeGraphPaginationSchema: type: object additionalProperties: false description: Pagination envelope for a navigate listing. required: - page - limit - total - has_next - has_prev properties: page: type: integer limit: type: integer total: type: integer description: Total children of the current node, ignoring pagination. has_next: type: boolean has_prev: type: boolean NavigateKnowledgeGraphResponseSchema: type: object additionalProperties: false description: | Response returned by GET /connectors/navigate. Field names are snake_case, matching the knowledge graph's internal view models. `text` carries the same flat-text rendering PipesHub's own agent sees for this node; the remaining fields carry the same information in structured form. required: - current - breadcrumbs - rows - related - pagination - web_url - indexing_status - connector - text properties: current: allOf: - $ref: '#/components/schemas/KnowledgeGraphNodeRefSchema' nullable: true description: The node that was opened. Null at the root listing. breadcrumbs: type: array description: Root to parent, not including `current`. items: $ref: '#/components/schemas/KnowledgeGraphNodeRefSchema' rows: type: array description: The current node's children for this page. With `depth` > 1 this is a flat list of all descendants up to that depth, each row carrying its own `level`. items: $ref: '#/components/schemas/KnowledgeGraphNodeRowSchema' related: type: array description: Cross-referenced records (non-containment edges), e.g. the Confluence page linked from a Jira ticket. Page 1 only. items: $ref: '#/components/schemas/KnowledgeGraphNodeRowSchema' pagination: allOf: - $ref: '#/components/schemas/KnowledgeGraphPaginationSchema' nullable: true web_url: type: string nullable: true indexing_status: type: string nullable: true connector: type: string nullable: true context_block: type: string nullable: true description: Type-specific metadata for the current node when it is a record — for a ticket, status, assignee, priority and dates. text: type: string description: | Flat-text rendering of this view: breadcrumbs, the current node's metadata, the children listing with an id per row, `Related:`, and a closing `Next:` line naming a follow-up call. Shape depends on the page: pages after the first omit the header, breadcrumbs and related rows to stay compact, while the structured fields above remain complete. Capped at 25,000 bytes, with a truncation marker appended when exceeded. KnowledgeGraphLookupMatchSchema: type: object additionalProperties: false description: A single record resolved from an identifier. required: - id - name - record_type - connector_name - web_url - indexing_status - identifier_used properties: id: type: string description: The Record ID. Pass it to GET /connectors/record/{recordId}/content to read the record, or as `nodeId` to GET /connectors/navigate to list what is under it. name: type: string record_type: type: string nullable: true connector_name: type: string nullable: true web_url: type: string nullable: true indexing_status: type: string nullable: true identifier_used: type: string description: Which of the supplied identifiers produced this match. external_id: type: string nullable: true description: e.g. `PA-1787`, a Confluence page id, a Drive file id. context_block: type: string nullable: true description: Type-specific metadata for the record — for a ticket, status, assignee, priority and dates. LookupRecordResponseSchema: type: object additionalProperties: false description: | Response returned by GET /connectors/record/lookup. Field names are snake_case, matching the knowledge graph's internal view models. A miss is a 200 with an empty `matches` and the input echoed in `not_found_identifiers` — see the endpoint description. required: - matches - ambiguous - not_found_identifiers - text properties: matches: type: array items: $ref: '#/components/schemas/KnowledgeGraphLookupMatchSchema' ambiguous: type: boolean description: True when one identifier resolved to more than one accessible record. Present the choice rather than taking the first, or retry with `connectorName`. not_found_identifiers: type: array description: Identifiers that resolved to nothing the caller can access. items: type: string searched_connectors: type: object description: Per-identifier list of the connector types that were searched, so a miss can be retried against a different `connectorName`. Keys are the identifiers exactly as supplied. additionalProperties: type: array items: type: string text: type: string description: Flat-text rendering of the matches — each match's metadata block followed by a `Next:` line naming a follow-up call — and a line per miss naming the connectors that were searched. Capped at 25,000 bytes. DeleteRecordResponseSchema: type: object additionalProperties: false description: Response returned by DELETE /knowledgeBase/record/{recordId}. required: - success - message - recordId properties: success: type: boolean enum: - true message: type: string recordId: type: string connector: type: string nullable: true timestamp: type: integer format: int64 nullable: true softDeleted: type: boolean description: True when the record went to the trash instead of being removed batchId: type: string nullable: true description: The delete batch; restoring any record in it restores all of it vectorCleanupPending: type: boolean description: The record is deleted, but removing its embeddings is still owed vectorCleanupFailedRecordIds: type: array items: type: string vectorCleanupFailedVirtualRecordIds: type: array items: type: string RestoredRecordSchema: type: object additionalProperties: false required: - recordId properties: recordId: type: string name: type: string nullable: true description: The name it has now renamedFrom: type: string description: Its name before restore, when another item next to it had taken that name RestoreRecordResponseSchema: type: object additionalProperties: false description: Response returned by POST /knowledgeBase/record/{recordId}/restore. required: - success - message - restoredRecords properties: success: type: boolean enum: - true message: type: string batchId: type: string nullable: true description: The delete batch that was restored restoredRecords: type: array items: $ref: '#/components/schemas/RestoredRecordSchema' reindexPending: type: boolean description: Some restored files could not be queued for indexing reindexPendingRecordIds: type: array items: type: string description: Files to reindex by hand to make them searchable again reindexPendingReason: type: string renamePendingRecordIds: type: array items: type: string description: Items restored under a name another item next to them has; rename them renamePendingReason: type: string TrashItemSchema: type: object additionalProperties: false description: What one delete removed (a folder with its contents, or every item of a multi-select); restoring it brings back all of it. required: - id - isFolder - parentInTrash - itemCount - deletedAtTimestamp properties: id: type: string description: Record ID to restore name: type: string nullable: true recordType: type: string nullable: true isFolder: type: boolean description: A folder, deleted with everything in it mimeType: type: string nullable: true sizeInBytes: type: integer format: int64 nullable: true parentId: type: string nullable: true description: The folder it was in; null at the collection's top level parentName: type: string nullable: true parentInTrash: type: boolean description: That folder is in the trash too; restore it first itemCount: type: integer minimum: 0 description: Records this delete removed, all of which a restore brings back rootCount: type: integer minimum: 1 description: Items selected in the delete; more than one for a multi-select delete, which is one item here otherRootNames: type: array items: type: string description: Names of up to three of the other items selected with this one deletedAtTimestamp: type: integer format: int64 deletedBy: type: object nullable: true additionalProperties: false properties: name: type: string nullable: true email: type: string nullable: true removableAfterTimestamp: type: integer format: int64 nullable: true description: From when the scheduled cleanup may remove it for good; null when unknown ListTrashResponseSchema: type: object additionalProperties: false description: Response returned by GET /knowledgeBase/{kbId}/trash. required: - success - items - pagination properties: success: type: boolean enum: - true items: type: array items: $ref: '#/components/schemas/TrashItemSchema' pagination: type: object additionalProperties: false required: - page - limit - totalCount - totalPages properties: page: type: integer limit: type: integer totalCount: type: integer totalPages: type: integer retention: type: object nullable: true additionalProperties: false required: - minAgeMs properties: minAgeMs: type: integer format: int64 description: Items stay at least this long before the cleanup may remove them RestoreRecordsRequestBody: type: object description: Request body for POST /knowledgeBase/records/restore. Fields other than `recordIds` are dropped by the validator, not refused. required: - recordIds properties: recordIds: type: array minItems: 1 maxItems: 100 description: Record ids to restore. An id listed more than once is restored, and answered, once. items: type: string minLength: 1 pattern: \S description: Whitespace around an id is removed first, so an id made only of whitespace is refused as empty. RestoreRecordsResponseSchema: type: object additionalProperties: false description: Response returned by POST /knowledgeBase/records/restore. required: - success - restoredCount - failedCount - results properties: success: type: boolean description: True when every requested record was restored restoredCount: type: integer description: Records restored, counting those restored along with them failedCount: type: integer results: type: array description: | One entry per requested id: `recordId`, `success`, `code`, and either the restore response fields or a `reason` that says what to do next. items: $ref: '#/components/schemas/RestoreRecordsResultItemSchema' RestoreRecordsResultItemSchema: type: object additionalProperties: false description: | The outcome for one id of POST /knowledgeBase/records/restore. A restored item carries the fields of RestoreRecordResponseSchema; a refused one carries `reason`, and, when the refusal names other records, `blockedRecordId` plus the snake_case fields below (`external_id`, `conflicting_record_id`, `conflicting_record_name`). required: - recordId - success properties: recordId: type: string success: type: boolean code: type: integer description: The status the single restore would have answered (200, 403, 404, 409) reason: type: string description: Why it was not restored, and what to do next message: type: string description: Present on success batchId: type: string nullable: true description: The delete batch that was restored; `null` when the record was not in the trash restoredRecords: type: array description: Present on success; empty when the record was not in the trash items: $ref: '#/components/schemas/RestoredRecordSchema' reindexPending: type: boolean description: Some restored files could not be queued for indexing reindexPendingRecordIds: type: array items: type: string description: Files to reindex by hand to make them searchable again reindexPendingReason: type: string renamePendingRecordIds: type: array items: type: string description: Items restored under a name another item next to them has; rename them renamePendingReason: type: string blockedRecordId: type: string description: The item a refusal is about external_id: type: string description: The source item two of the restored items came from conflicting_record_id: type: string description: The live item that holds this item's place conflicting_record_name: type: string nullable: true missing: type: array items: type: string description: Items of the batch that were no longer in the trash parentId: type: string nullable: true description: The folder still in the trash that must be restored first reIndexRecordResponseSchema: type: object additionalProperties: false description: Response returned by POST /knowledgeBase/reindex/record/{recordId}. required: - success - message - eventPublished - depth properties: success: type: boolean enum: - true message: type: string recordId: type: string nullable: true recordName: type: string nullable: true connector: type: string nullable: true eventPublished: type: boolean userRole: type: string nullable: true depth: type: integer ReIndexRecordGroupResponseSchema: type: object additionalProperties: false description: Response returned by POST /knowledgeBase/reindex/record-group/{recordGroupId}. required: - success - message - recordGroupId - depth - connectorId - connector - eventPublished properties: success: type: boolean enum: - true message: type: string recordGroupId: type: string depth: type: integer connectorId: type: string description: Id of the connector instance the record group belongs to connector: type: string nullable: true eventPublished: type: boolean UploadLimitsResponseSchema: type: object additionalProperties: false description: Upload constraints returned by GET /knowledgeBase/limits. required: - maxFilesPerRequest - maxFileSizeBytes properties: maxFilesPerRequest: type: integer minimum: 1 example: 1000 description: Maximum number of files per upload request maxFileSizeBytes: type: integer minimum: 1 example: 31457280 description: Maximum file size in bytes (default 30MB when platform settings unavailable) UploadStreamSSEEvent: type: object description: | Server-Sent Event envelope for the KB streaming upload endpoint (`POST /knowledgeBase/{kbId}/upload`, with optional `folderId` query param). These endpoints respond with `Content-Type: text/event-stream`: the upload and its per-file progress are a single request. The body streams one terminal event per file, then a final `done` summary, then closes. `data` is a JSON-encoded string whose decoded shape depends on `event`: - `file:succeeded` — see `UploadSucceededFileDetail`. The file was uploaded and its record created; content indexing then continues asynchronously. - `file:failed` — see `UploadFailedFileDetail`. Covers files rejected up front (oversize / unsupported type — these carry `reason`), files that failed the storage upload (`stage: "upload"`), and files the indexing service could not create (`stage: "index"`). - `done` — see `UploadDoneSummary`. Final event; the stream closes after it. - `error` — `{ "message": string }`. Emitted only on a catastrophic mid-stream failure (after the 200 headers were already sent), then the stream closes. Clients MUST treat any file without a terminal `file:succeeded` / `file:failed` as failed when this is received. The stream also emits SSE comment heartbeats (`: keepalive`) roughly every 1s during slow work; these carry no `event`/`data` and should be ignored. Authentication, permission, and request-shape failures occur BEFORE the stream starts and are returned as ordinary 4xx JSON errors (not stream events). properties: event: type: string enum: - file:succeeded - file:failed - done - error data: type: string description: JSON-encoded event payload. Shape depends on `event`. IndexingStatusFilter: type: string description: | Indexing status used to filter which records are included in a scoped reindex (record or record-group). Omit `statusFilters` to reindex all descendants regardless of status. The statuses records carry are `NOT_STARTED`, `QUEUED`, `IN_PROGRESS`, `COMPLETED`, `FAILED`, `FILE_TYPE_NOT_SUPPORTED`, `AUTO_INDEX_OFF`, `EMPTY` and `PASSWORD_PROTECTED`. Any other string is accepted too (no validator checks the value) and simply matches no record. ReindexRecordRequestBody: type: object description: | Optional body for single-record reindex. Only `depth` and `statusFilters` are read; any other field is dropped by the validator before the handler runs (there is no `force` option: the handler always sends `force: false`). No body, an empty object or an empty `statusFilters` list mean depth `0` and no status filter. properties: depth: type: integer minimum: -1 maximum: 100 default: 0 description: | Child traversal depth (`0` = record only; higher values include descendants; `-1` = all of them; `100` is used by clients for folder-like reindex). statusFilters: type: array items: $ref: '#/components/schemas/IndexingStatusFilter' description: | When set, only records whose indexing status matches one of these values are reindexed (applies to the record and its descendants per `depth`). ReindexRecordGroupRequestBody: type: object description: | Optional body for record-group reindex. Only `depth` and `statusFilters` are read; any other field is dropped by the validator before the handler runs (there is no `force` option: the handler always sends `force: false`). properties: depth: type: integer minimum: -1 maximum: 100 default: 0 description: | Depth of records under the record group to include. `-1` (everything) is applied, and answered, as `100`. statusFilters: type: array items: $ref: '#/components/schemas/IndexingStatusFilter' description: | When set, only records matching these indexing statuses are reindexed. ResyncConnectorRequestBody: type: object required: - connectorName properties: connectorName: type: string minLength: 1 description: | The connector's type as the registry lists it, for example `RSS` or `Jira`. It names the sync event; the connector synced is always the one in the path. example: RSS fullSync: type: boolean description: When true, triggers a full sync instead of incremental. default: false ConnectorStats: type: object description: Statistics for a connector's records required: - success - data properties: success: type: boolean data: type: object required: - orgId - connectorId - origin - stats - byRecordType properties: orgId: type: string connectorId: type: string origin: type: string description: Where the records come from; `CONNECTOR` for a connector. example: CONNECTOR enum: - CONNECTOR - COLLECTION stats: type: object required: - total - indexingStatus properties: total: type: integer description: Records synced from the connector indexingStatus: $ref: '#/components/schemas/ConnectorIndexingStatusCounts' extractionStatus: type: object description: Record counts by classification (`classify` stage) status. additionalProperties: type: integer entitiesStatus: type: object description: | Record counts by named-entity extraction (`entities` stage) status, read from each record's `entities` stage state on its current revision. A record with no such state is not counted. additionalProperties: type: integer byRecordType: type: array description: The same counts per record type; empty before the first sync. items: type: object required: - recordType - total - indexingStatus properties: recordType: type: string example: FILE total: type: integer indexingStatus: $ref: '#/components/schemas/ConnectorIndexingStatusCounts' extractionStatus: type: object description: Record counts by classification (`classify` stage) status. additionalProperties: type: integer entitiesStatus: type: object description: Record counts by named-entity extraction (`entities` stage) status. additionalProperties: type: integer ConnectorIndexingStatusCounts: type: object description: Number of records in each indexing status. additionalProperties: type: integer properties: NOT_STARTED: type: integer PAUSED: type: integer IN_PROGRESS: type: integer COMPLETED: type: integer FAILED: type: integer FILE_TYPE_NOT_SUPPORTED: type: integer AUTO_INDEX_OFF: type: integer EMPTY: type: integer ENABLE_MULTIMODAL_MODELS: type: integer QUEUED: type: integer SKIPPED: type: integer PASSWORD_PROTECTED: type: integer DateRangeFilter: type: object description: Date range filter with optional inclusive bounds (epoch ms). properties: gte: type: integer nullable: true description: Greater-than-or-equal bound (epoch ms). lte: type: integer nullable: true description: Less-than-or-equal bound (epoch ms). SizeRangeFilter: type: object description: Size range filter with optional inclusive bounds (bytes). properties: gte: type: integer nullable: true description: Greater-than-or-equal bound (bytes). lte: type: integer nullable: true description: Less-than-or-equal bound (bytes). FilterOption: type: object description: A single filter option for knowledge hub filters. required: - id - label properties: id: type: string description: Filter ID value to send in requests. label: type: string description: Display label for the filter. type: type: string nullable: true description: Additional type information (currently unused, may be null). connectorType: type: string nullable: true description: Connector type/name. Set only for entries in the `connectors` list. KnowledgeHubNode: type: object description: | One element of `items`. The live API keeps keys stable and sets inapplicable values to JSON `null` (not omitted). required: - id - name - nodeType - parentId - origin - connector - connectorId - recordType - recordGroupType - indexingStatus - reason - createdAt - updatedAt - sizeInBytes - mimeType - extension - webUrl - hasChildren - previewRenderable - permission - sharingStatus - isInternal - isPlaceholder properties: id: type: string description: Unique identifier for the node. name: type: string description: Display name of the node. nodeType: type: string enum: - app - recordGroup - folder - record description: Type of the node (app, recordGroup, folder, or record). parentId: type: string nullable: true description: | Parent node ID, or `null` at the root browse level. Also `null` for a connected app whose limit leaves the parent out: the node is listed, and the folder above it is not named. parent: type: object nullable: true description: | The parent this node is listed under, with what a client needs to open it and name it; `null` exactly when `parentId` is. It is the node's placement parent: an ancestor the user cannot open is replaced, never named. required: - id - nodeType properties: id: type: string description: Parent node ID. nodeType: type: string enum: - app - recordGroup - folder - record description: Parent node type; opened as `/nodes/{nodeType}/{id}`. name: type: string nullable: true description: Parent display name. origin: type: string enum: - COLLECTION - CONNECTOR description: Origin type. connector: type: string nullable: true description: Connector display name / key when applicable; otherwise `null`. connectorId: type: string nullable: true description: Connector instance id for records and groups that come from a connector; otherwise `null` (Collections, and app nodes). recordType: type: string nullable: true description: Record type when `nodeType` is `record`; otherwise `null`. recordGroupType: type: string nullable: true description: Record group type when `nodeType` is `recordGroup`; otherwise `null`. indexingStatus: type: string nullable: true description: Indexing status when `nodeType` is `record`; otherwise `null`. reason: type: string nullable: true description: Failure or status reason when set; otherwise `null`. isInternal: type: boolean description: True for internal/system nodes that do not originate from a source. isPlaceholder: type: boolean description: True for placeholder/stub nodes standing in for an out-of-scope ancestor (rendered read-only, no content actions; excluded from search and indexing). createdAt: type: integer description: Creation timestamp (epoch ms). updatedAt: type: integer description: Update timestamp (epoch ms). sizeInBytes: type: integer nullable: true description: File size in bytes for file records; otherwise `null`. mimeType: type: string nullable: true extension: type: string nullable: true webUrl: type: string nullable: true hasChildren: type: boolean description: Whether the node has children (sidebar / tree). previewRenderable: type: boolean nullable: true permission: type: object nullable: true description: Per-item permission when `include=permissions` is requested; otherwise `null`. required: - role - canEdit - canDelete properties: role: type: string canEdit: type: boolean canDelete: type: boolean sharingStatus: type: string nullable: true description: | Sharing status (e.g. `private`, `shared`, `team`, `workspace`) when applicable; otherwise `null`. KnowledgeHubNodesResponse: type: object description: | Response body for the Knowledge Hub nodes API. The deployed service serialises optional values as JSON `null` and always includes the keys listed in `required` (Swagger / clients will see stable shapes, not omitted properties). required: - success - error - id - currentNode - parentNode - items - pagination - filters - breadcrumbs - counts - permissions - maxFolderDepth properties: maxFolderDepth: type: integer description: Deepest a collection folder may be nested; a folder directly in the collection is depth 1. success: type: boolean enum: - true description: Always `true` on HTTP 200. Failures use 4xx/5xx error envelopes, not this body shape. error: type: string nullable: true description: Always `null` on HTTP 200. errorCode: type: integer nullable: true description: | Status the connector service turns a failed lookup into. Always `null` on HTTP 200: a failure is answered with that status and the error envelope instead of this body. typed_records: type: object nullable: true description: | Full record documents keyed by record ID. Filled only for in-process callers (the agent's knowledge navigator); always `null` over HTTP. additionalProperties: $ref: '#/components/schemas/SemanticSearchGraphRecord' id: type: string nullable: true description: Current parent node ID when browsing children; `null` at root. currentNode: type: object nullable: true description: Node being browsed when `parentId` is in the path; `null` at root. required: - id - name - nodeType properties: id: type: string name: type: string nodeType: type: string description: One of `app`, `recordGroup`, `folder`, `record`. subType: type: string nullable: true description: Connector name or record type when applicable; otherwise `null`. parentNode: type: object nullable: true description: | Parent of `currentNode` when present; `null` when not applicable, and for a connected app whose limit leaves that parent out. For such an app, `breadcrumbs` likewise names only the nodes inside its limit. required: - id - name - nodeType properties: id: type: string name: type: string nodeType: type: string description: One of `app`, `recordGroup`, `folder`, `record`. subType: type: string nullable: true items: type: array description: Page of nodes for the current browse or search. items: $ref: '#/components/schemas/KnowledgeHubNode' pagination: type: object required: - page - limit - totalItems - totalPages - hasNext - hasPrev properties: page: type: integer nullable: true description: Current page (1-indexed). `null` when the request pages by `cursor`. limit: type: integer description: Page size. totalItems: type: integer description: | Items in the whole result. For a search by a connected app limited to spaces, folders or files, the matches found so far: it can grow from page to page and is final once `hasNext` is false. totalPages: type: integer nullable: true description: Number of pages. `null` when the request pages by `cursor`. hasNext: type: boolean hasPrev: type: boolean nextCursor: type: string nullable: true description: Cursor for the next page; null on the last page. prevCursor: type: string nullable: true description: Cursor for the previous page; null on the first page. startIndex: type: integer nullable: true description: 1-based index of the first item on this page, when known. endIndex: type: integer nullable: true description: 1-based index of the last item on this page, when known. currentPageItems: type: integer nullable: true description: Number of items on this page. filters: type: object required: - applied - available properties: applied: type: object description: Echo of applied filters; unused slots are JSON `null`. required: - q - nodeTypes - recordTypes - origins - connectorIds - indexingStatus - createdAt - updatedAt - size - sortBy - sortOrder properties: q: type: string nullable: true nodeTypes: type: array nullable: true items: type: string recordTypes: type: array nullable: true items: type: string origins: type: array nullable: true items: type: string connectorIds: type: array nullable: true items: type: string indexingStatus: type: array nullable: true items: type: string createdAt: nullable: true allOf: - $ref: '#/components/schemas/DateRangeFilter' updatedAt: nullable: true allOf: - $ref: '#/components/schemas/DateRangeFilter' size: nullable: true allOf: - $ref: '#/components/schemas/SizeRangeFilter' sortBy: type: string description: Effective sort field after server normalisation. sortOrder: type: string description: Effective sort order after server normalisation. available: type: object nullable: true description: Populated when `include=availableFilters`; otherwise `null`. required: - nodeTypes - recordTypes - origins - connectors - indexingStatus - sortBy - sortOrder properties: nodeTypes: type: array items: $ref: '#/components/schemas/FilterOption' recordTypes: type: array items: $ref: '#/components/schemas/FilterOption' origins: type: array items: $ref: '#/components/schemas/FilterOption' connectors: type: array description: | The apps and collections to filter by, each with its own name. For a connected app with a limit, only those the limit covers whole: an app or collection it covers only in part (some of its spaces, folders or files) is not offered. The other option lists are fixed and name no source. items: $ref: '#/components/schemas/FilterOption' indexingStatus: type: array items: $ref: '#/components/schemas/FilterOption' sortBy: type: array items: $ref: '#/components/schemas/FilterOption' sortOrder: type: array items: $ref: '#/components/schemas/FilterOption' breadcrumbs: type: array nullable: true description: Present when `include=breadcrumbs`; otherwise `null`. items: type: object required: - id - name - nodeType properties: id: type: string name: type: string nodeType: type: string description: One of `app`, `recordGroup`, `folder`, `record`. subType: type: string nullable: true counts: type: object nullable: true description: Present when `include=counts`; otherwise `null`. required: - items - total properties: items: type: array items: type: object required: - label - count properties: label: type: string count: type: integer total: type: integer permissions: type: object nullable: true description: Present when `include=permissions`; otherwise `null`. required: - role - canUpload - canCreateFolders - canEdit - canDelete - canManagePermissions properties: role: type: string canUpload: type: boolean canCreateFolders: type: boolean canEdit: type: boolean canDelete: type: boolean canManagePermissions: type: boolean collectionRole: type: string nullable: true description: | The caller's own role on the collection (knowledge base) this node is in, as restore and `GET /knowledgeBase/{kbId}/trash` check it: OWNER, WRITER, FILEORGANIZER, COMMENTER or READER. Unlike `role`, which ranks record permissions inside a folder, it is the same at every level of the collection. Null outside a collection. AgentCapabilities: type: object additionalProperties: false description: | Per-request agent capability toggles. Only meaningful when `chatMode` selects an agent mode; ignored otherwise. Each field falls back to its own `default` below when omitted — a missing flag is not uniformly `true`. Omitting the whole object applies every default. properties: internalSearch: type: boolean default: true description: Whether the agent may search internal knowledge bases for this turn. webSearch: type: boolean default: true description: Whether the agent may perform web search for this turn. deepSearch: type: boolean default: false description: Whether the agent may use deeper, higher-latency retrieval for this turn. Filters: type: object additionalProperties: false description: | What retrieval is narrowed to for a turn: whole apps, and selections below app level (record groups, folders, records). The keys are a union. For **org assistant** chat streams, send explicit lists. For **agent** chat streams, send explicit id lists, or **omit** `filters` (and `tools`) to let the service use the agent’s stored knowledge and tool configuration. Sending `{ "apps": [], "kb": [] }` on an agent stream means **no** knowledge sources for that turn (it is not “full org default”). A selection below app level is the whole scope of the turn: with `recordGroups`, `records` or `recordsExact` present, an absent or empty `apps` / `kb` means none of them. A selected node that does not exist or that the caller cannot read contributes nothing; when nothing valid remains the turn searches nothing. `/search` answers 404 for that; a chat turn is not failed, its search simply finds nothing. At most 200 ids may be sent in one request, counted across all keys, and every id must be a valid UUID. A key this schema does not list is refused with 400; the message names the key and lists the allowed ones. A value of the wrong type is refused with 400 too. A selection that covers more than the configured number of nodes (50,000 by default), or that touches a source still being prepared for folder-level search, is refused: `/search` and the non-streaming chat routes answer 422 with the message, and a chat stream ends with an error frame (`RUN_ERROR`, or `error` on the legacy protocol) whose `code` is `selection_too_large` or `selection_not_ready` and whose `message` says what to change. No model call is made for such a turn. properties: apps: type: array items: type: string pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ description: | Whole apps: connector instance ids and collection (knowledge base) ids. Each element must be a valid UUID. kb: type: array items: type: string pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ description: | Older name for collection ids; treated the same as `apps`. Each element must be a valid UUID. recordGroups: type: array items: type: string pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ description: | Record group ids (a drive, a space, a project). Each selects the group, the groups nested in it and every record that belongs to them. Each element must be a valid UUID. records: type: array items: type: string pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ description: | Folder or record ids. Each selects the node and everything under it (child items and attachments). Each element must be a valid UUID. recordsExact: type: array items: type: string pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ description: | Record ids selected alone, without what is under them. Each element must be a valid UUID. entityFilters: type: object additionalProperties: false x-pipeshub-stability: experimental description: | **Experimental** named-entity constraints (Labs flag `ENABLE_NAMED_ENTITY_EXTRACTION`, or `HIGH_ACCURACY_MODE`). Every constraint must hold for the same record: an amount and a date match a record that mentions both, not one entity that is both. `name`/`entityIds` match entity nodes; `mentionedDate`, `amount`, `quantity` and `percent` match typed values the record mentions (any date mentioned, not a role such as "signed on"). A kind of a typed constraint's own kind narrows it (`currency` with `amount`); other kinds are a constraint of their own. Only records the caller may read are considered: every empty result reads the same. An unusable filter (unknown kind, bad currency, a range whose low end is above its high end, or a blank name) fails request validation with 400, including on conversation requests. On `POST /search`, a valid filter matching too many readable records is refused with 422. Conversation operations apply a valid filter to every search behind the answer; if retrieval refuses it, the request does not fail and the answer says why the filter could not be applied. properties: kinds: type: array minItems: 1 maxItems: 23 description: Lowercase, as listed. The server trims and lowercases each kind before checking it. items: title: NamedEntityKind type: string enum: - person - person_type - organization - event - product - skill - location - city - state - country_region - address - date - date_range - date_time - duration - currency - percentage - age - dimension - email - url - phone - ip name: type: string minLength: 1 maxLength: 256 description: Case-insensitive prefix of an entity name; surrounding blanks are trimmed and a blank name is refused. entityIds: type: array minItems: 1 maxItems: 100 items: type: string format: uuid description: Entity node ids, as returned by entity search. Typed values have no id. mentionedDate: type: object additionalProperties: false description: Overlap with a date the record mentions; half-open, `from` must not be after `to`. properties: from: oneOf: - type: integer description: Epoch milliseconds. - type: string description: ISO-8601 date or date-time; a date alone is UTC midnight. to: oneOf: - type: integer description: Epoch milliseconds. - type: string description: ISO-8601 date or date-time; a date alone is UTC midnight. amount: type: object additionalProperties: false description: '`min` must not be after `max`.' properties: min: type: number max: type: number currency: type: string pattern: ^[A-Za-z]{3}$ description: ISO 4217 code, case-insensitive. quantity: type: object additionalProperties: false description: In SI units of the dimension. `min` must not be after `max`. properties: min: type: number max: type: number dimension: type: string pattern: ^[A-Za-z_]{1,32}$ description: For example `length`, `weight`, `speed`, `temperature`, `age`. percent: type: object additionalProperties: false description: Fractions (0.125 is 12.5%). `min` must not be after `max`. properties: min: type: number max: type: number where: description: Reserved for the typed filter tree that will replace `entityFilters`; any value is refused with 400. not: {} x-speakeasy-ignore: true AppliedFilterNode: type: object additionalProperties: false description: | A single filter node selected by the user (used for display/persistence of active filters). The gateway refuses a node that lacks any of the four fields. required: - id - name - nodeType - connector properties: id: type: string description: Unique identifier of the filter node name: type: string description: Display name of the filter node nodeType: type: string description: Type of the node (e.g. app, recordGroup, folder, record) connector: type: string description: Connector identifier associated with this node extension: type: string description: File extension of a record node, for its icon mimeType: type: string description: MIME type of a record node, for its icon mentioned: type: boolean description: True when the node was picked with an @ mention in the message AppliedFilters: type: object additionalProperties: false description: | Rich filter state selected by the user, used for display and persistence only. This mirrors the active selection shown in the UI and is distinct from the machine-readable `filters` field used for retrieval scoping. properties: apps: type: array items: anyOf: - $ref: '#/components/schemas/AppliedFilterNode' - $ref: '#/components/schemas/RedactedSource' description: Applied app/connector filter nodes kb: type: array items: anyOf: - $ref: '#/components/schemas/AppliedFilterNode' - $ref: '#/components/schemas/RedactedSource' description: Applied knowledge-base filter nodes recordGroups: type: array items: anyOf: - $ref: '#/components/schemas/AppliedFilterNode' - $ref: '#/components/schemas/RedactedSource' description: Applied record-group filter nodes records: type: array items: anyOf: - $ref: '#/components/schemas/AppliedFilterNode' - $ref: '#/components/schemas/RedactedSource' description: Applied folder and record filter nodes (each with everything under it) recordsExact: type: array items: anyOf: - $ref: '#/components/schemas/AppliedFilterNode' - $ref: '#/components/schemas/RedactedSource' description: Applied record filter nodes selected alone ChatAttachmentRef: type: object additionalProperties: false description: | Reference to an attachment produced by `POST /conversations/attachments/upload` (or the equivalent agent route). Include in create/stream/message bodies so the turn is sent with uploaded files. required: - recordId properties: recordId: type: string minLength: 1 description: Attachment record id returned from the upload endpoint. recordName: type: string minLength: 1 description: Original display name of the file when known. mimeType: type: string minLength: 1 description: MIME type of the uploaded file. extension: type: string minLength: 1 description: File extension (e.g. `pdf`). virtualRecordId: type: string minLength: 1 description: Optional synthetic record id used by the graph layer. source: type: string enum: - upload - paste-text description: | How the attachment was produced: a file upload, or text pasted into the composer and sent as a file. Stored on the saved message as sent. ChatAttachmentUploadRef: type: object additionalProperties: false description: | Concrete attachment metadata returned by `POST /conversations/attachments/upload` (or the equivalent agent route). required: - recordId - recordName - mimeType - extension - virtualRecordId properties: recordId: type: string minLength: 1 description: Server-assigned attachment record id. recordName: type: string minLength: 1 description: Original filename stored for the attachment. mimeType: type: string minLength: 1 description: | MIME type of the stored file. For an image whose bytes are a JPEG or a PNG it is `image/jpeg` or `image/png` as the bytes say, whatever type was declared; other files keep the declared type. Files, images included, are stored as sent. extension: type: string minLength: 1 description: File extension derived by the backend. virtualRecordId: type: string minLength: 1 description: Synthetic record id used by the graph layer. parseMode: type: string minLength: 1 description: Backend-reported parsing mode used for the attachment (e.g. `pdfplumber`, `docling`, `csv_lightweight`). ocrMode: type: string minLength: 1 deprecated: true description: Deprecated alias for `parseMode`, kept for backward compatibility. Use `parseMode` instead. ChatAttachmentUploadResponse: type: object additionalProperties: false description: | Success envelope returned by `POST /conversations/attachments/upload` and `POST /agents/{agentKey}/conversations/attachments/upload`. required: - conversationId - attachments properties: conversationId: type: string nullable: true description: | Existing conversation id echoed from the request when the upload is tied to a thread; otherwise `null`. attachments: type: array minItems: 1 items: $ref: '#/components/schemas/ChatAttachmentUploadRef' CreateConversationRequest: type: object description: | Request body for creating a new AI conversation. **Query Processing:** The query is processed through PipesHub's AI pipeline which: - Performs semantic search across indexed knowledge bases - Retrieves relevant context from matching documents - Generates a response with citations to source materials - Suggests follow-up questions based on the conversation **History:** a new conversation starts with no history, and history is never accepted from the caller. A body carrying `previousConversations` with any value but an empty list (`null`, a string or an object included) is rejected with 400; an empty list is dropped. Send follow-up questions to `POST /conversations/{conversationId}/messages`; the server replays the stored conversation. Fields not listed here are dropped by the gateway's validator, not refused. required: - query properties: query: type: string minLength: 1 maxLength: 100000 description: | The user's question or prompt to start the conversation. Supports natural language queries of any complexity. example: What are the key findings from our Q4 financial report? recordIds: type: array items: type: string format: objectId pattern: ^[0-9a-fA-F]{24}$ description: | Limit the AI's knowledge scope to specific records/documents. When provided, only these records will be searched for context. example: - 507f1f77bcf86cd799439011 - 507f1f77bcf86cd799439012 filters: $ref: '#/components/schemas/Filters' appliedFilters: $ref: '#/components/schemas/AppliedFilters' attachments: type: array items: $ref: '#/components/schemas/ChatAttachmentRef' description: | Uploaded chat attachments to associate with this conversation turn (see `POST /conversations/attachments/upload`). projectId: type: string format: objectId pattern: ^[0-9a-fA-F]{24}$ description: | Link the new conversation to a project the caller has at least viewer access to. When the project's instructions, knowledge scope, or files are set and this request didn't supply its own `filters`/`attachments`, they are merged in as a fallback (the request always wins). Ignored on follow-up turns — only meaningful when creating a conversation. An OAuth token or PAT also needs `project:read`. An archived project answers `409`; a project the caller cannot see, `404`, before any stream opens. projectVisibility: type: string enum: - private - project description: | Only meaningful together with `projectId`. Overrides the project's default sharing behavior for this one conversation: `private` keeps it visible to the owner only; `project` exposes it to every project member. Defaults from the project's `chatSharing` setting when omitted. modelKey: type: string minLength: 1 description: | Identifier for the AI model configuration to use. Available models depend on organization settings. When neither `modelKey` nor `modelName` is sent, the organization's default model answers and is saved in the conversation's `modelInfo`. A key that matches no configured model is not refused: the default model answers, and the unknown key is still saved in `modelInfo`. example: gpt-5.6-luna modelName: type: string minLength: 1 description: Display name of the AI model example: gpt-5.6-luna modelFriendlyName: type: string minLength: 1 description: Friendly display name of the selected model example: gpt-5.6-Luna chatMode: type: string enum: - agent - internal_search - web_search - deep - quick - verification - auto description: | Optional execution mode for non-stream consumers of this shared request schema. `deep`, `quick`, `verification` and `auto` are legacy values: still accepted, they run as `internal_search`. The streaming routes accept only `agent`, `internal_search` and `web_search`. `agent:` is refused. `agent` uses the universal agent loop, while `internal_search` and `web_search` use their corresponding assistant search paths. example: internal_search timezone: type: string minLength: 1 description: | IANA timezone identifier from the client (top-level field). Used to provide time-aware context to the AI. example: America/New_York currentTime: type: string format: date-time pattern: ^\d{4}-\d{2}-\d{2}T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?(Z|[+-]\d{2}:?\d{2})$ description: | ISO 8601 / RFC 3339 datetime from the client (top-level field; UTC `Z` or numeric offset). example: '2026-04-12T16:00:00+05:30' tools: type: array items: type: string minLength: 1 description: | Optional list of tool identifiers (fully-qualified action names such as "jira.create_issue") that the AI agent is permitted to invoke for this request. When omitted the agent may use any configured tool. Applicable only when `chatMode` is `agent`. Web search is not governed by this list; `agentCapabilities.webSearch` turns it on or off. example: - jira.create_issue - confluence.search_content protocol: type: string enum: - agui description: | AG-UI is the only supported wire protocol. When present must be `"agui"`. Omitting the field is equivalent — the server always uses the AG-UI vocabulary (`RUN_STARTED`, `TEXT_MESSAGE_CONTENT`, etc.). Kept in the schema for backward compatibility with callers that already send it. agentCapabilities: $ref: '#/components/schemas/AgentCapabilities' includeRetrievalContext: type: boolean default: false description: 'Opt in to measurement frames on this stream: CUSTOM `retrieval_context` events naming the records and blocks that reached the model, and one CUSTOM `run_usage` event carrying the run''s token totals and loop outcome. Identifiers only, never block text. Does not change the answer.' keywordSearchBudget: type: string enum: - short - full description: How long each search waits for the keyword (grep) match after its semantic results are in. `short` waits at most 5 seconds and keeps the keyword matches found by then, for callers that would rather answer sooner, such as agents calling chat over MCP. `full`, or leaving it out, waits for all of it. Deployments running `HIGH_ACCURACY_MODE` always wait in full. toolApproval: $ref: '#/components/schemas/ToolApprovalAnswer' runId: type: string format: uuid pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ description: | Client-generated identifier for this run. Send it here to enable `POST /conversations/{conversationId}/cancel {runId}` while the stream is still generating. Optional — a caller that never sends one just can't cooperatively cancel the run. reasoningEffort: type: string enum: - none - low - medium - high - max description: | How hard a reasoning model should think for this turn. Saved in the conversation's `modelInfo`. ConversationStreamRequest: allOf: - $ref: '#/components/schemas/CreateConversationRequest' - type: object description: | Request body for `POST /conversations/stream`. The public stream requires an explicit universal execution mode; the legacy modes the non-streaming route accepts are refused here. Unlike `POST /conversations/create`, a `query` of only whitespace is not refused: the turn runs and the conversation is titled with it. required: - chatMode properties: chatMode: type: string enum: - agent - internal_search - web_search CreateAgentConversationResponse: type: object description: | Envelope returned by `POST /agents/{agentKey}/conversations`: the persisted agent conversation (initial user message plus the agent's answer) and request metadata. required: - conversation - meta properties: conversation: $ref: '#/components/schemas/AgentConversation' meta: type: object required: - timestamp - duration properties: requestId: type: string timestamp: type: string format: date-time duration: type: integer minimum: 0 CreateConversationResponse: type: object additionalProperties: false description: | Envelope returned by `POST /conversations/create`. Contains the persisted conversation (including the initial user message and the AI response) plus request metadata. required: - conversation - meta properties: conversation: $ref: '#/components/schemas/Conversation' meta: type: object additionalProperties: false required: - timestamp - duration properties: requestId: type: string description: | Request correlation id. Omitted when upstream middleware did not set a request id on the context. timestamp: type: string format: date-time description: Server timestamp when the response was sent. duration: type: integer minimum: 0 description: Total handler duration in milliseconds. AgentStreamCreateConversationRequest: allOf: - $ref: '#/components/schemas/AgentCreateConversationRequest' - type: object description: | Request body for `POST /agents/{agentKey}/conversations/stream`. `query` and `chatMode: quick` are required; all other fields are optional overrides. Unknown fields are stripped during validation. Unlike the non-streaming route, a `query` of only whitespace is accepted and starts a conversation. required: - chatMode AgentCreateConversationRequest: type: object additionalProperties: false description: | First turn of an agent conversation. Used as-is by the non-streaming `POST /agents/{agentKey}/conversations`, where `chatMode` may be omitted; `AgentStreamCreateConversationRequest` additionally requires it. Unknown fields are stripped during validation, except `previousConversations`: history is never accepted from the caller, so `previousConversations` with any value but an empty list (`null`, a string or an object included) is rejected with 400; an empty list is dropped. Follow-up turns go to `POST /agents/{agentKey}/conversations/{conversationId}/messages`, where the server replays the stored conversation. required: - query properties: query: type: string minLength: 1 maxLength: 100000 description: | User prompt for the first turn. Saved as the initial `user_query` message and sent to the agent backend. recordIds: type: array items: type: string format: objectId pattern: ^[0-9a-fA-F]{24}$ description: | Optional record ids to include as context for this turn. Each id must be a 24-character MongoDB ObjectId. filters: allOf: - $ref: '#/components/schemas/Filters' description: | Optional retrieval scope (`apps` / `kb`) for this turn. Each id must be a valid UUID. Omit for agent defaults; send `{ "apps": [], "kb": [] }` to force no knowledge sources for this turn. appliedFilters: allOf: - $ref: '#/components/schemas/AppliedFilters' description: | UI filter state persisted on the saved user message. Not used for retrieval and not forwarded to the upstream agent backend. attachments: type: array items: $ref: '#/components/schemas/ChatAttachmentRef' description: | Uploaded attachments to ground this turn. Each entry references a record id returned from the agent attachment upload endpoint. projectId: type: string format: objectId pattern: ^[0-9a-fA-F]{24}$ description: | Link the new agent conversation to a project the caller has at least viewer access to. Same fallback/merge semantics as `POST /conversations/create`. Ignored on follow-up turns — the session row is the source of truth once the conversation exists. An OAuth token or PAT also needs `project:read`. An archived project answers `409`; a project the caller cannot see, `404`, before any stream opens. projectVisibility: type: string enum: - private - project description: | Only meaningful together with `projectId`. Overrides the project's default sharing behavior for this one conversation. chatMode: type: string enum: - quick description: | Execution mode. Scoped agent conversations support only `quick`. Required on the `/stream` route; optional on the non-streaming route. modelKey: type: string minLength: 1 description: | AI model configuration id for this turn. Omit to use the agent's default model. A key that names no configured model is not refused: the turn runs on the organization's default model and the unknown key is still stored in the conversation's `modelInfo`. modelName: type: string minLength: 1 description: Provider model name (the underlying LLM identifier). modelFriendlyName: type: string minLength: 1 description: Friendly UI label for the selected model. timezone: type: string minLength: 1 description: | Client IANA timezone, such as `America/New_York`. Helps the agent resolve relative date references in the prompt. currentTime: type: string format: date-time pattern: ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?(Z|[+-]\d{2}:?\d{2})$ description: | Client time in ISO 8601 / RFC 3339 format (UTC `Z` or numeric offset). Sent alongside `timezone` for time-aware answers. tools: type: array items: type: string minLength: 1 description: | Allowed tool ids for this turn, such as `jira.create_issue`. Omit to let the agent use its default toolset; send `[]` to disable tools for this turn. A list that leaves out `web_search` turns off the agent's web search for the turn. protocol: type: string enum: - agui description: | AG-UI is the only supported wire protocol. When present must be `"agui"`. Omitting the field is equivalent — the server always uses the AG-UI vocabulary (see `AgentStreamSSEEvent`). Kept in the schema for backward compatibility with callers that already send it. agentCapabilities: $ref: '#/components/schemas/AgentCapabilities' includeRetrievalContext: type: boolean default: false description: 'Opt in to measurement frames on this stream: CUSTOM `retrieval_context` events naming the records and blocks that reached the model, and one CUSTOM `run_usage` event carrying the run''s token totals and loop outcome. Identifiers only, never block text. Does not change the answer.' keywordSearchBudget: type: string enum: - short - full description: How long each search waits for the keyword (grep) match after its semantic results are in. `short` waits at most 5 seconds and keeps the keyword matches found by then, for callers that would rather answer sooner, such as agents calling chat over MCP. `full`, or leaving it out, waits for all of it. Deployments running `HIGH_ACCURACY_MODE` always wait in full. toolApproval: $ref: '#/components/schemas/ToolApprovalAnswer' runId: type: string format: uuid pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ description: | Client-generated identifier for this run. Send it here to enable `POST /agents/{agentKey}/conversations/{conversationId}/cancel {runId}` while the stream is still generating. reasoningEffort: type: string enum: - none - low - medium - high - max description: | Reasoning effort for this turn, for models that support it. Stored on the conversation's model info and forwarded to the AI backend. example: query: what are some latest tech news? modelKey: 5c1832f4-fa19-4167-b913-307fad3a6551 modelName: gpt-5.6-luna modelFriendlyName: GPT 5.4 mini chatMode: quick timezone: Asia/Kolkata currentTime: '2026-05-19T12:58:01+05:30' tools: [] filters: apps: - 2605c882-61d4-4aa2-b480-a68c957c151d - ed6d6cc4-70bd-4838-9aeb-488e910c833a - aeab9ddc-fb9b-47c8-ad98-bd4744e19555 kb: - 8747da12-4724-4a95-ac92-827b88d79647 appliedFilters: apps: - id: 2605c882-61d4-4aa2-b480-a68c957c151d name: US Headlines, abcnews nodeType: app connector: RSS - id: ed6d6cc4-70bd-4838-9aeb-488e910c833a name: ABC News RSS nodeType: app connector: RSS - id: aeab9ddc-fb9b-47c8-ad98-bd4744e19555 name: Hacker news rss nodeType: app connector: RSS kb: - id: 8747da12-4724-4a95-ac92-827b88d79647 name: Siddhant Ota's Private nodeType: recordGroup connector: KB RegenerateRequest: type: object additionalProperties: false description: | Request body for regenerating an AI response. All fields are optional; when omitted the model selection and execution context from the original message are reused. Supported fields: - `attachments` — optional files of the edited question, replacing the stored ones (an empty list removes them all); omitted keeps them. At most 10, each a ref from the attachment upload - `query` — optional edited question. When it differs from the stored question, the answer is regenerated for it and the stored question is replaced once the new answer is saved; a failed run keeps the original - `filters` — optional `{ apps?, kb? }` filter object - `chatMode` — optional non-empty chat mode string - `modelKey`, `modelName`, `modelFriendlyName` — optional non-empty model override fields - `timezone` — optional non-empty client timezone string - `currentTime` — optional ISO 8601 / RFC 3339 datetime string with UTC `Z` or a numeric offset - `tools` — optional array of non-empty tool identifiers - `protocol` — optional inert compatibility field; when present it must be `agui`, which is also the protocol used when the field is omitted - `agentCapabilities` — optional per-request agent capability toggles - `answers` — optional changed answers to the question card the reply followed; the turn is re-run with them and they replace the saved ones properties: query: type: string minLength: 1 maxLength: 100000 description: | The edited question to answer in place of the stored one. Only the last question of a conversation can be edited, and not one that ran an approved action or is itself a tool-approval or question-card answer. A turn that went through an answered question card can be edited: the question is rewritten and the whole turn, including its card and the card's answers, is replaced by the new answer. A card turn that spans more than 50 saved rows cannot be edited; the request is refused and the stored answer is left as it was. So is an edit of an older question while a newer question has no answer. A run that ends in an error answer, or whose answer cannot be saved, leaves the stored question, its files and the rows of its card turn as they were. attachments: type: array maxItems: 10 items: $ref: '#/components/schemas/ChatAttachmentRef' description: | The files of the edited question, replacing the stored ones. An empty list removes them all; omitted keeps the stored files. A change of files alone, with the same `query`, also counts as an edit. Files are not deleted when removed from a question: a fork of the chat may still refer to them. filters: $ref: '#/components/schemas/Filters' answers: type: string maxLength: 8000 pattern: '^\s*User selections:' description: | The answers to the question card this reply followed, in the form the app sends them (`User selections:` followed by one `N. "question" → answer` line each). When present, the turn is re-run with these answers instead of the saved ones, and they are saved in their place. Over 8000 characters, or text not starting with `User selections:`, is refused with `400 VALIDATION_ERROR`. A reply that did not follow a question card, or a request that also edits the question (`query` or `attachments`), is refused after the stream opens: a 200 stream ending in a `RUN_ERROR` (see the operation). example: |- User selections: 1. "Which region?" → EU modelKey: type: string minLength: 1 description: | Identifier of the AI model configuration to use for regeneration. Typically a UUID returned by the model-management endpoints. When omitted, the model used for the original message is reused. example: 05438a37-68f2-4641-a8dc-6c47e63278ca modelName: type: string minLength: 1 description: Provider model name (e.g. the underlying LLM identifier). example: gpt-5.6-luna modelFriendlyName: type: string minLength: 1 description: Friendly display name of the selected model. example: mini chatMode: type: string minLength: 1 description: | Chat mode used for regeneration (for example `internal_search`, `web_search`, or the universal `agent` mode). example: internal_search reasoningEffort: type: string enum: - none - low - medium - high - max description: | Reasoning effort for this turn, for models that support it. Stored on the conversation's model info and forwarded to the AI backend. timezone: type: string minLength: 1 description: | IANA timezone identifier from the client. Used to provide time-aware context to the AI during regeneration. example: Asia/Calcutta currentTime: type: string format: date-time pattern: ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?(Z|[+-]\d{2}:?\d{2})$ description: | ISO 8601 / RFC 3339 datetime from the client (UTC `Z` or numeric offset). Used to anchor any relative time references in the query. example: '2026-05-11T15:43:21+05:30' tools: type: array items: type: string minLength: 1 description: | Optional list of tool identifiers (fully-qualified action names such as `jira.create_issue`) the agent may invoke when regenerating. Applicable only in agent chat modes. Regenerating in the assistant's agent mode, web search is not governed by this list; `agentCapabilities.webSearch` turns it on or off. Regenerating a named agent's message (`AgentRegenerateRequest`), a list that leaves out `web_search` turns off the agent's web search for the turn, even when `agentCapabilities.webSearch` is true. example: - jira.create_issue - confluence.search_content protocol: type: string enum: - agui description: | AG-UI is the only supported wire protocol. When present must be `"agui"`. Omitting the field is equivalent — the server always uses the AG-UI vocabulary. Kept in the schema for backward compatibility with callers that already send it. agentCapabilities: $ref: '#/components/schemas/AgentCapabilities' toolApproval: $ref: '#/components/schemas/ToolApprovalAnswer' runId: type: string format: uuid pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ description: | Client-generated identifier for this regeneration run. Send it here to enable `POST .../cancel {runId}` while it is still generating. AgentRegenerateRequest: allOf: - $ref: '#/components/schemas/RegenerateRequest' - type: object description: | Regeneration payload for a scoped agent conversation. The public gateway requires `chatMode: quick`; all other fields retain the semantics defined by `RegenerateRequest`. required: - chatMode properties: chatMode: type: string enum: - quick AddMessageRequest: type: object description: | Request body for adding a message to an existing conversation. Fields not listed here are dropped by the gateway's validator, not refused. required: - query properties: query: type: string minLength: 1 maxLength: 100000 description: | The follow-up question or message content. Held to the same 100,000-character limit as the first message of a conversation. example: Can you elaborate on the revenue trends? filters: $ref: '#/components/schemas/Filters' appliedFilters: $ref: '#/components/schemas/AppliedFilters' attachments: type: array items: $ref: '#/components/schemas/ChatAttachmentRef' description: | Uploaded chat attachments for this follow-up turn (see `POST /conversations/attachments/upload`). modelKey: type: string minLength: 1 description: | Override the model for this specific message. When neither `modelKey` nor `modelName` is sent, the organization's default model answers and is saved in the message's `modelInfo`. modelName: type: string minLength: 1 description: Display name of the model modelFriendlyName: type: string minLength: 1 description: Friendly display name of the model chatMode: type: string enum: - agent - internal_search - web_search - deep - quick - verification - auto description: | Optional execution mode for non-stream consumers of this shared request schema. `deep`, `quick`, `verification` and `auto` are legacy values: still accepted, they run as `internal_search`. The streaming routes accept only `agent`, `internal_search` and `web_search`. `agent:` is refused. timezone: type: string minLength: 1 description: | IANA timezone identifier from the client (top-level field). Used to provide time-aware context to the AI. example: America/New_York currentTime: type: string format: date-time pattern: ^\d{4}-\d{2}-\d{2}T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?(Z|[+-]\d{2}:?\d{2})$ description: | ISO 8601 / RFC 3339 datetime from the client (top-level field; UTC `Z` or numeric offset). example: '2026-04-12T16:00:00+05:30' tools: type: array items: type: string minLength: 1 description: | Optional list of tool identifiers the agent may invoke for this follow-up message. Semantics are identical to the create-conversation tools field. example: - jira.create_issue - confluence.search_content protocol: type: string enum: - agui description: | AG-UI is the only supported wire protocol. When present must be `"agui"`. Omitting the field is equivalent — the server always uses the AG-UI vocabulary (see `ConversationMessageStreamSSEEvent`). Kept in the schema for backward compatibility with callers that already send it. agentCapabilities: $ref: '#/components/schemas/AgentCapabilities' includeRetrievalContext: type: boolean default: false description: 'Opt in to measurement frames on this stream: CUSTOM `retrieval_context` events naming the records and blocks that reached the model, and one CUSTOM `run_usage` event carrying the run''s token totals and loop outcome. Identifiers only, never block text. Does not change the answer.' keywordSearchBudget: type: string enum: - short - full description: How long each search waits for the keyword (grep) match after its semantic results are in. `short` waits at most 5 seconds and keeps the keyword matches found by then, for callers that would rather answer sooner, such as agents calling chat over MCP. `full`, or leaving it out, waits for all of it. Deployments running `HIGH_ACCURACY_MODE` always wait in full. toolApproval: $ref: '#/components/schemas/ToolApprovalAnswer' runId: type: string format: uuid pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ description: | Client-generated identifier for this run. Send it here to enable `POST /conversations/{conversationId}/cancel {runId}` while the stream is still generating. reasoningEffort: type: string enum: - none - low - medium - high - max description: | How hard a reasoning model should think for this turn. Saved in the conversation's `modelInfo`. ConversationMessageStreamRequest: allOf: - $ref: '#/components/schemas/AddMessageRequest' - type: object description: | Request body for `POST /conversations/{conversationId}/messages/stream`. The public stream requires an explicit universal execution mode; the legacy modes the non-streaming route accepts are refused here. Unlike the non-streaming route, a `query` of only whitespace is not refused: the turn runs with it. required: - chatMode properties: chatMode: type: string enum: - agent - internal_search - web_search AddMessageResponse: type: object additionalProperties: false description: | Envelope returned by `POST /conversations/{conversationId}/messages`. Contains the updated conversation, the number of citation records used to ground the AI response for this turn, and request metadata. required: - conversation - recordsUsed - meta properties: conversation: $ref: '#/components/schemas/Conversation' recordsUsed: type: integer minimum: 0 description: | Number of citation records used to ground the AI response generated for this turn. meta: type: object additionalProperties: false required: - timestamp - duration properties: requestId: type: string description: | Request correlation id. Omitted when upstream middleware did not set `req.context.requestId`. timestamp: type: string format: date-time description: Server timestamp when the response was sent. duration: type: integer minimum: 0 description: Total handler duration in milliseconds. recordsUsed: type: integer minimum: 0 description: | Same value as the top-level `recordsUsed`. Duplicated inside `meta` for client convenience. AgentAddMessageStreamRequest: allOf: - $ref: '#/components/schemas/AgentAddMessageRequest' - type: object description: | Request body for `POST /agents/{agentKey}/conversations/{conversationId}/messages/stream`. `query` and `chatMode: quick` are required; all other fields are optional overrides. Unknown fields are stripped during validation. required: - chatMode AgentAddMessageRequest: type: object additionalProperties: false description: | Follow-up turn on an agent conversation. Used as-is by the non-streaming `POST /agents/{agentKey}/conversations/{conversationId}/messages`, where `chatMode` may be omitted; `AgentAddMessageStreamRequest` additionally requires it. Unknown fields are stripped during validation. required: - query properties: query: type: string minLength: 1 maxLength: 100000 description: | User follow-up prompt to append to the existing agent conversation. Saved as a new `user_query` message before the upstream AI stream starts. filters: allOf: - $ref: '#/components/schemas/Filters' description: | Optional retrieval scope (`apps` / `kb`) for this turn. Each id must be a valid UUID. Omit to let the agent use its stored defaults; send `{ "apps": [], "kb": [] }` to force no knowledge sources for this turn. appliedFilters: allOf: - $ref: '#/components/schemas/AppliedFilters' description: | UI filter state persisted on the saved user message. Not used for retrieval and not forwarded to the upstream agent backend. attachments: type: array items: $ref: '#/components/schemas/ChatAttachmentRef' description: | Uploaded attachments to ground this turn. Each entry references a record id returned from the agent attachment upload endpoint. chatMode: type: string enum: - quick description: | Execution mode. Scoped agent conversations support only `quick`. Required on the `/stream` route; optional on the non-streaming route. modelKey: type: string minLength: 1 description: | AI model configuration id override for this turn. Omit to use the agent's default model. A key that names no configured model is not refused: the turn runs on the organization's default model. modelName: type: string minLength: 1 description: Provider model name (the underlying LLM identifier). modelFriendlyName: type: string minLength: 1 description: Friendly UI label for the selected model. timezone: type: string minLength: 1 description: | Client IANA timezone, such as `America/New_York`. Helps the agent resolve relative date references in the prompt. currentTime: type: string format: date-time pattern: ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?(Z|[+-]\d{2}:?\d{2})$ description: | Client time in ISO 8601 / RFC 3339 format (UTC `Z` or numeric offset). Sent alongside `timezone` for time-aware answers. tools: type: array items: type: string minLength: 1 description: | Allowed tool ids for this turn, such as `jira.create_issue`. Omit to let the agent use its default toolset; send `[]` to disable tools for this turn. A list that leaves out `web_search` turns off the agent's web search for the turn. protocol: type: string enum: - agui description: | AG-UI is the only supported wire protocol. When present must be `"agui"`. Omitting the field is equivalent — the server always uses the AG-UI vocabulary (see `AgentMessageStreamSSEEvent`). Kept in the schema for backward compatibility with callers that already send it. agentCapabilities: $ref: '#/components/schemas/AgentCapabilities' includeRetrievalContext: type: boolean default: false description: 'Opt in to measurement frames on this stream: CUSTOM `retrieval_context` events naming the records and blocks that reached the model, and one CUSTOM `run_usage` event carrying the run''s token totals and loop outcome. Identifiers only, never block text. Does not change the answer.' keywordSearchBudget: type: string enum: - short - full description: How long each search waits for the keyword (grep) match after its semantic results are in. `short` waits at most 5 seconds and keeps the keyword matches found by then, for callers that would rather answer sooner, such as agents calling chat over MCP. `full`, or leaving it out, waits for all of it. Deployments running `HIGH_ACCURACY_MODE` always wait in full. toolApproval: $ref: '#/components/schemas/ToolApprovalAnswer' runId: type: string format: uuid pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ description: | Client-generated identifier for this run. Send it here to enable `POST /agents/{agentKey}/conversations/{conversationId}/cancel {runId}` while the stream is still generating. reasoningEffort: type: string enum: - none - low - medium - high - max description: | Reasoning effort for this turn, for models that support it. Stored on the conversation's model info and forwarded to the AI backend. example: query: can you elaborate on the latest headlines? modelKey: 5c1832f4-fa19-4167-b913-307fad3a6551 modelName: gpt-5.6-luna modelFriendlyName: GPT 5.4 mini chatMode: quick timezone: Asia/Kolkata currentTime: '2026-05-19T12:58:01+05:30' tools: [] filters: apps: - 2605c882-61d4-4aa2-b480-a68c957c151d - ed6d6cc4-70bd-4838-9aeb-488e910c833a kb: - 8747da12-4724-4a95-ac92-827b88d79647 appliedFilters: apps: - id: 2605c882-61d4-4aa2-b480-a68c957c151d name: US Headlines, abcnews nodeType: app connector: RSS - id: ed6d6cc4-70bd-4838-9aeb-488e910c833a name: ABC News RSS nodeType: app connector: RSS kb: - id: 8747da12-4724-4a95-ac92-827b88d79647 name: Siddhant Ota's Private nodeType: recordGroup connector: KB MessageToolCall: type: object additionalProperties: false description: One tool invocation recorded on a message turn. properties: toolName: type: string toolResult: {} MessageReasoningTurn: type: object additionalProperties: false description: | One model turn's chain-of-thought. Persisted only when reasoning persistence is enabled; the array is empty otherwise. required: - content properties: messageId: type: string turnIndex: type: number content: type: string MessagePart: type: object additionalProperties: false description: | One entry in the ordered agent-activity transcript for a message turn. Every field beyond `type` is optional and depends on the part kind, and `sub_agent` nests this same shape recursively under `parts`. Tool results here are always a bounded preview, never the full payload — `artifactId` points at the complete result. properties: type: type: string enum: - text - reasoning - tool_call - sub_agent content: type: string toolCallId: type: string toolName: type: string displayName: type: string args: type: string argsSummary: type: string description: Human-readable summary of `args`, computed server-side. status: type: string enum: - running - completed - failed - blocked resultPreview: type: string resultSummary: type: string description: | Human-readable summary of the tool result, computed server-side from the full untruncated output. artifactId: type: string description: Blob-backed artifact id for the full tool result. runId: type: string roleName: type: string isFinal: type: boolean description: | Set on the single root-level `text` part carrying the answer. Every other root `text` part is an abandoned preamble turn. Never set on child parts nested under a `sub_agent`. parts: type: array description: Nested transcript of a `sub_agent` part. items: $ref: '#/components/schemas/MessagePart' Message: type: object additionalProperties: false description: | A single message within a conversation. Messages can be user queries, AI responses, system messages, or error notifications. properties: _id: type: string format: objectId description: Unique message identifier messageType: type: string enum: - user_query - bot_response - error - feedback - system - tool_call description: | Type of message: - `user_query` - User's question or input - `bot_response` - AI-generated response - `error` - Error message from the system - `feedback` - User feedback on a response - `system` - System notification or status - `tool_call` - Tool invocation turn; details are on `tools` content: type: string description: The message text content contentFormat: type: string enum: - MARKDOWN - JSON - HTML description: Format of the content for rendering default: MARKDOWN citations: type: array items: anyOf: - $ref: '#/components/schemas/CitationReference' - $ref: '#/components/schemas/PopulatedCitationReference' - $ref: '#/components/schemas/RedactedSource' description: | References to source documents used in the response. Routes that return the saved conversation after a turn (create, add message) populate each item to `{ citationId, citationData }`. confidence: type: string nullable: true description: | AI confidence in the answer. Present only on `bot_response` messages, and only when the model emitted a trailing confidence block. This field is now optional and nullable; it was previously always present and non-nullable. Treat a missing or `null` value as "no confidence reported" and guard before using it. Change effective in SDK v1.3.0 (v1.2.0 and earlier always populated it). followUpQuestions: type: array items: $ref: '#/components/schemas/FollowUpQuestion' description: Suggested follow-up questions feedback: type: array items: $ref: '#/components/schemas/MessageFeedback' description: User feedback on this message metadata: type: object additionalProperties: false properties: processingTimeMs: type: number description: Time taken to generate response in milliseconds modelVersion: type: string description: Version of the AI model used aiTransactionId: type: string description: Transaction ID for tracking in AI backend reason: type: string description: Additional context or reasoning modelInfo: $ref: '#/components/schemas/ConversationModelInfo' appliedFilters: $ref: '#/components/schemas/AppliedFilters' referenceData: type: array description: | Reference identifiers extracted from tool responses, used to scope follow-up queries (for example Jira project keys or record IDs). items: anyOf: - type: object additionalProperties: false properties: name: type: string description: Display name shown to the user. id: type: string description: Technical identifier (numeric ID, UUID, etc.). type: type: string description: Item type (e.g. `project`, `issue`, `file`, `notebook`, `page`). app: type: string description: | Source application (e.g. `jira`, `confluence`, `sharepoint`, `slack`, `drive`, `gmail`). webUrl: type: string description: URL to open the item in a browser. metadata: type: object additionalProperties: type: string description: | App-specific fields keyed by name (e.g. `key` for a Jira project, `siteId` for a SharePoint document). - $ref: '#/components/schemas/RedactedSource' attachments: type: array description: | Files uploaded for this message turn (see `POST /conversations/attachments/upload`). items: $ref: '#/components/schemas/ChatAttachmentRef' tools: type: array description: Tool call results invoked during this message turn. items: $ref: '#/components/schemas/MessageToolCall' reasoning: type: array description: Persisted chain-of-thought for this turn. items: $ref: '#/components/schemas/MessageReasoningTurn' parts: type: array description: Ordered agent-activity transcript for this turn. items: $ref: '#/components/schemas/MessagePart' createdAt: type: string format: date-time updatedAt: type: string format: date-time RedactedSource: type: object additionalProperties: false description: | Stands in for a source the viewer of a shared chat or search cannot read: a citation, reference, filter node or search hit with no id, name, link or text. Only the owner of a conversation or search sees every source. required: - redacted - reason properties: redacted: type: boolean enum: - true reason: type: string enum: - no_access name: type: string description: A neutral label (`Restricted source`). CitationReference: type: object additionalProperties: false description: Reference to a source document cited in a response properties: citationId: type: string format: objectId description: ID of the citation record relevanceScore: type: number minimum: 0 maximum: 1 description: How relevant this citation is to the query (0-1) excerpt: type: string description: Relevant excerpt from the source document context: type: string description: Additional context around the citation PopulatedCitationReference: type: object additionalProperties: false description: | A message's citation reference after the handler populates it: the stored reference fields, with `citationId` as the id and the cited document under `citationData`. properties: citationId: type: string format: objectId description: ID of the citation record relevanceScore: type: number minimum: 0 maximum: 1 description: How relevant this citation is to the query (0-1) excerpt: type: string description: Relevant excerpt from the source document context: type: string description: Additional context around the citation citationData: $ref: '#/components/schemas/Citation' Citation: type: object additionalProperties: false description: | A populated citation document. Represents a single chunk of source content (e.g. a passage from a document or record) referenced by an AI response, together with its provenance metadata. required: - _id - content - chunkIndex - citationType - metadata - createdAt - updatedAt properties: _id: type: string format: objectId content: type: string description: The cited text chunk chunkIndex: type: integer description: Index of this chunk within the source record citationType: type: string description: Source type identifier (e.g. `vectordb|document`) metadata: $ref: '#/components/schemas/PersistedSemanticSearchCitationMetadata' createdAt: type: string format: date-time updatedAt: type: string format: date-time ConversationModelInfo: type: object additionalProperties: false description: AI model configuration recorded against a conversation or message. properties: modelKey: type: string description: Stable identifier of the configured model record modelName: type: string description: Provider-facing model name (e.g. `gpt-5.6-luna`) modelProvider: type: string description: Provider key (e.g. `openai`, `anthropic`) modelFriendlyName: type: string description: Human-readable display name chatMode: type: string description: Chat mode used for this turn (e.g. `quick`, `internal_search`) reasoningEffort: type: string enum: - none - low - medium - high - max description: Reasoning effort requested for the turn, when one was set. FollowUpQuestion: type: object additionalProperties: false description: AI-suggested follow-up question properties: question: type: string description: The suggested question text confidence: type: string description: Confidence level for this suggestion reasoning: type: string description: Why this question might be relevant MessageFeedbackSubmitRequest: type: object additionalProperties: false description: | Gateway request body for submitting message feedback (Zod `feedbackBodySchema`). All fields are optional; an empty object is accepted. Matches the first-party chat UI payload shape. properties: isHelpful: type: boolean description: Overall helpfulness signal (thumbs up/down). categories: type: array description: Issue or positive categories that apply to the response. items: type: string enum: - incorrect_information - missing_information - irrelevant_information - unclear_explanation - poor_citations - excellent_answer - helpful_citations - well_explained - other comments: type: object additionalProperties: false description: Free-text comments grouped by sentiment. properties: positive: type: string description: What was good about the response. negative: type: string description: What could be improved. MessageFeedback: type: object additionalProperties: false description: | Comprehensive feedback on an AI response. Feedback helps improve the AI's performance and response quality over time. properties: isHelpful: type: boolean description: Overall helpfulness rating ratings: type: object additionalProperties: false properties: accuracy: type: integer minimum: 1 maximum: 5 description: How accurate was the information (1-5) relevance: type: integer minimum: 1 maximum: 5 description: How relevant was the response (1-5) completeness: type: integer minimum: 1 maximum: 5 description: How complete was the answer (1-5) clarity: type: integer minimum: 1 maximum: 5 description: How clear was the explanation (1-5) categories: type: array items: type: string enum: - incorrect_information - missing_information - irrelevant_information - unclear_explanation - poor_citations - excellent_answer - helpful_citations - well_explained - other description: Categories of issues or positive attributes identified comments: type: object additionalProperties: false properties: positive: type: string description: What was good about the response negative: type: string description: What could be improved suggestions: type: string description: Specific suggestions for improvement citationFeedback: type: array items: type: object additionalProperties: false properties: _id: type: string format: objectId description: Auto-generated sub-document identifier citationId: type: string format: objectId isRelevant: type: boolean relevanceScore: type: integer minimum: 1 maximum: 5 comment: type: string description: Feedback on individual citations followUpQuestionsHelpful: type: boolean description: Were the suggested follow-up questions helpful unusedFollowUpQuestions: type: array items: type: string description: Follow-up questions that were suggested but not used by the user source: type: string enum: - user - system - admin - auto default: user description: Origin of the feedback. Always present in responses (server applies the default `user`). feedbackProvider: type: string format: objectId description: User who submitted the feedback timestamp: type: integer format: int64 description: | Time the feedback was created, stored as a Number (epoch milliseconds) with a server-side default of `Date.now`, so always present in responses. Not an ISO 8601 datetime. revisions: type: array description: Audit trail of edits to this feedback entry items: type: object additionalProperties: false properties: _id: type: string format: objectId description: Auto-generated sub-document identifier updatedFields: type: array items: type: string description: Names of feedback fields modified in this revision previousValues: type: object additionalProperties: true description: | Map of previously-set values for the fields named in `updatedFields`, keyed by field name. Stored as a Mongoose Map of Mixed values. updatedBy: type: string format: objectId updatedAt: type: integer format: int64 description: Time the revision was recorded, as epoch milliseconds. metrics: type: object additionalProperties: false description: Optional telemetry captured alongside the feedback properties: timeToFeedback: type: number description: Time from response delivery to feedback submission userInteractionTime: type: number description: Total time the user spent reviewing the response feedbackSessionId: type: string userAgent: type: string platform: type: string MessageFeedbackAppendMetrics: type: object additionalProperties: false required: - timeToFeedback description: | Telemetry recorded server-side alongside the feedback. Always present on append responses. properties: timeToFeedback: type: number description: | Milliseconds between message creation and feedback submission. Always present. userAgent: type: string description: Value of the `User-Agent` request header captured server-side. MessageFeedbackAppendEntry: type: object additionalProperties: false required: - feedbackProvider - timestamp - metrics description: | The feedback entry just appended to the message. Echoes the fields supplied in the request plus server-stamped `feedbackProvider`, `timestamp`, and `metrics`. properties: isHelpful: type: boolean description: Echoed from the request when supplied. categories: type: array description: Echoed categories from the request. items: type: string enum: - incorrect_information - missing_information - irrelevant_information - unclear_explanation - poor_citations - excellent_answer - helpful_citations - well_explained - other comments: type: object additionalProperties: false description: Echoed free-text comments from the request. properties: positive: type: string negative: type: string feedbackProvider: type: string format: objectId description: User who submitted the feedback. Always present. timestamp: type: integer format: int64 description: | Submission time as epoch milliseconds (not an ISO 8601 datetime). Always present. metrics: $ref: '#/components/schemas/MessageFeedbackAppendMetrics' MessageFeedbackUpdateResponse: type: object additionalProperties: false required: - conversationId - messageId - feedback - meta description: | Gateway response after appending feedback to a bot-response message. properties: conversationId: type: string format: objectId description: Conversation the feedback was attached to. messageId: type: string format: objectId description: Message the feedback was attached to. feedback: $ref: '#/components/schemas/MessageFeedbackAppendEntry' meta: type: object additionalProperties: false required: - requestId - timestamp - duration properties: requestId: type: string description: | Server-side request identifier. Read from the `X-Request-ID` header when supplied, otherwise auto-generated, so this field is always present. timestamp: type: string format: date-time duration: type: integer description: Server-side processing time in milliseconds. Conversation: type: object description: | A conversation represents a chat session between a user and the AI. Conversations maintain context across multiple messages and can be shared, archived, and organized. properties: _id: type: string format: objectId description: Unique conversation identifier userId: type: string format: objectId description: ID of the user who owns this conversation orgId: type: string format: objectId description: Organization this conversation belongs to title: type: string description: | Conversation title, auto-generated from first query or manually updated example: Q4 Financial Report Discussion initiator: type: string format: objectId description: User who started the conversation messages: type: array items: $ref: '#/components/schemas/Message' description: All messages in this conversation status: type: string enum: - None - Inprogress - Complete - Failed - Stopped description: | Current status of the conversation: - `None` — no activity yet - `Inprogress` — AI is processing - `Complete` — response ready - `Failed` — error occurred - `Stopped` — cancelled, or the client disconnected mid-answer; the last message keeps the partial answer failReason: type: string description: Error description, populated only when `status` is `Failed`. modelInfo: type: object properties: modelKey: type: string modelName: type: string modelFriendlyName: type: string description: Friendly display name of the selected model modelProvider: type: string chatMode: type: string reasoningEffort: type: string enum: - none - low - medium - high - max description: Reasoning effort requested for the turn, when one was set. description: AI model configuration used isShared: type: boolean default: false description: Whether this conversation is shared with others shareLink: type: string description: Shareable link if conversation is shared sharedWith: type: array items: type: object properties: userId: type: string format: objectId accessLevel: type: string enum: - read - write _id: type: string format: objectId description: Subdocument identifier auto-assigned by MongoDB. description: Users this conversation is shared with isArchived: type: boolean default: false description: Whether this conversation is archived archivedBy: type: string format: objectId nullable: true description: | User ID of the last user who archived this row, or `null` after unarchive cleared the archive state. Absent on rows that have never been archived. isDeleted: type: boolean default: false description: Whether this conversation has been soft-deleted. deletedBy: type: string format: objectId description: User who soft-deleted this conversation. conversationErrors: type: array description: Errors recorded against this conversation (e.g. failed message generations). items: type: object required: - message properties: _id: type: string format: objectId description: Sub-document identifier auto-assigned by MongoDB. message: type: string errorType: type: string timestamp: type: string format: date-time messageId: type: string format: objectId stack: type: string metadata: type: object additionalProperties: true metadata: type: object additionalProperties: true description: Free-form metadata attached to the conversation. lastActivityAt: type: integer description: Unix timestamp of last activity createdAt: type: string format: date-time updatedAt: type: string format: date-time isOwner: type: boolean description: | Computed per request. `true` when the requesting user is the conversation's `initiator`. readOnly: true accessLevel: type: string enum: - read - write description: | Computed per request. The requester's effective access level: their entry in `sharedWith`, or `read` by default. readOnly: true projectId: type: string format: objectId nullable: true description: | The project this conversation is linked to, if any. Set via `PUT /conversations/{conversationId}/project` or at creation time; absent on conversations that were never linked. projectVisibility: type: string enum: - private - project nullable: true description: | Only meaningful when `projectId` is set. `private` (default) keeps the conversation visible to its owner only; `project` exposes it to every member of the linked project. See `PATCH /conversations/{conversationId}/project-visibility`. sharedBy: $ref: '#/components/schemas/ConversationSharedBy' agentKey: type: string description: | Only on agent conversations (returned by `GET /conversations?source=shared`). Open them with `GET /agents/{agentKey}/conversations/{conversationId}`. __v: type: integer readOnly: true description: Mongoose document version key. ConversationSharedBy: type: object additionalProperties: false description: | Present on conversations the caller received via share. Identifies the conversation initiator (the only user who can share a chat). required: - userId - name properties: userId: type: string format: objectId name: type: string description: Display name, falling back to email or the user id ConversationListItem: type: object description: | Conversation summary returned by list endpoints. Identical to `Conversation` but omits `messages` to keep list payloads small. Fetch a single conversation to retrieve its messages. properties: agentKey: type: string description: | Only on agent conversations (returned by `GET /conversations?source=shared`). Open them with `GET /agents/{agentKey}/conversations/{conversationId}`. _id: type: string format: objectId userId: type: string format: objectId orgId: type: string format: objectId title: type: string initiator: type: string format: objectId status: type: string enum: - None - Inprogress - Complete - Failed - Stopped failReason: type: string modelInfo: type: object properties: modelKey: type: string modelName: type: string modelFriendlyName: type: string modelProvider: type: string chatMode: type: string reasoningEffort: type: string enum: - none - low - medium - high - max description: Reasoning effort requested when the conversation was created, when one was set. isShared: type: boolean shareLink: type: string sharedWith: type: array items: type: object properties: userId: type: string format: objectId accessLevel: type: string enum: - read - write _id: type: string format: objectId description: Subdocument identifier auto-assigned by MongoDB. isArchived: type: boolean archivedBy: type: string format: objectId nullable: true description: | User ID of the last user who archived this row, or `null` after unarchive cleared the archive state. Absent on rows that have never been archived. isDeleted: type: boolean deletedBy: type: string format: objectId conversationErrors: type: array items: type: object properties: _id: type: string format: objectId description: Sub-document identifier auto-assigned by MongoDB. message: type: string errorType: type: string timestamp: type: string format: date-time messageId: type: string format: objectId stack: type: string metadata: type: object additionalProperties: true metadata: type: object additionalProperties: true lastActivityAt: type: integer createdAt: type: string format: date-time updatedAt: type: string format: date-time isOwner: type: boolean readOnly: true accessLevel: type: string enum: - read - write readOnly: true isPinned: type: boolean description: | `true` when the owner pinned this conversation. Absent on conversations that are not pinned (never `false`). Set via `PATCH /conversations/{conversationId}/pin`. Only returned to the owner: lists with `source=shared` omit it. projectId: type: string format: objectId nullable: true description: | The project this conversation is linked to, if any. Set via `PUT /conversations/{conversationId}/project` or at creation time; absent on conversations that were never linked. projectVisibility: type: string enum: - private - project nullable: true description: | Only meaningful when `projectId` is set. `private` (default) keeps the conversation visible to its owner only; `project` exposes it to every member of the linked project. See `PATCH /conversations/{conversationId}/project-visibility`. sharedBy: $ref: '#/components/schemas/ConversationSharedBy' ConversationPinResponse: type: object additionalProperties: false required: - id - isPinned - meta properties: id: type: string format: objectId description: Conversation identifier isPinned: type: boolean description: The conversation's pin state after the call. meta: type: object additionalProperties: false properties: requestId: type: string description: Request correlation identifier timestamp: type: string format: date-time description: Response generation timestamp duration: type: integer description: Server processing time in milliseconds ForkConversationResponse: type: object additionalProperties: false required: - id - title - forkedFrom - messageCount - meta properties: id: type: string format: objectId description: New conversation identifier title: type: string description: Title of the forked conversation projectId: type: string format: objectId description: | Project the fork is linked to. Present only when the source conversation belonged to a project that the caller can still view; otherwise omitted. forkedFrom: type: object additionalProperties: false required: - conversationId - messageId properties: conversationId: type: string format: objectId messageId: type: string format: objectId messageCount: type: integer minimum: 0 description: Number of messages copied into the new conversation meta: type: object additionalProperties: false required: - timestamp - duration properties: requestId: type: string timestamp: type: string format: date-time duration: type: integer description: Server-side processing time in milliseconds RetrieveRequest: type: object required: - query properties: query: type: string minLength: 1 maxLength: 100000 description: What to find. This text is always searched. limit: type: integer minimum: 1 maximum: 50 default: 8 description: Most chunks to return, best first. Left out, the AI service uses 8. filters: $ref: '#/components/schemas/Filters' createdAfter: type: string description: '"YYYY-MM-DD" or an ISO 8601 datetime with an offset, inclusive. When the source record was created.' minLength: 1 maxLength: 64 createdBefore: type: string description: As `createdAfter`, the other end. minLength: 1 maxLength: 64 modifiedAfter: type: string description: As `createdAfter`, on when the source record last changed. minLength: 1 maxLength: 64 modifiedBefore: type: string description: As `modifiedAfter`, the other end. minLength: 1 maxLength: 64 maxChunksPerRecord: type: integer minimum: 1 maximum: 50 description: | Most chunks from any one record. Left out, the search's own per-record allowance applies: at most `max(4, limit / 6)` passages from one record with default settings (4 at `limit` 8, 8 at `limit` 50). With `limit` 4 or less, one record can fill the page. rewriteQuery: type: boolean default: false description: | Also search rewritten and expanded versions of the query, made by the org's model (slower). The original text is still searched. RetrieveChunk: type: object required: - id - text - record - citation properties: id: type: string description: Stable for the same indexed block, for deduplication and caching. text: type: string score: type: number nullable: true record: type: object properties: recordId: type: string name: type: string nullable: true connector: type: string nullable: true mimeType: type: string nullable: true updatedAt: type: string nullable: true additionalProperties: false required: - recordId - name - connector - mimeType - updatedAt citation: type: object properties: url: type: string nullable: true description: Deep link to the passage (a `#:~:text=` fragment where the source supports one). recordUrl: type: string nullable: true description: Link to the record itself. pageNum: type: array nullable: true items: type: integer blockIndex: type: integer nullable: true additionalProperties: false required: - url - recordUrl - pageNum - blockIndex additionalProperties: false RetrieveResponse: type: object required: - chunks - incomplete properties: requestId: type: string description: The request's id, as in the `x-request-id` response header. chunks: type: array items: $ref: '#/components/schemas/RetrieveChunk' incomplete: type: boolean description: True when part of the search could not run (for example a source timed out). message: type: string description: Why `chunks` is empty, when it is; `null` when the search gave no reason. Absent when `chunks` is not empty. nullable: true additionalProperties: false SemanticSearchRequest: type: object description: | Request body for performing semantic search across the enterprise knowledge base. **How Semantic Search Works:** 1. Query is converted to vector embeddings 2. Similar content is found using vector similarity 3. Results are ranked by relevance score 4. Matching chunks with metadata are returned **Filtering:** Use filters to narrow search scope to specific apps or knowledge bases. **Date bounds** (`createdAfter`, `createdBefore`, `modifiedAfter`, `modifiedBefore`) match when a record was written or last changed, not when an event it describes happened. With any of them set, and while the server's `DATE_FILTER_SAFETY_NET` setting is on (the default), the same search also runs once without dates, and its best matches that the bounds left out come back apart from the results, in `searchResponse.outsideDates`. With the setting off, no such search runs and `outsideDates` is omitted. required: - query properties: query: type: string minLength: 1 description: | Natural language search query. The system understands semantic meaning, not just keywords. A query containing HTML markup or a printf-style format specifier (`%s`, `%x`, `%n`) is refused with `400` `HTTP_BAD_REQUEST`. example: employee onboarding procedures filters: $ref: '#/components/schemas/Filters' limit: type: integer minimum: 1 maximum: 100 description: | For a ranked search, the maximum number of passages (entries of `searchResults`) the response holds, counted after query expansion and section expansion, so it holds at any number of queries. `records` lists only the records those passages come from. A search run as a timeline is not cut to `limit` as a whole: `limit` is what each period's own search asks for, and a period can hold more (see `timeline`). When the timeline was chosen automatically (`timeline` left out), each period asks for at most 10, whatever `limit` is. Left out, it is `10`. A numeric string (`"2"`) is accepted and read as a number; a fractional number is refused with `400 VALIDATION_ERROR`. default: 10 createdAfter: type: string maxLength: 64 description: | Only records the source created on or after this date. `YYYY-MM-DD` (the whole day, UTC) or an ISO 8601 datetime with a timezone offset. A date that is not one of these, or in the future, is a `400` that says why. example: '2026-01-01' createdBefore: type: string maxLength: 64 description: | Only records the source created on or before this date. A `YYYY-MM-DD` value includes that whole day. example: '2026-03-31' modifiedAfter: type: string maxLength: 64 description: | Only records last changed in the source on or after this date. Same format as `createdAfter`. example: '2026-09-01' modifiedBefore: type: string maxLength: 64 description: | Only records last changed in the source on or before this date. Same format as `createdBefore`. Records with no source date are left out whenever a bound on that date is set. example: '2026-09-30T23:59:59Z' rewriteQuery: type: boolean default: true description: | Whether the AI model's rewrite of the query and its expanded variants are searched as well. The query as sent is always searched, so an exact code or name in it is never lost to the rewrite. Set it to `false` to search the query exactly as sent and nothing else, with no model call: faster, and suited to callers that write their own queries, such as an agent. The results can differ from a search with the rewrite. example: false timeline: type: string enum: - created - modified - 'off' description: | Search a span of time period by period instead of ranking matches over the whole span, for questions such as "how did this evolve" or "what changed between March and May". A plain search returns the best matches overall, which bunch around one moment; a timeline splits the span into up to 6 periods (calendar months, or weeks within one month), searches each period on its own and beside one search of the whole span, and returns the matches in date order, each labelled with its period. The value is the date records are placed by: `created` (when the record was created at its source) or `modified` (its last change). The span is that date's bounds (`createdAfter` / `createdBefore`, or `modifiedAfter` / `modifiedBefore`); with no start it covers the year before the end, or further back to the earliest of the best matches. Bounds on the other date apply to every period. `limit` is the number of matches each period's own search asks for; matches from the search of the whole span (up to 50) are added to the periods their records are dated in, so a period can hold more than `limit`. Each search applies the same permissions and filters as a plain search. Left out, a search whose bounds on one date have a start and cover more than 31 days (a missing end is today) runs as a timeline on that date, and `timeline.automatic` is `true`; any other search is a plain ranked search. `off` is always a plain ranked search. A server whose `TIMELINE_SUMMARIES` setting is `false` ignores the field and never runs a timeline. example: created saveHistory: type: boolean default: true description: | Whether the search is saved to the caller's search history (`GET /search`) with a citation for each match. Set it to `false` for searches that are not the user's own, such as an agent searching on their behalf: nothing is stored and the response has no `searchId`. A server older than this field ignores it and saves the search. example: true SemanticSearchBoundingBox: type: object additionalProperties: false description: Normalized bounding region for a chunk (when available). properties: x: type: number y: type: number SemanticSearchHitMetadata: type: object additionalProperties: false description: | Per-hit metadata after retrieval enrichment (record + vector context). Listed fields are the documented contract; Qdrant or pipeline updates may add more keys over time—extend this schema when new stable fields appear. properties: orgId: type: string nullable: true recordId: type: string nullable: true virtualRecordId: type: string nullable: true recordName: type: string nullable: true recordType: type: string nullable: true recordVersion: nullable: true oneOf: - type: string - type: number origin: type: string nullable: true connector: type: string nullable: true connectorId: type: string nullable: true connectorInstanceName: type: string nullable: true description: The name the admin gave the connector instance; `connector` is only its type. connectorName: type: string nullable: true blockText: type: string nullable: true blockType: type: string nullable: true description: | Block type for this hit. Common values: `text`, `image`, `table_row`, `table`, `record_summary` (whole-record semantic summary chunk — no block index). bounding_box: type: array nullable: true items: $ref: '#/components/schemas/SemanticSearchBoundingBox' pageNum: type: array nullable: true items: nullable: true type: integer extension: type: string nullable: true mimeType: type: string nullable: true blockNum: type: array nullable: true items: type: number chunkIndex: type: integer nullable: true sheetName: type: string nullable: true sheetNum: type: integer nullable: true webUrl: type: string nullable: true previewRenderable: type: boolean nullable: true hideWeburl: type: boolean nullable: true updatedAt: type: string format: date-time nullable: true categories: type: array nullable: true items: type: string departments: type: array nullable: true items: type: string topics: type: array nullable: true items: type: string languages: type: array nullable: true items: type: string subcategoryLevel1: type: string nullable: true subcategoryLevel2: type: string nullable: true subcategoryLevel3: type: string nullable: true score: type: number nullable: true _id: type: string nullable: true _collection_name: type: string nullable: true blockIndex: type: integer nullable: true blockId: type: string nullable: true isBlock: type: boolean nullable: true isBlockGroup: type: boolean nullable: true isRecordSummary: type: boolean nullable: true description: | Set to `true` by the indexing pipeline when this vector chunk represents a whole-record semantic summary rather than an individual block. When true, `blockIndex` is absent and `block_type` on the parent hit is `record_summary`. kbId: type: string nullable: true description: Knowledge base id merged from graph record during retrieval (when present). point_id: description: Qdrant point identifier attached during vector lookup (shape varies by deployment). nullable: true oneOf: - type: string - type: integer - type: number SemanticSearchHit: type: object additionalProperties: false description: | One search hit returned by the retrieval service. Most hits use string `content`; table or grouped blocks may serialize structured payloads as JSON arrays. Listed fields are the documented contract; extend this schema when new stable top-level keys are introduced. properties: score: type: number nullable: true citationType: type: string nullable: true chunkIndex: type: integer nullable: true metadata: $ref: '#/components/schemas/SemanticSearchHitMetadata' content: type: string nullable: true virtual_record_id: type: string nullable: true block_type: type: string nullable: true description: | Block type for this hit. Common values: `text`, `image`, `table_row`, `table`, `record_summary` (whole-record semantic summary — `block_index` is `null` for these hits). block_index: type: integer nullable: true qualified_name: type: string description: | For a block of source code, the symbol it holds, written `kind:Scope.name` (for example `method:Outer.Inner.run`). A span with no name is addressed by its lines instead: `kind:L{line}` for one line (`statements:L1`), `kind:L{start}-{end}` for several (`imports:L1-5`), or `kind:anonymous` when it has no line numbers. An empty string for a block that is not code. Present on a hit that is a code block, and on each passage of a list, thread, text section or code group brought back whole because of a match inside it. Absent on any other hit that matched directly, on the rows of a table brought back whole, and on every piece of a section split around an image: its text, the image, and the text that stands in for the image when it is not sent as an image. distanceFromMatch: type: integer minimum: 0 description: | Set on a passage returned because a match inside its section (a thread, a list, a table) brought the whole section back: how many blocks it sits from the passage the search matched nearest to it. `0` is a matched passage. Absent when the hit matched directly or the section matched as a whole. block_group_index: type: integer nullable: true description: | Set on each passage of a section brought back whole (see `distanceFromMatch`): the index of that section in its record, so passages of two sections of one record can be told apart. Absent on a hit that matched directly. timelinePeriod: type: integer minimum: 0 description: | Set only on a search with `timeline`: the index in `timeline.periods` of the period this hit's record is dated in. GraphRecordFields: type: object description: | The fields of a record graph document that every graph backend returns. `id` is not among them: a schema that names the record adds it. properties: recordName: type: string nullable: true externalRecordId: type: string nullable: true recordType: type: string nullable: true origin: type: string nullable: true createdAtTimestamp: type: number nullable: true connectorId: type: string nullable: true orgId: type: string nullable: true updatedAtTimestamp: type: number nullable: true externalGroupId: type: string nullable: true externalParentId: type: string nullable: true externalRevisionId: type: string nullable: true externalRootGroupId: type: string nullable: true recordGroupId: type: string nullable: true version: type: number nullable: true connectorName: type: string nullable: true mimeType: type: string nullable: true webUrl: type: string nullable: true lastSyncTimestamp: type: number nullable: true sourceCreatedAtTimestamp: type: number nullable: true sourceLastModifiedTimestamp: type: number nullable: true isDeleted: type: boolean nullable: true isArchived: type: boolean nullable: true isVLMOcrProcessed: type: boolean nullable: true deletedByUserId: type: string nullable: true restoredAtTimestamp: type: number nullable: true description: When a restore last brought the file back from the trash (epoch milliseconds); see `restoredAtTimestamp` on GET /knowledgeBase/record/{recordId}. processingStartedAt: type: number nullable: true queuedAtTimestamp: type: number nullable: true retryNotBefore: type: number nullable: true description: | Earliest time (epoch milliseconds) indexing reads a record its source rate-limited; see `retryNotBefore` on GET /knowledgeBase/record/{recordId}. parsingStatus: type: string nullable: true indexingStatus: type: string nullable: true extractionStatus: type: string nullable: true isLatestVersion: type: boolean nullable: true isDirty: type: boolean nullable: true reason: type: string nullable: true lastIndexTimestamp: type: number nullable: true lastExtractionTimestamp: type: number nullable: true entityExtractionStatus: type: string nullable: true description: | Where named-entity extraction stands for the record; see `entityExtractionStatus` on GET /knowledgeBase/record/{recordId}. lastEntityExtractionTimestamp: type: number nullable: true entityExtractionStrategy: type: string nullable: true entityExtractorVersion: type: string nullable: true summaryDocumentId: type: string nullable: true storageDocumentId: type: string nullable: true virtualRecordId: type: string nullable: true contentRev: type: string nullable: true description: | Revision of the record's content (16 lowercase hex characters); see `contentRev` on GET /knowledgeBase/record/{recordId}. Absent on records not indexed since pipeline stages were introduced. previewRenderable: type: boolean nullable: true isShared: type: boolean nullable: true isDependentNode: type: boolean nullable: true parentNodeId: type: string nullable: true hideWeburl: type: boolean nullable: true isInternal: type: boolean nullable: true md5Checksum: type: string nullable: true sizeInBytes: type: number nullable: true definition: type: string nullable: true sourceTables: type: array nullable: true items: type: string rowCount: type: number nullable: true isPlaceholder: type: boolean description: | `true` when the record is a stub parent created for an ancestor that is not yet synced or is out of scope; replaced when the real record syncs. location: type: string description: | Where the record sits, from its source down to its parent, as `Name (App ID: id) -> Name (Record Group ID: id) -> Name (Record ID: id)`, resolved at retrieval time (not stored on the record). It names only the ancestors the caller can open; for a connected app with a limit, only those inside the limit. Absent when none is left. statusSummary: type: string description: | Where a ticket, pull request or project stands, in one short phrase, on `records` entries only: for example `Merged 2026-09-25 by alice`, `Closed without merging 2026-09-26`, or `DONE (Jira: Done), resolved 2026-06-06, assignee Bo, priority HIGH`. Dates are days in UTC. Absent for record types with no state of their own. linkedRecords: type: array description: | On `records` entries only: the records that name this one (`mentionedIn`) and that it names (`linksTo`) by web address or issue key, across connectors, limited to records the caller may read, at most 10 per relation. For a connected app with a limit, only records inside it. Absent when there are none. items: $ref: '#/components/schemas/SemanticSearchLinkedRecord' SemanticSearchGraphRecord: type: object description: | Graph record vertex returned in `records` and as values of `virtual_to_record_map`. All listed fields are optional in the schema so partial or evolving documents validate; typical Arango documents usually include `_key`, `_id`, `_rev`, `orgId`, `recordName`, `externalRecordId`, `recordType`, `origin`, `createdAtTimestamp`, and `connectorId`. Extend this schema when new stable fields appear on Record vertices. For a connected app with a limit, a parent the limit leaves out is not named: `recordGroupId` (with `externalGroupId`), `rootRecordGroupId` and `parentNodeId` (with `externalParentId`) are `null` for it, and `location` keeps only the part of the trail inside the limit, or is left out. `connectorInstanceName`, the source's or collection's own name, is left out unless the limit covers that app whole; this applies to each hit's `metadata` too. allOf: - $ref: '#/components/schemas/GraphRecordFields' - type: object properties: _key: type: string nullable: true _id: type: string nullable: true _rev: type: string nullable: true SemanticSearchLinkedRecord: type: object additionalProperties: false required: - relation - recordId - recordName - recordType - connector properties: relation: type: string enum: - mentionedIn - linksTo recordId: type: string recordName: type: string recordType: type: string connector: type: string status: type: string description: The linked record's `statusSummary`, when it has one. SemanticSearchAppliedFilters: type: object additionalProperties: false description: Present when KB filters were applied to the search request. required: - kb - kb_count properties: kb: type: array items: type: string kb_count: type: integer SemanticSearchAiResponse: type: object additionalProperties: false description: | Payload returned by the AI retrieval service for a semantic search (embedded in `searchResponse`). Optional `virtual_to_record_map` maps each virtual record id (string key) to one resolved graph record document. Empty responses from the retrieval layer omit `virtual_to_record_map`; success payloads may include it alongside hits and records. required: - searchResults - records - status - status_code - message properties: searchResults: type: array items: $ref: '#/components/schemas/SemanticSearchHit' records: type: array items: $ref: '#/components/schemas/SemanticSearchGraphRecord' status: type: string status_code: type: integer message: type: string appliedFilters: $ref: '#/components/schemas/SemanticSearchAppliedFilters' omittedPassages: type: object description: | Present only when some records lost passages to the per-record cap: maps a `recordId` to how many of its passages were left out. Each record keeps at most `max(SEARCH_PER_RECORD_FLOOR, limit / SEARCH_PER_RECORD_DIVISOR)` passages (4 at the default limit): the passages that matched first, then those nearest a match. additionalProperties: type: integer minimum: 1 virtual_to_record_map: type: object description: Maps virtual record id (object property name) to the accessible graph record document for that id. additionalProperties: $ref: '#/components/schemas/SemanticSearchGraphRecord' timeline: $ref: '#/components/schemas/SemanticSearchTimeline' outsideDates: $ref: '#/components/schemas/SemanticSearchOutsideDates' searchIncomplete: type: string description: | Present only when part of the search index could not be searched, so the results may miss documents: says how many parts failed, for example `1 of 3 parts of the search index could not be searched, so this search may have missed documents.` SemanticSearchOutsideDates: type: object additionalProperties: false description: | Present only on a search with date bounds, when the same search without them found matches the bounds left out. Date bounds match when a record was written or last changed, not when the event it describes happened, so release notes, a retrospective or a weekly summary written after a period are left out of a search bounded to that period. The search without dates, with the same query, filters and permissions and a limit of 10, runs beside the bounded search (or beside a timeline's searches: one plain search, not a second timeline). Its best matches outside the bounds, up to 3 records, best first, come here, never in `searchResults`, which are unchanged. When nothing inside the bounds matched, the response is then a `200` with empty `searchResults`. A server whose `DATE_FILTER_SAFETY_NET` setting is `false` never sends this field. required: - note - searchResults - records properties: note: type: string description: A plain-language label for these matches, to pass on with them. searchResults: type: array description: Up to 3 passages of each record, best first. items: $ref: '#/components/schemas/SemanticSearchHit' records: type: array description: The records, best first, with their source dates. items: $ref: '#/components/schemas/SemanticSearchGraphRecord' SemanticSearchTimeline: type: object additionalProperties: false description: | Present only on a search run as a timeline: one with `timeline` set, or one over a span of more than 31 days with it left out. `searchResults` then come period by period, oldest first, and by record date within a period; each hit's `timelinePeriod` is its period's index in `periods`. required: - dateField - start - end - periods - notes properties: dateField: type: string enum: - created - modified automatic: type: boolean description: | `true` when the caller left `timeline` out and the span was long enough to run as a timeline; `false` when the caller asked for it. start: type: string format: date description: First day of the span searched. end: type: string format: date description: Last day of the span searched, inclusive. periods: type: array items: $ref: '#/components/schemas/SemanticSearchTimelinePeriod' notes: type: array items: type: string description: Plain-language notes on the span, such as the start chosen when none was given. SemanticSearchTimelinePeriod: type: object additionalProperties: false required: - label - start - end - records - matches properties: label: type: string example: March 2026 (2026-03-01 to 2026-03-31) start: type: string format: date nullable: true description: First day of the period. `null` for the last bucket, of matches whose record has no source date. end: type: string format: date nullable: true records: type: integer minimum: 0 description: Records with matches in this period. `0` may only mean nothing matched this query then. matches: type: integer minimum: 0 searchFailed: type: boolean description: This period's own search failed; any matches shown come from the search of the whole span. searchIncomplete: type: string description: The search reached only part of the index for this period, so a record missing here may still exist. SemanticSearchExecuteResponse: type: object additionalProperties: false description: | Immediate POST `/search` response: persisted search id plus the raw retrieval payload. `searchId` is absent when the request set `saveHistory` to `false`. SDK-oriented modeling: named fields only at this level; dynamic-key maps inside `searchResponse` use `additionalProperties` with a `$ref` (e.g. `virtual_to_record_map`) rather than boolean `additionalProperties: true`, so generated clients retain typed values where possible. required: - searchResponse properties: searchId: type: string format: objectId description: The saved search, for `GET /search/{searchId}`. Absent when `saveHistory` is `false`. searchResponse: $ref: '#/components/schemas/SemanticSearchAiResponse' PersistedSemanticSearchBoundingBox: type: object additionalProperties: false description: | Bounding box subdocument embedded in persisted citation metadata. `boundingBoxSchema` does not set `_id: false`, so Mongoose auto-injects an `_id`. required: - _id - x - y properties: _id: type: string format: objectId x: type: number y: type: number PersistedSemanticSearchCitationMetadata: type: object additionalProperties: false description: | Citation metadata as persisted in MongoDB. Required fields mirror the Mongoose schema's `required: true` flags; the rest are optional and may come through as `null` because the AI retrieval service emits explicit nulls for absent fields. required: - orgId - mimeType - recordId - recordName - origin properties: orgId: type: string mimeType: type: string recordId: type: string recordName: type: string origin: type: string recordVersion: type: integer nullable: true extension: type: string nullable: true webUrl: type: string nullable: true previewRenderable: type: boolean nullable: true hideWeburl: type: boolean nullable: true connector: type: string nullable: true connectorId: type: string nullable: true description: | The connector instance the record came from. `connector` names only the kind of source (for example `SLACK`), which several instances can share. Absent on citations saved before this field was stored. connectorInstanceName: type: string nullable: true description: | The name the connector instance had when the answer was written. Absent on citations saved before this field was stored. recordType: type: string nullable: true blockNum: type: array nullable: true items: type: number nullable: true pageNum: type: array nullable: true items: type: number nullable: true sheetNum: type: number nullable: true sheetName: type: string nullable: true bounding_box: type: array nullable: true items: $ref: '#/components/schemas/PersistedSemanticSearchBoundingBox' blockType: type: string nullable: true description: | Block type for this citation. Common values: `text`, `image`, `table_row`, `table`, `record_summary` (whole-record semantic summary chunk). blockText: type: string nullable: true departments: type: array nullable: true items: type: string languages: type: array nullable: true items: type: string topics: type: array nullable: true items: type: string updatedAt: type: string format: date-time nullable: true description: When the cited record was last updated at its source. PersistedSemanticSearchCitation: type: object additionalProperties: false description: | Populated citation document referenced from a persisted search. The controller strips `__v` via `populate({ select: '-__v' })`. required: - _id - content - chunkIndex - citationType - metadata - createdAt - updatedAt properties: _id: type: string format: objectId content: type: string chunkIndex: type: integer citationType: type: string metadata: $ref: '#/components/schemas/PersistedSemanticSearchCitationMetadata' createdAt: type: string format: date-time updatedAt: type: string format: date-time PersistedSemanticSearchSharedWithEntry: type: object additionalProperties: false description: | Entry inside `sharedWith[]`: one user the search is shared with and the access level they were given. required: - userId - accessLevel properties: _id: type: string format: objectId description: | Identifier of this share entry, generated when the entry is stored. It identifies the entry, not the user — use `userId` for the user. userId: type: string format: objectId description: User the search is shared with. accessLevel: type: string enum: - read - write description: Access granted to that user. PersistedSemanticSearch: type: object additionalProperties: false description: | Persisted search document as returned to clients. `records` is a string-valued map: each value is `JSON.stringify()`, keyed by the source record's `_id` or `_key`. Clients must `JSON.parse` each value to recover the underlying record object (whose shape resembles `SemanticSearchGraphRecord`). This intentionally differs from the `searchResponse.records` array on the POST `/search` response, which is passed through from the retrieval service untouched. required: - _id - __v - query - limit - orgId - userId - citationIds - records - isShared - sharedWith - isArchived - createdAt - updatedAt properties: _id: type: string format: objectId __v: type: integer query: type: string limit: type: integer orgId: type: string format: objectId userId: type: string format: objectId citationIds: type: array description: | For a search shared with the viewer, a hit the viewer cannot read is a `RedactedSource`. items: anyOf: - $ref: '#/components/schemas/PersistedSemanticSearchCitation' - $ref: '#/components/schemas/RedactedSource' records: type: object description: | Map of source-record id (or `_key`) to a JSON-stringified record object. Clients must `JSON.parse` each value before reading fields. additionalProperties: type: string isShared: type: boolean shareLink: type: string description: Set once the search has been shared via `/search/{searchId}/share`. sharedWith: type: array items: $ref: '#/components/schemas/PersistedSemanticSearchSharedWithEntry' isArchived: type: boolean archivedBy: type: string format: objectId nullable: true description: | User ID of the last user who archived this row, or `null` after an unarchive cleared the archive state. Absent on rows that have never been archived. Currently-archived rows cannot reach this endpoint because `buildFilter` enforces `isArchived: false`. createdAt: type: string format: date-time updatedAt: type: string format: date-time PersistedSemanticSearchEnvelope: type: array description: | GET `/search/{searchId}` calls `Model.find()` (not `findOne()`) and sends the result as-is, so the wire format is an array of zero or one persisted search docs. A non-existent id returns `200 []`, **not** `404`. items: $ref: '#/components/schemas/PersistedSemanticSearch' SemanticSearchHistoryItem: type: object additionalProperties: false description: | One persisted search row as returned by `GET /search`. Mirrors the persisted search document except `citationIds` is an array of ObjectId strings (citations are not populated on the list endpoint, unlike `GET /search/{searchId}`). `shareLink` is absent from the JSON when the search has not been shared. `archivedBy` is `null` on rows that were previously archived and then unarchived, and absent on rows that have never been archived. The list endpoint already filters out currently-archived rows, so a string user-id value never appears here in practice. required: - _id - __v - query - limit - orgId - userId - citationIds - records - isShared - sharedWith - isArchived - createdAt - updatedAt properties: _id: type: string format: objectId __v: type: integer query: type: string limit: type: integer orgId: type: string format: objectId userId: type: string format: objectId citationIds: type: array items: type: string format: objectId description: | Hit ids. For a search shared with the viewer the ids are not returned (they cannot be checked against the viewer's access), so the array is empty. records: type: object description: | Map of source-record id (or `_key`) to `JSON.stringify()`. Clients must `JSON.parse` each value to recover the underlying record object. Populated at write-time in the POST `/search` handler. additionalProperties: type: string isShared: type: boolean shareLink: type: string description: Set once the search has been shared via `/search/{searchId}/share`. sharedWith: type: array items: $ref: '#/components/schemas/PersistedSemanticSearchSharedWithEntry' isArchived: type: boolean archivedBy: type: string format: objectId nullable: true description: | User ID of the last user who archived this row, or `null` after an unarchive cleared the archive state. Absent on rows that have never been archived. createdAt: type: string format: date-time updatedAt: type: string format: date-time SemanticSearchHistoryPagination: type: object additionalProperties: false description: | Pagination block emitted by `buildPaginationMetadata` (utils.ts:417). `totalPages` is `Math.ceil(totalCount / limit)`, so an empty result has `totalPages: 0`, not `1`. required: - page - limit - totalCount - totalPages - hasNextPage - hasPrevPage properties: page: type: integer limit: type: integer totalCount: type: integer totalPages: type: integer hasNextPage: type: boolean hasPrevPage: type: boolean SemanticSearchHistoryAppliedDateRange: type: object additionalProperties: false description: | Echoed back only when the caller passed `startDate` and/or `endDate`. Each bound is an ISO 8601 string when set; the field is absent when the corresponding query param was omitted (utils.ts:480-486 reads `appliedFilters.createdAt.$gte?.toISOString()` directly, so missing bounds become `undefined` and drop out of the JSON). properties: start: type: string format: date-time end: type: string format: date-time SemanticSearchHistoryFiltersApplied: type: object additionalProperties: false description: | Echo of which filters the caller actually supplied, built by `buildFiltersMetadata` (utils.ts:430-486). `page` and `limit` always appear because they are normalised to defaults before being recorded, so `filters` is never empty and `values` always contains at least `{ page, limit }`. Other keys appear only when the matching query param was non-empty (or, for `dateRange`, when `createdAt` was set on the Mongo filter). `values` keys are scalar strings rather than typed primitives (`'true'`/`'false'`, `'5'`, etc.) because they are passed through from `req.query` as Express parsed them — only `page` and `limit` are coerced to integers via `safeParsePagination`. required: - filters - values properties: filters: type: array items: type: string enum: - page - limit - search - shared - tags - minMessages - sortBy - sortOrder - startDate - endDate - messageType - dateRange values: type: object additionalProperties: false properties: page: type: integer limit: type: integer search: type: string shared: type: string tags: type: string minMessages: type: string sortBy: type: string sortOrder: type: string startDate: type: string endDate: type: string messageType: type: string dateRange: $ref: '#/components/schemas/SemanticSearchHistoryAppliedDateRange' SemanticSearchHistoryFilterToggle: type: object additionalProperties: false description: | Generic "filter X is available, current value is Y" block used for `shared`, `tags`, `minMessages`, `search`, and `messageType`. Either `type` (free-form value) or `values` (enum of allowed strings) is present, not both. `current` is the caller-supplied value passed through from `req.query`, hence string-or-null even when `type` is `'number'`. required: - description - current - applied properties: type: type: string values: type: array items: type: string description: type: string current: type: string nullable: true applied: type: boolean SemanticSearchHistoryPaginationField: type: object additionalProperties: false required: - type - current - min - max - default - description - applied properties: type: type: string current: type: integer min: type: integer max: type: integer default: type: integer description: type: string applied: type: boolean SemanticSearchHistorySortField: type: object additionalProperties: false description: | Used for `available.sorting.{sortBy,sortOrder}` and `available.sortingMessages.{sortBy,sortOrder}`. The `applied` flag is present on `sorting.*` and absent on `sortingMessages.*`, so it is optional here. required: - values - default - description - current properties: values: type: array items: type: string default: type: string description: type: string current: type: string applied: type: boolean SemanticSearchHistoryDateRange: type: object additionalProperties: false required: - type - description - format - current - applied properties: type: type: string description: type: string format: type: string current: type: object additionalProperties: false required: - start - end properties: start: type: string nullable: true end: type: string nullable: true applied: type: boolean SemanticSearchHistoryFiltersAvailable: type: object additionalProperties: false description: | Catalogue of filters the endpoint supports, plus their current values and `applied` flags. Built by `buildFiltersMetadata` (utils.ts:430-624). required: - shared - tags - minMessages - search - pagination - sorting - dateFilters - messageFilters - sortingMessages properties: shared: $ref: '#/components/schemas/SemanticSearchHistoryFilterToggle' tags: $ref: '#/components/schemas/SemanticSearchHistoryFilterToggle' minMessages: $ref: '#/components/schemas/SemanticSearchHistoryFilterToggle' search: $ref: '#/components/schemas/SemanticSearchHistoryFilterToggle' pagination: type: object additionalProperties: false required: - page - limit properties: page: $ref: '#/components/schemas/SemanticSearchHistoryPaginationField' limit: $ref: '#/components/schemas/SemanticSearchHistoryPaginationField' sorting: type: object additionalProperties: false required: - sortBy - sortOrder properties: sortBy: $ref: '#/components/schemas/SemanticSearchHistorySortField' sortOrder: $ref: '#/components/schemas/SemanticSearchHistorySortField' dateFilters: type: object additionalProperties: false required: - dateRange properties: dateRange: $ref: '#/components/schemas/SemanticSearchHistoryDateRange' messageFilters: type: object additionalProperties: false required: - messageType properties: messageType: $ref: '#/components/schemas/SemanticSearchHistoryFilterToggle' sortingMessages: type: object additionalProperties: false required: - sortBy - sortOrder properties: sortBy: $ref: '#/components/schemas/SemanticSearchHistorySortField' sortOrder: $ref: '#/components/schemas/SemanticSearchHistorySortField' SemanticSearchHistoryFilters: type: object additionalProperties: false required: - applied - available properties: applied: $ref: '#/components/schemas/SemanticSearchHistoryFiltersApplied' available: $ref: '#/components/schemas/SemanticSearchHistoryFiltersAvailable' SemanticSearchHistoryMeta: type: object additionalProperties: false description: | `requestId` comes from `req.context?.requestId` and is omitted from the JSON when upstream middleware did not set it. required: - timestamp - duration properties: requestId: type: string timestamp: type: string format: date-time duration: type: integer SemanticSearchHistoryResponse: type: object additionalProperties: false description: | Envelope returned by `GET /search`. The handler runs `find()` plus `countDocuments()` in parallel and assembles `{ searchHistory, pagination, filters, meta }` (es_controller.ts:3925-3973). required: - searchHistory - pagination - filters - meta properties: searchHistory: type: array items: $ref: '#/components/schemas/SemanticSearchHistoryItem' pagination: $ref: '#/components/schemas/SemanticSearchHistoryPagination' filters: $ref: '#/components/schemas/SemanticSearchHistoryFilters' meta: $ref: '#/components/schemas/SemanticSearchHistoryMeta' AgentKnowledgeFiltersParsed: type: object additionalProperties: false description: | What a knowledge source is limited to. With both lists empty the agent searches the whole source; otherwise it searches only the listed record groups, folders and records, each with everything under it, and a selection made in a chat must lie under them. On GET, `filtersParsed` is this shape parsed from the stored `filters` JSON string. On create and update, `filters` may be this object or its JSON text. The two lists must hold valid UUIDs, at most 200 across both; a value that cannot be read is refused with 400 rather than saved as "no limit", and so is a limit that covers more nodes than one search may (50,000 by default). A stored limit that cannot be read limits the source to nothing. properties: recordGroups: type: array items: type: string description: Record-group ids (e.g. Confluence spaces, Jira projects), each with the groups nested in it. Not set for KB (Collection) entries — a KB is identified by its own `connectorId`; an id equal to a source's `connectorId` is ignored. records: type: array items: type: string description: Folder or record ids, each with everything under it. nodes: type: array description: Names of the ids above, for display. Not used for search. items: type: object additionalProperties: false required: - id - name - nodeType properties: id: type: string name: type: string nodeType: type: string description: '`recordGroup`, `folder` or `record`.' Toolset: type: object additionalProperties: false description: | Toolset instance linked to an agent, as projected by the graph store on `GET /agents/{agentKey}` and `GET /agents`. Multiple instances of the same integration type are distinguished by `instanceId` and optional `instanceName`. properties: _key: type: string description: Toolset instance node key in the backing graph store. name: allOf: - $ref: '#/components/schemas/AgentCreateToolsetName' description: Integration / toolset type key. displayName: type: string description: Human-readable toolset product label (for example `Jira` or `Slack`). type: type: string instanceId: type: string nullable: true description: | Admin-created toolset instance id. `null` when the toolset was attached to the agent without an instance. instanceName: type: string nullable: true description: | Human-readable instance label (e.g. sidebar instance name). `null` when the toolset was attached without an instance. selectedTools: type: array nullable: true description: | Tool names explicitly selected for this toolset instance, when the instance was created with a subset selection. `null`/absent when the instance exposes all of the toolset's tools. items: type: string tools: type: array items: type: object additionalProperties: false properties: _key: type: string description: Tool node key in the backing graph store. name: type: string fullName: type: string toolsetName: type: string description: Toolset type key the tool belongs to. description: type: string deprecated: type: boolean readOnly: true description: | Server-stamped on `GET /agents/{agentKey}`: `true` when the tool's `fullName` is no longer in the runtime tool registry (its `@tool` was removed). Read-only; ignored on create/update bodies. Not stamped on the `GET /agents` list projection. McpServer: type: object additionalProperties: false description: | MCP server instance linked to an agent, as projected by the graph store on `GET /agents/{agentKey}` and `GET /agents` — same shape as `Toolset`. MCP server nodes carry no secrets, only the attach-time snapshot of `instanceId`/`typeId`/`name`. properties: _key: type: string description: MCP server instance node key in the backing graph store. name: type: string description: MCP server attachment name (attach-time snapshot). displayName: type: string description: Human-readable MCP server product label (for example `Jira MCP`). typeId: type: string description: Catalog server type id, when this instance came from a registered template. instanceId: type: string description: Admin-created MCP server instance id. tools: type: array items: type: object additionalProperties: false properties: _key: type: string description: Tool node key in the backing graph store. name: type: string fullName: type: string description: type: string AgentFilters: description: | Knowledge scope filter as stored on the graph edge. The Node `getAgent` handler proxies this field unchanged from the AI service (only `agent.id` is stripped). May be a JSON string (typical graph storage) or an object. Prefer `filtersParsed` on GET for a guaranteed parsed object with the same keys as the object branch below. oneOf: - $ref: '#/components/schemas/AgentKnowledgeFiltersParsed' - type: string description: JSON-encoded filter object (graph storage format). Knowledge: type: object additionalProperties: false description: | Knowledge connector / indexed scope linked to an agent, as projected by the graph store on `GET /agents/{agentKey}` and `GET /agents`. properties: _key: type: string connectorId: type: string name: type: string type: type: string displayName: type: string filters: $ref: '#/components/schemas/AgentFilters' filtersParsed: readOnly: true description: | Server-derived read-only object parsed from the stored `filters` JSON by the graph provider on GET (Neo4j / Arango). Empty object when `filters` is missing or invalid JSON. allOf: - $ref: '#/components/schemas/AgentKnowledgeFiltersParsed' AgentSkill: type: object additionalProperties: false description: | A skill linked to the agent, as returned by the agent detail graph projection. Flat by design — a skill carries no sub-entities analogous to a toolset's tools. Fields other than `name` are read straight off the skill document and are null when unset. required: - name properties: name: type: string description: Unique skill name, used to reference the skill on agent create/update. description: type: string nullable: true category: type: string nullable: true subcategory: type: string nullable: true version: type: string nullable: true status: type: string nullable: true description: Lifecycle state of the skill — `active`, `deprecated`, or `disabled`. `candidate` is a learning-loop record state, not an assignable skill, and is not returned here. Agent: type: object additionalProperties: false description: | Detailed agent projection returned by agent detail-style endpoints such as `GET /agents/{agentKey}`. properties: _id: type: string description: Full document id in the backing graph store. example: agentInstances/e6f848ca-e2ab-4594-9925-e1136629f474 _key: type: string description: Stable agent key used in route params. example: e6f848ca-e2ab-4594-9925-e1136629f474 _rev: type: string description: Backend document revision token. example: _lkNlcOm--- name: type: string description: Display name of the agent example: Customer Support Assistant description: type: string description: What this agent is designed to do systemPrompt: type: string description: System instructions that define agent behavior createdBy: type: string format: objectId description: MongoDB user ID of the agent creator startMessage: type: string description: Initial greeting shown when a conversation with this agent starts instructions: type: string nullable: true description: Additional agent execution instructions models: type: array description: | Configured model entries for this agent. example: - modelType: llm provider: azureOpenAI modelName: gpt-5.6-luna modelKey: f3a4b5b6-5b6c-4e85-9097-3202cfe696fc isMultimodal: true isReasoning: true isDefault: true modelFriendlyName: GPT 5.4 mini items: oneOf: - type: string - type: object additionalProperties: false properties: modelKey: type: string modelName: type: string provider: type: string isReasoning: type: boolean isMultimodal: type: boolean isDefault: type: boolean modelType: type: string description: | Model category. Must be `llm` for agent model entries (same value as `ModelType` for LLMs; string only — enum is not used here). example: llm modelFriendlyName: type: string defaultReasoningEffort: type: string enum: - none - low - medium - high - max description: The model's own reasoning effort, read from its stored configuration. It applies only when the chat request sets no `reasoningEffort` and the agent has no `defaultReasoningEffort`. Omitted when nothing valid is stored; `none` is a legacy value that runs as `low`. example: low usesOrgDefault: type: boolean description: | True when this agent has no models configured and will use the organization's default LLM (marked `isDefault: true` in the AI models configuration) at chat time. Derived from `models` being empty — not persisted separately. toolsets: type: array description: | Toolset instances linked to the agent (GET /agents/{agentKey} graph projection). Multiple instances of the same integration type are distinguished by `instanceId` and optional `instanceName`. items: $ref: '#/components/schemas/Toolset' mcpServers: type: array description: | MCP server instances linked to the agent (GET /agents/{agentKey} graph projection). Same shape/semantics as `toolsets`, keyed by `instanceId`. items: $ref: '#/components/schemas/McpServer' knowledge: type: array description: Knowledge connectors and indexed scopes linked to the agent items: $ref: '#/components/schemas/Knowledge' skills: type: array description: Skills linked to the agent via `agentHasSkill` edges items: $ref: '#/components/schemas/AgentSkill' shareWithOrg: type: boolean description: Whether the agent is shared with the whole organization webSearch: type: object additionalProperties: false nullable: true description: Web search provider attached to this agent. Null when none is configured. properties: provider: type: string description: Provider identifier (e.g. "tavily", "serper", "exa", "duckduckgo") providerKey: type: string providerLabel: type: string required: - provider example: provider: serper defaultReasoningEffort: type: string nullable: true enum: - none - low - medium - high - max description: Agent-level reasoning effort used when a chat request omits its own. Null when unset. apiToolsetTools: type: array maxItems: 500 description: | API toolset operations the agent may call, each `:` (see `/api-toolsets`). A service-account agent may only use toolsets with a shared credential. items: type: string pattern: ^[0-9a-f]{32}:[A-Za-z0-9_-]{1,64}$ sendUserContext: type: boolean description: | When true (default), the agent's system prompt includes the current user's name, email, and organization. When false, the agent relies on tools, actions, and knowledge sources without that profile data. tags: type: array items: type: string description: Free-form agent tags. createdAtTimestamp: type: integer format: int64 description: Unix epoch timestamp in milliseconds when the agent was created. updatedAtTimestamp: type: integer format: int64 description: Unix epoch timestamp in milliseconds when the agent was last updated. updatedBy: type: string nullable: true description: User id of the last updater, if present. isActive: type: boolean description: Whether the agent is active. isDeleted: type: boolean description: Whether the agent has been soft-deleted. isServiceAccount: type: boolean description: Whether this agent is a service-account agent. serviceAccountUserId: type: string nullable: true description: | The `userId` of the service account this agent searches knowledge as, or null when it searches as its creator. Only set on a service-account agent. access_type: type: string description: How the user can access this agent. example: INDIVIDUAL user_role: type: string description: Effective role of the current user on this agent. example: OWNER can_view: type: boolean description: Effective permission to view the agent. can_share: type: boolean description: Effective permission to share the agent. can_edit: type: boolean description: Effective permission to edit the agent. can_delete: type: boolean description: Effective permission to delete the agent. required: - _id - _key - createdAtTimestamp - createdBy - isActive - isDeleted - isServiceAccount - models - name - tags - updatedAtTimestamp - shareWithOrg - knowledge - toolsets - mcpServers - skills - can_view - can_share - can_edit - can_delete - user_role - access_type AgentListItem: type: object additionalProperties: false description: | Agent projection returned by `GET /agents`. This is the list-view envelope item emitted by the Python backend and forwarded by the Node gateway. It is not the full detail projection used by `GET /agents/{agentKey}`. properties: _id: type: string description: Full document id in the backing graph store. example: agentInstances/e6f848ca-e2ab-4594-9925-e1136629f474 _key: type: string description: Stable agent key used in route params. example: e6f848ca-e2ab-4594-9925-e1136629f474 _rev: type: string description: Backend document revision token. example: _lkNlcOm--- createdAtTimestamp: type: integer format: int64 description: Unix epoch timestamp in milliseconds when the agent was created. createdBy: type: string description: MongoDB user ID of the agent creator description: type: string nullable: true description: Short human-readable description of the agent. instructions: type: string nullable: true description: Additional execution instructions stored on the agent. isActive: type: boolean description: Whether the agent is active. isDeleted: type: boolean description: Whether the agent has been soft-deleted. isServiceAccount: type: boolean description: Whether this agent is a service-account agent. serviceAccountUserId: type: string nullable: true description: | The `userId` of the service account this agent searches knowledge as, or null when it searches as its creator. Only set on a service-account agent. models: type: array description: | Model entries configured on the agent. For `GET /agents`, the backend returns the stored normalized string representation, typically `modelKey_modelName`. items: type: string name: type: string description: Display name of the agent. startMessage: type: string nullable: true description: Greeting shown at conversation start. systemPrompt: type: string nullable: true description: System prompt stored on the agent. tags: type: array items: type: string description: Free-form agent tags. updatedAtTimestamp: type: integer format: int64 description: Unix epoch timestamp in milliseconds when the agent was last updated. updatedBy: type: string nullable: true description: User id of the last updater, if present. usesOrgDefault: type: boolean description: | True when this agent has no models configured and will use the organization's default LLM (marked `isDefault: true` in the AI models configuration) at chat time. Derived from `models` being empty — not persisted separately. webSearch: type: object additionalProperties: false nullable: true description: | Web-search provider attachment for this agent, or `null` when none is attached. For `GET /agents`, the response formatter always emits `provider`. It may also emit `providerKey` and `providerLabel` when those values were present on the stored attachment. It does not emit `iconPath` on this response path. properties: provider: type: string providerKey: type: string providerLabel: type: string defaultReasoningEffort: type: string nullable: true enum: - none - low - medium - high - max description: Agent-level reasoning effort used when a chat request omits its own. Null when unset. apiToolsetTools: type: array maxItems: 500 description: | API toolset operations the agent may call, each `:` (see `/api-toolsets`). A service-account agent may only use toolsets with a shared credential. items: type: string pattern: ^[0-9a-f]{32}:[A-Za-z0-9_-]{1,64}$ sendUserContext: type: boolean description: When false, this agent omits user name/email/org from its system prompt. shareWithOrg: type: boolean description: Whether the agent is shared with the organization. toolsets: type: array description: | Toolset instances linked to the agent. Same projection as `GET /agents/{agentKey}`; the backend builds it from the graph edges for each agent on the returned page. items: $ref: '#/components/schemas/Toolset' mcpServers: type: array description: | MCP server instances linked to the agent. Same projection as `GET /agents/{agentKey}`; the backend builds it from the graph edges for each agent on the returned page. items: $ref: '#/components/schemas/McpServer' knowledge: type: array description: | Knowledge connectors and indexed scopes linked to the agent. Same projection as `GET /agents/{agentKey}`; the backend builds it from the graph edges for each agent on the returned page. items: $ref: '#/components/schemas/Knowledge' can_view: type: boolean description: Effective permission to view the agent. can_share: type: boolean description: Effective permission to share the agent. can_edit: type: boolean description: Effective permission to edit the agent. can_delete: type: boolean description: Effective permission to delete the agent. user_role: type: string description: Effective role of the current user on this agent. example: OWNER access_type: type: string description: How the user can access this agent. example: INDIVIDUAL required: - _id - _key - createdAtTimestamp - createdBy - isActive - isDeleted - isServiceAccount - models - name - tags - updatedAtTimestamp - shareWithOrg - toolsets - mcpServers - knowledge - can_view - can_share - can_edit - can_delete - user_role - access_type AgentListPagination: type: object additionalProperties: false description: Pagination block returned by `GET /agents`. required: - currentPage - limit - totalItems - totalPages - hasNext - hasPrev properties: currentPage: type: integer description: Current 1-based page number. example: 1 limit: type: integer description: Page size actually applied by the backend. example: 20 totalItems: type: integer description: Total number of matching agents across all pages. example: 2 totalPages: type: integer description: Total number of pages for the current query. example: 1 hasNext: type: boolean description: Whether a later page exists. example: false hasPrev: type: boolean description: Whether an earlier page exists. example: false AgentListResponse: type: object additionalProperties: false description: | Paginated response returned by `GET /agents`. The Node gateway forwards the Python backend response on success. If the backend returns a non-200 response, the gateway still returns HTTP 200 with `success: true`, an empty `agents` array, and a zeroed pagination block derived from the requested `page` / `limit`. required: - success - agents - pagination properties: success: type: boolean example: true agents: type: array items: $ref: '#/components/schemas/AgentListItem' pagination: $ref: '#/components/schemas/AgentListPagination' GetAgentResponse: type: object additionalProperties: false description: | Success envelope returned by `GET /agents/{agentKey}`. The Node gateway forwards the backend response as an envelope with a top-level status/message and the detailed agent projection nested under `agent`. required: - status - message - agent properties: status: type: string example: success message: type: string example: Agent retrieved successfully agent: $ref: '#/components/schemas/Agent' AgentCreateModelEntry: description: | Accepted model entry for `POST /agents/create`. The gateway accepts either a non-empty string model entry or an object entry with a required `modelKey`. The `models` array itself is optional and may be empty (the agent then uses the organization's default LLM). When the array is non-empty, it must include at least one object entry with `isReasoning: true`. String-only entries are schema-valid but, if present without any reasoning-flagged object entry, are rejected at the gateway with HTTP 400. oneOf: - type: string minLength: 1 pattern: \S - type: object additionalProperties: false required: - modelKey properties: modelKey: type: string minLength: 1 pattern: \S description: Surrounding whitespace is trimmed; a blank key is refused with `400`. modelName: type: string provider: type: string isReasoning: type: boolean AgentCreateToolRef: type: object additionalProperties: false required: - name properties: name: type: string minLength: 1 pattern: \S description: Surrounding whitespace is trimmed; a blank name is refused with `400`. fullName: type: string minLength: 1 pattern: \S description: Surrounding whitespace is trimmed; a blank value is refused with `400`. description: type: string maxLength: 10000 AgentCreateToolsetName: type: string description: Registered toolset name (lowercase) accepted by the create-agent gateway. enum: - calendar - clickup - confluence - confluencedatacenter - docs - drive - github - gmail - jira - jiradatacenter - lumos - mariadb - onedrive - outlook - redshift - salesforce - sharepoint - sheets - slack - teams - zoom AgentCreateToolset: type: object additionalProperties: false required: - name properties: name: allOf: - $ref: '#/components/schemas/AgentCreateToolsetName' description: | Toolset name. Surrounding whitespace is trimmed before the check, so `" slack "` is accepted. displayName: type: string maxLength: 200 type: type: string maxLength: 100 instanceId: type: string maxLength: 256 instanceName: type: string maxLength: 200 tools: type: array items: $ref: '#/components/schemas/AgentCreateToolRef' AgentCreateKnowledge: type: object additionalProperties: false required: - connectorId properties: connectorId: type: string minLength: 1 pattern: \S description: Surrounding whitespace is trimmed; a blank id is refused with `400`. filters: description: | The source's limit, as an object or its JSON text. An empty string or an empty array means no limit. A non-empty array, `null`, or text that is not the JSON of an object is refused with `400` (`VALIDATION_ERROR`, "Knowledge filters must be an object, with recordGroups and records as lists of ids"). oneOf: - $ref: '#/components/schemas/AgentKnowledgeFiltersParsed' - type: string - type: array maxItems: 0 items: {} AgentCreateWebSearch: description: | Web-search attachment for an agent. Accepts a provider string, an object with at least a `provider` field, or `null`. The provider string and `provider` are trimmed; an empty or blank value is refused with `400`. anyOf: - type: string nullable: true minLength: 1 pattern: \S - type: object additionalProperties: false properties: provider: type: string minLength: 1 pattern: \S providerKey: type: string maxLength: 256 providerLabel: type: string maxLength: 200 iconPath: type: string maxLength: 500 required: - provider AgentSkillAssignment: type: object additionalProperties: false description: Reference to an existing skill assigned to an agent. required: - name properties: name: type: string minLength: 1 maxLength: 64 pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$ description: Lowercase skill name using single hyphens between segments. AgentCreateRequest: type: object additionalProperties: false description: | Body of `POST /agents/create`. Fields the gateway validator does not know (at the top level and inside `models[]`, `webSearch`, `knowledge[]`, `toolsets[]` and `mcpServers[]`) are dropped before the request reaches the AI backend; they are not refused. required: - name properties: name: type: string minLength: 1 maxLength: 200 pattern: \S description: | Agent display name. Surrounding whitespace is trimmed; a blank name is refused with `400` (`VALIDATION_ERROR`, "Name is required"). example: Product Support Agent description: type: string maxLength: 100000 description: What the agent does startMessage: type: string maxLength: 100000 description: Initial greeting shown when conversation starts systemPrompt: type: string maxLength: 100000 description: System instructions for the agent instructions: type: string maxLength: 100000 description: Additional agent execution instructions models: type: array minItems: 0 default: [] description: | Agent model configuration entries. Optional — an agent created without any models (an empty array or an omitted field) uses the organization's default LLM at chat time. A non-empty list must hold at least one object entry with `isReasoning: true`; otherwise the gateway answers `400` (`VALIDATION_ERROR` on `models`, "At least one reasoning model is required"). items: $ref: '#/components/schemas/AgentCreateModelEntry' tags: type: array maxItems: 50 items: type: string maxLength: 100 shareWithOrg: type: boolean default: false description: Share agent with the organization isServiceAccount: type: boolean default: false description: Create the agent as a service-account agent serviceAccountUserId: type: string description: | The `userId` of the service account this agent should search knowledge as, as returned by the service accounts API. Only valid on a service-account agent: sending it without `isServiceAccount` is refused rather than ignored, so a request cannot quietly create an agent that searches as its creator. Omit it and the agent searches as its creator. Whitespace around it is removed first; 1-128 characters. minLength: 1 maxLength: 128 toolsets: type: array maxItems: 100 description: Toolsets attached to the agent (instance-aware) items: $ref: '#/components/schemas/AgentCreateToolset' knowledge: type: array maxItems: 100 description: Knowledge sources connected to the agent items: $ref: '#/components/schemas/AgentCreateKnowledge' skills: type: array maxItems: 100 description: Existing skills to assign to the agent items: $ref: '#/components/schemas/AgentSkillAssignment' mcpServers: type: array maxItems: 50 description: | MCP server instances attached to the agent. Two entries with the same `typeId` are refused with `400` (`VALIDATION_ERROR` on `mcpServers..typeId`); entries without a `typeId` are exempt. The instance is not looked up when the agent is saved. items: $ref: '#/components/schemas/AgentMcpServerRef' webSearch: $ref: '#/components/schemas/AgentCreateWebSearch' defaultReasoningEffort: type: string nullable: true enum: - none - low - medium - high - max description: Agent-level reasoning effort used when a chat request omits its own. apiToolsetTools: type: array maxItems: 500 description: | API toolset operations the agent may call, each `:` (see `/api-toolsets`). A service-account agent may only use toolsets with a shared credential. items: type: string pattern: ^[0-9a-f]{32}:[A-Za-z0-9_-]{1,64}$ sendUserContext: type: boolean default: true description: | When true (default), include the current user's name, email, and organization in this agent's system prompt. When false, omit that profile data. AgentCreateWarning: type: object additionalProperties: false properties: name: type: string error: type: string AgentCreateResponseTool: type: object additionalProperties: false required: - name - fullName - key properties: name: type: string fullName: type: string key: type: string AgentCreateResponseToolset: type: object additionalProperties: false required: - name - displayName - key - tools properties: name: $ref: '#/components/schemas/AgentCreateToolsetName' displayName: type: string description: Human-readable toolset product label (for example `Jira` or `Slack`). key: type: string tools: type: array items: $ref: '#/components/schemas/AgentCreateResponseTool' AgentCreateResponseMcpServerTool: type: object additionalProperties: false required: - name - fullName - key properties: name: type: string fullName: type: string key: type: string AgentCreateResponseMcpServer: type: object additionalProperties: false required: - name - displayName - key - tools properties: name: type: string displayName: type: string description: Human-readable MCP server product label (for example `Jira MCP`). key: type: string tools: type: array items: $ref: '#/components/schemas/AgentCreateResponseMcpServerTool' AgentCreateResponseKnowledge: type: object additionalProperties: false required: - connectorId - key - filters properties: connectorId: type: string key: type: string filters: oneOf: - type: object additionalProperties: true - type: string - type: array items: {} AgentCreateResponseSkill: type: object additionalProperties: false description: | A skill linked to the agent at creation time. Creating or updating an agent only links edges to skills that already exist and never writes a skill document, so only the name is echoed back here. required: - name properties: name: type: string AgentCreateResponseAgent: type: object additionalProperties: false required: - _key - name - description - startMessage - systemPrompt - instructions - models - tags - webSearch - isActive - isServiceAccount - createdBy - updatedBy - createdAtTimestamp - updatedAtTimestamp - isDeleted - toolsets - mcpServers - knowledge - skills properties: _key: type: string name: type: string description: type: string startMessage: type: string systemPrompt: type: string instructions: type: string nullable: true models: type: array items: type: string tags: type: array items: type: string webSearch: anyOf: - type: object nullable: true additionalProperties: false properties: provider: type: string providerKey: type: string providerLabel: type: string defaultReasoningEffort: type: string nullable: true enum: - none - low - medium - high - max description: Agent-level reasoning effort used when a chat request omits its own. Null when unset. apiToolsetTools: type: array maxItems: 500 description: | API toolset operations the agent may call, each `:` (see `/api-toolsets`). A service-account agent may only use toolsets with a shared credential. items: type: string pattern: ^[0-9a-f]{32}:[A-Za-z0-9_-]{1,64}$ sendUserContext: type: boolean description: When false, this agent omits user name/email/org from its system prompt. isActive: type: boolean isServiceAccount: type: boolean serviceAccountUserId: type: string nullable: true description: | The `userId` of the service account this agent searches knowledge as, or null when it searches as its creator. Only set on a service-account agent. createdBy: type: string updatedBy: type: string nullable: true createdAtTimestamp: type: integer updatedAtTimestamp: type: integer isDeleted: type: boolean toolsets: type: array items: $ref: '#/components/schemas/AgentCreateResponseToolset' mcpServers: type: array items: $ref: '#/components/schemas/AgentCreateResponseMcpServer' knowledge: type: array items: $ref: '#/components/schemas/AgentCreateResponseKnowledge' skills: type: array items: $ref: '#/components/schemas/AgentCreateResponseSkill' AgentCreateResponse: type: object additionalProperties: false required: - status - message - agent properties: status: type: string enum: - success - partial_success message: type: string agent: $ref: '#/components/schemas/AgentCreateResponseAgent' warnings: type: array nullable: true items: $ref: '#/components/schemas/AgentCreateWarning' AgentUpdateRequest: type: object additionalProperties: false description: | Partial update payload for `PUT /agents/{agentKey}`. Every field is optional — only the fields present in the request body are updated. `models` may be omitted, set to an empty array to clear the agent's models (reverting it to the organization's default LLM at chat time), or set to a non-empty array. When a non-empty array is provided, the gateway Zod middleware (mirroring the Python backend) requires at least one object entry with `isReasoning: true`. Fields the gateway validator does not know are dropped before the request reaches the AI backend; they are not refused. properties: name: type: string minLength: 1 maxLength: 200 pattern: \S description: | Agent display name. Surrounding whitespace is trimmed; a blank name is refused with `400` (`VALIDATION_ERROR`). example: Renamed Agent description: type: string maxLength: 100000 description: What the agent does startMessage: type: string maxLength: 100000 description: Initial greeting shown when conversation starts systemPrompt: type: string maxLength: 100000 description: System instructions for the agent instructions: type: string maxLength: 100000 description: Additional agent execution instructions models: type: array minItems: 0 description: | Agent model configuration entries. Optional. An empty array clears the agent's models so it falls back to the organization's default LLM. A non-empty list must hold at least one object entry with `isReasoning: true`; otherwise the gateway answers `400` (`VALIDATION_ERROR` on `models`). items: $ref: '#/components/schemas/AgentCreateModelEntry' tags: type: array maxItems: 50 items: type: string maxLength: 100 shareWithOrg: type: boolean default: false description: Share agent with the organization isServiceAccount: type: boolean default: false description: Mark agent as a service account serviceAccountUserId: type: string nullable: true description: | The `userId` of the service account this agent should search knowledge as. Only valid on a service-account agent, checked against what the agent will be once the request is applied, so one request may convert an agent and bind it. Null is accepted on any agent and clears the binding, going back to searching as the agent's creator. Whitespace around it is removed first; 1-128 characters. minLength: 1 maxLength: 128 toolsets: type: array maxItems: 100 description: Toolsets attached to the agent (instance-aware) items: $ref: '#/components/schemas/AgentCreateToolset' knowledge: type: array maxItems: 100 description: Knowledge sources connected to the agent items: $ref: '#/components/schemas/AgentCreateKnowledge' skills: type: array maxItems: 100 description: | Complete replacement set of skills assigned to the agent. Send an empty array to clear all skill assignments. items: $ref: '#/components/schemas/AgentSkillAssignment' mcpServers: type: array maxItems: 50 description: | MCP server instances attached to the agent. Two entries with the same `typeId` are refused with `400` (`VALIDATION_ERROR` on `mcpServers..typeId`); entries without a `typeId` are exempt. items: $ref: '#/components/schemas/AgentMcpServerRef' webSearch: $ref: '#/components/schemas/AgentCreateWebSearch' defaultReasoningEffort: type: string nullable: true enum: - none - low - medium - high - max description: Agent-level reasoning effort used when a chat request omits its own. apiToolsetTools: type: array maxItems: 500 description: | API toolset operations the agent may call, each `:` (see `/api-toolsets`). A service-account agent may only use toolsets with a shared credential. items: type: string pattern: ^[0-9a-f]{32}:[A-Za-z0-9_-]{1,64}$ sendUserContext: type: boolean description: | When true (default), include the current user's name, email, and organization in this agent's system prompt. When false, omit that profile data. AgentUpdateResponse: type: object additionalProperties: false required: - status - message properties: status: type: string enum: - success message: type: string example: Agent updated successfully AgentDeleteResponse: type: object additionalProperties: false required: - status - message - deleted properties: status: type: string enum: - success message: type: string example: Agent deleted successfully deleted: type: object additionalProperties: false required: - agents - toolsets - tools - knowledge - edges properties: agents: type: integer minimum: 0 example: 1 toolsets: type: integer minimum: 0 example: 0 tools: type: integer minimum: 0 example: 0 knowledge: type: integer minimum: 0 example: 0 edges: type: integer minimum: 0 example: 0 AgentConversation: type: object description: | A conversation with a specific AI agent. Similar to regular conversations but tied to an agent's configuration and capabilities. properties: _id: type: string format: objectId agentKey: type: string description: The agent this conversation is with userId: type: string format: objectId orgId: type: string format: objectId title: type: string initiator: type: string format: objectId description: User who started the conversation. messages: type: array items: $ref: '#/components/schemas/Message' status: type: string enum: - None - Inprogress - Complete - Failed - Stopped description: Same values as `Conversation.status`. failReason: type: string description: Error description, populated only when `status` is `Failed`. modelInfo: $ref: '#/components/schemas/ConversationModelInfo' isShared: type: boolean shareLink: type: string description: Shareable link, present only when the conversation is shared. sharedWith: type: array items: type: object properties: userId: type: string accessLevel: type: string enum: - read - write isArchived: type: boolean description: Whether this conversation is archived. archivedBy: type: string format: objectId nullable: true description: User who archived the conversation. Absent on rows that were never archived. isDeleted: type: boolean description: Whether this conversation has been soft-deleted. deletedBy: type: string format: objectId nullable: true description: User who soft-deleted the conversation. Absent on live rows. conversationErrors: type: array description: Errors recorded against this conversation (e.g. failed message generations). items: type: object required: - message properties: _id: type: string format: objectId message: type: string errorType: type: string timestamp: type: string format: date-time messageId: type: string format: objectId stack: type: string metadata: type: object additionalProperties: true description: Free-form map of error context values. metadata: type: object additionalProperties: true description: Free-form metadata map attached to the conversation. conversationSource: type: string enum: - agent_chat description: Always `agent_chat` for agent conversations. compactedSummary: type: string description: | Summary of older turns, present only once the conversation has been compacted. compactedAtTurnIndex: type: integer description: Turn index up to which `compactedSummary` covers the conversation. compactedAtTimestamp: type: integer format: int64 description: Unix timestamp (ms) of the last compaction. lastActivityAt: type: integer createdAt: type: string format: date-time updatedAt: type: string format: date-time __v: type: integer description: Document version counter of the backing store. projectId: type: string format: objectId nullable: true description: The project this agent conversation is linked to, if any. projectVisibility: type: string enum: - private - project nullable: true description: | Only meaningful when `projectId` is set. `project` exposes the conversation to every member of the linked project. AgentConversationListItem: type: object additionalProperties: false description: | Conversation summary returned by `GET /agents/{agentKey}/conversations`. The handler excludes `messages` and `__v` from both result sets. Rows in `sharedWithMeConversations` also omit `sharedWith` because the secondary query explicitly deselects that field before serialization. properties: _id: type: string format: objectId agentKey: type: string description: Agent identifier from the route path userId: type: string format: objectId orgId: type: string format: objectId title: type: string initiator: type: string format: objectId status: type: string enum: - None - Inprogress - Complete - Failed - Stopped failReason: type: string modelInfo: $ref: '#/components/schemas/ConversationModelInfo' isShared: type: boolean shareLink: type: string sharedWith: type: array items: type: object additionalProperties: false properties: _id: type: string format: objectId userId: type: string format: objectId accessLevel: type: string enum: - read - write isArchived: type: boolean archivedBy: type: string format: objectId nullable: true archivedAt: type: string format: date-time description: | Present on archived conversation endpoints. Derived from the document `updatedAt` timestamp when the archive response is built. isDeleted: type: boolean deletedBy: type: string format: objectId nullable: true conversationErrors: type: array items: type: object additionalProperties: false properties: _id: type: string format: objectId description: Sub-document identifier auto-assigned by MongoDB. message: type: string errorType: type: string timestamp: type: string format: date-time messageId: type: string format: objectId stack: type: string metadata: type: object additionalProperties: true conversationSource: type: string enum: - agent_chat lastActivityAt: type: integer format: int64 description: Epoch milliseconds of the latest activity on the thread createdAt: type: string format: date-time updatedAt: type: string format: date-time isOwner: type: boolean readOnly: true description: | Computed per request. `true` when the conversation `initiator` matches the authenticated user. accessLevel: type: string enum: - read - write readOnly: true description: | Computed per request from `sharedWith`; defaults to `read` when no explicit share grant is attached to the serialized row. sharedBy: $ref: '#/components/schemas/ConversationSharedBy' projectId: type: string format: objectId nullable: true description: The project this agent conversation is linked to, if any. projectVisibility: type: string enum: - private - project nullable: true description: | Only meaningful when `projectId` is set. `project` exposes the conversation to every member of the linked project. AgentConversationListResponse: type: object additionalProperties: false description: | Envelope returned by `GET /agents/{agentKey}/conversations`. `conversations` contains rows owned by the caller for the agent; `sharedWithMeConversations` contains rows shared with the caller for the same agent. Both arrays use the same pagination and sort inputs, but `pagination.totalCount` and `totalPages` are computed only from `conversations` because the handler counts the owned-query filter only. required: - conversations - sharedWithMeConversations - pagination - filters - meta properties: conversations: type: array items: $ref: '#/components/schemas/AgentConversationListItem' sharedWithMeConversations: type: array items: $ref: '#/components/schemas/AgentConversationListItem' pagination: $ref: '#/components/schemas/SemanticSearchHistoryPagination' filters: $ref: '#/components/schemas/SemanticSearchHistoryFilters' meta: $ref: '#/components/schemas/SemanticSearchHistoryMeta' AgentArchivedConversationGroup: type: object additionalProperties: false description: | Archived conversations for a single agent, sliced to the first page of the per-agent archive query (limit 5, sorted newest first). required: - agentKey - conversations - pagination properties: agentKey: type: string description: Agent identifier the conversations belong to. conversations: type: array items: $ref: '#/components/schemas/AgentConversationListItem' pagination: $ref: '#/components/schemas/SemanticSearchHistoryPagination' AgentArchivedGroupsResponse: type: object additionalProperties: false description: | Response from `GET /agents/conversations/show/archives` — archived agent conversations grouped by `agentKey`, with agent-level pagination over the groups and a fixed slice of conversations under each agent. required: - groups - agentPagination - meta properties: groups: type: array items: $ref: '#/components/schemas/AgentArchivedConversationGroup' agentPagination: $ref: '#/components/schemas/SemanticSearchHistoryPagination' meta: $ref: '#/components/schemas/RequestMeta' AgentArchivedConversationSummary: type: object additionalProperties: false description: | Archive counts and bounds for the current result page returned by `GET /agents/{agentKey}/conversations/show/archives`. properties: totalArchived: type: integer description: Total archived conversations matching the filter oldestArchive: type: string format: date-time description: Archive timestamp of the first item in the current page. Omitted when the page is empty. newestArchive: type: string format: date-time description: Archive timestamp of the last item in the current page. Omitted when the page is empty. AgentArchivedConversationListResponse: type: object additionalProperties: false description: | Envelope returned by `GET /agents/{agentKey}/conversations/show/archives`. required: - conversations - pagination - filters - summary - meta properties: conversations: type: array items: $ref: '#/components/schemas/AgentConversationListItem' pagination: $ref: '#/components/schemas/SemanticSearchHistoryPagination' filters: $ref: '#/components/schemas/SemanticSearchHistoryFilters' summary: $ref: '#/components/schemas/AgentArchivedConversationSummary' meta: $ref: '#/components/schemas/SemanticSearchHistoryMeta' RequestMeta: type: object additionalProperties: false description: Basic request metadata returned by the API. required: - timestamp - duration properties: requestId: type: string timestamp: type: string format: date-time duration: type: integer ConversationTitleUpdateRequest: type: object additionalProperties: false required: - title properties: title: type: string minLength: 1 maxLength: 200 pattern: \S description: | New title for the conversation. Surrounding whitespace is trimmed; a blank title is refused. example: ABC News Follow-up StoredAgentConversation: type: object additionalProperties: false description: | Stored agent conversation document returned by non-list endpoints. properties: _id: type: string format: objectId agentKey: type: string userId: type: string format: objectId orgId: type: string format: objectId title: type: string initiator: type: string format: objectId messages: type: array items: $ref: '#/components/schemas/Message' status: type: string enum: - None - Inprogress - Complete - Failed - Stopped failReason: type: string modelInfo: $ref: '#/components/schemas/ConversationModelInfo' isShared: type: boolean shareLink: type: string sharedWith: type: array items: type: object additionalProperties: false properties: userId: type: string format: objectId accessLevel: type: string enum: - read - write isArchived: type: boolean archivedBy: type: string format: objectId nullable: true isDeleted: type: boolean deletedBy: type: string format: objectId nullable: true conversationErrors: type: array items: type: object additionalProperties: false properties: _id: type: string format: objectId message: type: string errorType: type: string timestamp: type: string format: date-time messageId: type: string format: objectId stack: type: string metadata: type: object additionalProperties: true conversationSource: type: string enum: - agent_chat lastActivityAt: type: integer format: int64 createdAt: type: string format: date-time updatedAt: type: string format: date-time __v: type: integer AgentConversationTitleUpdateResponse: type: object additionalProperties: false required: - conversation - meta properties: conversation: $ref: '#/components/schemas/StoredAgentConversation' meta: $ref: '#/components/schemas/RequestMeta' AgentConversationDeleteResponse: type: object additionalProperties: false description: | Envelope returned by `DELETE /agents/{agentKey}/conversations/{conversationId}`. When the conversation does not exist, belongs to a different agent, or was already deleted, the API still returns HTTP 200 with `conversation: null`. required: - message - conversation properties: message: type: string enum: - Conversation deleted successfully conversation: anyOf: - $ref: '#/components/schemas/StoredAgentConversation' - type: object nullable: true enum: - null AgentConversationDetailMessageCitation: type: object additionalProperties: false description: | Citation entry returned inside a conversation message after the handler populates `messages.citations.citationId` and rewrites each item to `{ citationId, citationData }`. properties: citationId: type: string format: objectId citationData: $ref: '#/components/schemas/Citation' AgentConversationDetailMessage: type: object additionalProperties: false description: | Message shape returned by `GET /agents/{agentKey}/conversations/{conversationId}`. The response spreads the stored message document and replaces `citations` with populated citation objects. properties: _id: type: string format: objectId messageType: type: string enum: - user_query - bot_response - error - feedback - system - tool_call content: type: string contentFormat: type: string enum: - MARKDOWN - JSON - HTML citations: type: array items: anyOf: - $ref: '#/components/schemas/AgentConversationDetailMessageCitation' - $ref: '#/components/schemas/RedactedSource' confidence: type: string enum: - Very High - High - Medium - Low - Unknown nullable: true description: | AI confidence in the answer. Present only on `bot_response` messages, and only when the model emitted a trailing confidence block. This field is now optional and nullable; it was previously always present and non-nullable. Treat a missing or `null` value as "no confidence reported" and guard before using it. Change effective in SDK v1.3.0 (v1.2.0 and earlier always populated it). followUpQuestions: type: array items: $ref: '#/components/schemas/FollowUpQuestion' feedback: type: array items: $ref: '#/components/schemas/MessageFeedback' referenceData: type: array description: | Reference identifiers surfaced from tool responses, used to scope follow-up queries. items: anyOf: - type: object additionalProperties: false properties: name: type: string description: Display name shown to the user. id: type: string description: Technical identifier (numeric ID, UUID, etc.). type: type: string description: Item type (e.g. `project`, `issue`, `file`, `notebook`, `page`). app: type: string description: | Source application (e.g. `jira`, `confluence`, `sharepoint`, `slack`, `drive`, `gmail`). webUrl: type: string description: URL to open the item in a browser. metadata: type: object additionalProperties: type: string description: | App-specific fields keyed by name (e.g. `key` for a Jira project, `siteId` for a SharePoint document). - $ref: '#/components/schemas/RedactedSource' attachments: type: array description: | Files uploaded for this message turn (see `POST /agents/{agentKey}/conversations/attachments/upload`). items: $ref: '#/components/schemas/ChatAttachmentRef' tools: type: array description: Tool call results invoked during this message turn. items: $ref: '#/components/schemas/MessageToolCall' reasoning: type: array description: Persisted chain-of-thought for this turn. items: $ref: '#/components/schemas/MessageReasoningTurn' parts: type: array description: Ordered agent-activity transcript for this turn. items: $ref: '#/components/schemas/MessagePart' modelInfo: $ref: '#/components/schemas/ConversationModelInfo' appliedFilters: $ref: '#/components/schemas/AppliedFilters' metadata: type: object additionalProperties: false properties: processingTimeMs: type: number modelVersion: type: string aiTransactionId: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time AgentConversationDetailPagination: type: object additionalProperties: false description: | Message pagination returned inside the `conversation` object. The handler paginates backwards from the end of the stored message array, then sorts the selected page in memory before serialization. required: - page - limit - totalCount - totalPages - hasNextPage - hasPrevPage - messageRange properties: page: type: integer limit: type: integer totalCount: type: integer totalPages: type: integer hasNextPage: type: boolean description: True when older messages exist outside the returned page hasPrevPage: type: boolean description: True when newer messages exist outside the returned page messageRange: type: object additionalProperties: false required: - start - end properties: start: type: integer end: type: integer AgentConversationDetailAccess: type: object additionalProperties: false properties: isOwner: type: boolean accessLevel: type: string enum: - read - write AgentConversationDetail: type: object additionalProperties: false description: | Reduced conversation view returned by the by-id GET route. This is not the raw `AgentConversation` document shape: fields like `agentKey`, `userId`, `orgId`, `conversationSource`, and root-level `messages` metadata outside the selected slice are omitted. `sharedWith` is the owner's list of recipients, so it is present only for the owner. A recipient gets `sharedBy` (who shared the chat) instead. required: - id - createdAt - isShared - messages - pagination - access properties: id: type: string format: objectId title: type: string initiator: type: string format: objectId createdAt: type: string format: date-time isShared: type: boolean sharedWith: type: array items: type: object additionalProperties: false properties: _id: type: string format: objectId userId: type: string format: objectId accessLevel: type: string enum: - read - write status: type: string enum: - None - Inprogress - Complete - Failed - Stopped failReason: type: string sharedBy: $ref: '#/components/schemas/ConversationSharedBy' messages: type: array items: $ref: '#/components/schemas/AgentConversationDetailMessage' modelInfo: $ref: '#/components/schemas/ConversationModelInfo' pagination: $ref: '#/components/schemas/AgentConversationDetailPagination' access: $ref: '#/components/schemas/AgentConversationDetailAccess' AgentConversationDetailMeta: type: object additionalProperties: false description: | Request-scoped metadata returned by the by-id GET route. `requestId` is omitted when upstream middleware did not attach one. required: - timestamp - duration - conversationId - messageCount properties: requestId: type: string timestamp: type: string format: date-time duration: type: integer conversationId: type: string format: objectId messageCount: type: integer AgentConversationDetailResponse: type: object additionalProperties: false description: | Envelope returned by `GET /agents/{agentKey}/conversations/{conversationId}`. required: - conversation - filters - meta properties: conversation: $ref: '#/components/schemas/AgentConversationDetail' filters: $ref: '#/components/schemas/SemanticSearchHistoryFilters' meta: $ref: '#/components/schemas/AgentConversationDetailMeta' AgentConversationArchiveMeta: type: object additionalProperties: false description: | Request-scoped metadata returned by the archive route. `requestId` is omitted when upstream middleware did not attach one. required: - timestamp - duration properties: requestId: type: string timestamp: type: string format: date-time duration: type: integer AgentConversationArchiveResponse: type: object additionalProperties: false description: | Envelope returned by `POST /agents/{agentKey}/conversations/{conversationId}/archive`. required: - id - status - archivedBy - archivedAt - meta properties: id: type: string format: objectId status: type: string enum: - archived archivedBy: type: string format: objectId archivedAt: type: string format: date-time meta: $ref: '#/components/schemas/AgentConversationArchiveMeta' AgentConversationUnarchiveMeta: type: object additionalProperties: false description: | Request-scoped metadata returned by the unarchive route. `requestId` is omitted when upstream middleware did not attach one. required: - timestamp - duration properties: requestId: type: string timestamp: type: string format: date-time duration: type: integer AgentConversationUnarchiveResponse: type: object additionalProperties: false description: | Envelope returned by `POST /agents/{agentKey}/conversations/{conversationId}/unarchive`. required: - id - status - unarchivedBy - unarchivedAt - meta properties: id: type: string format: objectId status: type: string enum: - unarchived unarchivedBy: type: string format: objectId unarchivedAt: type: string format: date-time meta: $ref: '#/components/schemas/AgentConversationUnarchiveMeta' AgentRegenerateSSEEvent: type: object additionalProperties: false description: | SSE event envelope for `POST /agents/{agentKey}/conversations/{conversationId}/message/{messageId}/regenerate`. AG-UI is the sole wire protocol. `event` carries the AG-UI type name and `data` is a JSON-encoded object that includes a `"type"` field matching `event`, plus type-specific fields. Stable gateway-generated top-level outcomes: - `RUN_FINISHED` returns `{ type, result }` where `result` is `{ conversation, recordsUsed, meta }` — the updated conversation plus request metadata after the regenerated response is persisted. - `RUN_ERROR` returns `{ type, message, code? }`. Conversation lookup failures, unauthorized conversation access, and regenerate rule failures such as "not the last message" are reported here. Other events are forwarded from the agent backend and should be treated as informational updates. Those forwarded lifecycle and child-run events may contain `runId`, `threadId`, and `parentRunId`; the gateway-generated root terminal event does not. properties: event: type: string enum: - RUN_STARTED - RUN_FINISHED - RUN_ERROR - STEP_STARTED - STEP_FINISHED - TEXT_MESSAGE_START - TEXT_MESSAGE_CONTENT - TEXT_MESSAGE_END - REASONING_START - REASONING_MESSAGE_START - REASONING_MESSAGE_CONTENT - REASONING_MESSAGE_END - REASONING_END - TOOL_CALL_START - TOOL_CALL_ARGS - TOOL_CALL_END - TOOL_CALL_RESULT - STATE_DELTA - STATE_SNAPSHOT - CUSTOM - HEARTBEAT data: type: string description: | JSON-encoded event payload. The decoded JSON includes a `"type"` field matching `event`, plus type-specific fields. Shape depends on `event`. SSEEvent: type: object description: | Server-Sent Event envelope for streaming chat responses. AG-UI is the sole wire protocol. `event` carries the AG-UI type name and `data` is a JSON-encoded object that includes a `"type"` field matching `event`, plus type-specific fields. Stable gateway-generated top-level outcomes: - `RUN_FINISHED` — `{ type, result }`; `result` carries the full persisted `conversation` and a `meta` block with `requestId`, `timestamp` and `duration`. - `RUN_ERROR` — `{ type, message, code? }`. The conversation row is marked FAILED before the stream closes. Forwarded upstream lifecycle or child-run events may contain `runId`, `threadId`, and `parentRunId`. Gateway-generated root terminal events do not. Clients should ignore unknown event names. properties: event: type: string enum: - RUN_STARTED - RUN_FINISHED - RUN_ERROR - STEP_STARTED - STEP_FINISHED - TEXT_MESSAGE_START - TEXT_MESSAGE_CONTENT - TEXT_MESSAGE_END - REASONING_START - REASONING_MESSAGE_START - REASONING_MESSAGE_CONTENT - REASONING_MESSAGE_END - REASONING_END - TOOL_CALL_START - TOOL_CALL_ARGS - TOOL_CALL_END - TOOL_CALL_RESULT - STATE_DELTA - STATE_SNAPSHOT - CUSTOM - HEARTBEAT data: type: string description: | JSON-encoded event payload. The decoded JSON includes a `"type"` field matching `event`, plus type-specific fields. Shape depends on `event`. ConversationStreamSSEEvent: type: object description: | Server-Sent Event envelope for public conversation streams using `agent`, `internal_search`, or `web_search`. AG-UI is the sole wire protocol. `event` carries the AG-UI type name and `data` is a JSON-encoded object that includes a `"type"` field matching `event`, plus type-specific fields. Universal `agent` mode may emit `STEP_STARTED`, `STEP_FINISHED`, and nested child-run events carrying `parentRunId`. Stable gateway-generated top-level outcomes: - `CUSTOM` (`name: "conversation_created"`) — fired once on connection. Carries the newly created `conversationId` and `title` so the client can link the stream to the new row before any tokens arrive. - `RUN_FINISHED` — fired once after the AI backend finishes. The gateway emits `{ type, result }`; `result` carries the full persisted `conversation` and a `meta` block. - `RUN_ERROR` — fired when the stream fails. Carries a `message` and optional `code`; the conversation row is marked FAILED before close. Forwarded upstream lifecycle or child-run events may contain `runId`, `threadId`, and `parentRunId`. Gateway-generated root terminal events do not. Clients should ignore unknown event names rather than treating them as errors. properties: event: type: string enum: - RUN_STARTED - RUN_FINISHED - RUN_ERROR - STEP_STARTED - STEP_FINISHED - TEXT_MESSAGE_START - TEXT_MESSAGE_CONTENT - TEXT_MESSAGE_END - REASONING_START - REASONING_MESSAGE_START - REASONING_MESSAGE_CONTENT - REASONING_MESSAGE_END - REASONING_END - TOOL_CALL_START - TOOL_CALL_ARGS - TOOL_CALL_END - TOOL_CALL_RESULT - STATE_DELTA - STATE_SNAPSHOT - CUSTOM - HEARTBEAT data: type: string description: | JSON-encoded event payload. The decoded JSON includes a `"type"` field matching `event`, plus type-specific fields. Shape depends on `event`. Forwarded lifecycle events may carry `runId`, `threadId`, and `parentRunId`. ConversationMessageStreamSSEEvent: type: object description: | Server-Sent Event envelope for public follow-up streams using `agent`, `internal_search`, or `web_search` on an existing conversation. AG-UI is the sole wire protocol. `event` carries the AG-UI type name and `data` is a JSON-encoded object that includes a `"type"` field matching `event`, plus type-specific fields. Universal `agent` mode may emit step and nested child-run events. Stable gateway-generated top-level outcomes: - `CUSTOM` (`name: "conversation_created"`) — fired once on connection. - `RUN_FINISHED` — fired once after the AI backend finishes. The gateway emits `{ type, result }`; `result` carries `{ conversation, recordsUsed, meta }`. `recordsUsed` is the count of citations attached to the new assistant message. - `RUN_ERROR` — fired when the stream fails. Carries a `message` and optional `code`; the conversation row is marked FAILED before close. Forwarded upstream lifecycle or child-run events may contain `runId`, `threadId`, and `parentRunId`. Gateway-generated root terminal events do not. Clients should ignore unknown event names rather than treating them as errors. properties: event: type: string enum: - RUN_STARTED - RUN_FINISHED - RUN_ERROR - STEP_STARTED - STEP_FINISHED - TEXT_MESSAGE_START - TEXT_MESSAGE_CONTENT - TEXT_MESSAGE_END - REASONING_START - REASONING_MESSAGE_START - REASONING_MESSAGE_CONTENT - REASONING_MESSAGE_END - REASONING_END - TOOL_CALL_START - TOOL_CALL_ARGS - TOOL_CALL_END - TOOL_CALL_RESULT - STATE_DELTA - STATE_SNAPSHOT - CUSTOM - HEARTBEAT data: type: string description: | JSON-encoded event payload. The decoded JSON includes a `"type"` field matching `event`, plus type-specific fields. Shape depends on `event`. Forwarded lifecycle events may carry `runId`, `threadId`, and `parentRunId`. AgentStreamSSEEvent: type: object description: | SSE event envelope for `POST /agents/{agentKey}/conversations/stream`. AG-UI is the sole wire protocol. `event` carries the AG-UI type name and `data` is a JSON-encoded object that includes a `"type"` field matching `event`, plus type-specific fields. The public route requires `chatMode: quick`. Forwarded lifecycle events may carry `runId`, `threadId`, and `parentRunId`; gateway-generated root terminal events do not. Stable gateway-generated top-level outcomes are `RUN_FINISHED` as `{ type, result }` and `RUN_ERROR` as `{ type, message, code? }`. Clients should ignore unknown event names rather than treating them as errors. properties: event: type: string enum: - RUN_STARTED - RUN_FINISHED - RUN_ERROR - STEP_STARTED - STEP_FINISHED - TEXT_MESSAGE_START - TEXT_MESSAGE_CONTENT - TEXT_MESSAGE_END - REASONING_START - REASONING_MESSAGE_START - REASONING_MESSAGE_CONTENT - REASONING_MESSAGE_END - REASONING_END - TOOL_CALL_START - TOOL_CALL_ARGS - TOOL_CALL_END - TOOL_CALL_RESULT - STATE_DELTA - STATE_SNAPSHOT - CUSTOM - HEARTBEAT data: type: string description: | JSON-encoded event payload. The decoded JSON includes a `"type"` field matching `event`, plus type-specific fields. Shape depends on `event`. AgentMessageStreamSSEEvent: type: object description: | Server-Sent Event envelope for `POST /agents/{agentKey}/conversations/{conversationId}/messages/stream`. AG-UI is the sole wire protocol. `event` carries the AG-UI type name and `data` is a JSON-encoded object that includes a `"type"` field matching `event`, plus type-specific fields. The public route requires `chatMode: quick`. Forwarded lifecycle events may carry `runId`, `threadId`, and `parentRunId`. Stable gateway-generated top-level outcomes: - `CUSTOM` (`name: "conversation_created"`) — fired once after the SSE stream opens. - `RUN_FINISHED` — fired once after the upstream AI's result is parsed, citations are saved, and the updated conversation is persisted. The gateway emits `{ type, result }`; `result` carries `{ conversation, recordsUsed, meta }`. - `RUN_ERROR` — fired for runtime failures after the stream has already started, including conversation lookup failures, upstream AI startup failures, save failures, and stream transport errors. Gateway-generated root terminal events do not contain `runId`, `threadId`, or `parentRunId`. Clients should ignore unknown event names rather than treating them as errors. properties: event: type: string enum: - RUN_STARTED - RUN_FINISHED - RUN_ERROR - STEP_STARTED - STEP_FINISHED - TEXT_MESSAGE_START - TEXT_MESSAGE_CONTENT - TEXT_MESSAGE_END - REASONING_START - REASONING_MESSAGE_START - REASONING_MESSAGE_CONTENT - REASONING_MESSAGE_END - REASONING_END - TOOL_CALL_START - TOOL_CALL_ARGS - TOOL_CALL_END - TOOL_CALL_RESULT - STATE_DELTA - STATE_SNAPSHOT - CUSTOM - HEARTBEAT data: type: string description: | JSON-encoded event payload. The decoded JSON includes a `"type"` field matching `event`, plus type-specific fields. Shape depends on `event`. ConnectorScope: type: string description: | Scope determines visibility and access control for connectors:
  • team - Available to all users in the organization (admin-only creation)
  • personal - Private to the creating user only
enum: - team - personal example: team ConnectorAuthType: type: string description: How a connector instance signs in to its source. Each connector type supports a subset; see `supportedAuthTypes` in the registry. enum: - OAUTH - OAUTH_ADMIN_CONSENT - API_TOKEN - BEARER_TOKEN - BASIC_AUTH - ACCESS_KEY - IAM_ROLE - ACCOUNT_KEY - CONNECTION_STRING - ENTRA_ID - CUSTOM - NONE ConnectorInstance: type: object description: 'A connector instance: its type''s registry entry plus the instance''s own state.' properties: _key: type: string description: Connector instance ID example: 7b387394-1248-4fb6-810d-e8ac9ac226eb name: type: string description: Instance name example: Company Google Drive type: type: string description: Connector type example: Drive Workspace appGroup: type: string description: Product family the connector belongs to supportedAuthTypes: type: array description: Authentication methods the connector type supports items: $ref: '#/components/schemas/ConnectorAuthType' appDescription: type: string appCategories: type: array items: type: string iconPath: type: string description: Path of the connector icon supportsRealtime: type: boolean supportsSync: type: boolean supportsAgent: type: boolean documentationLinks: type: array items: $ref: '#/components/schemas/ConnectorDocumentationLink' isAdminAccessRequired: type: boolean personalConnectorType: type: string description: For a team connector type, its personal counterpart, if any nullable: true connectorInfo: type: string description: Guidance shown before setup nullable: true authType: type: string nullable: true description: | Authentication method chosen when the instance was created, one of the type's `supportedAuthTypes`. example: OAUTH scope: $ref: '#/components/schemas/ConnectorScope' isActive: type: boolean description: Sync is on isAgentActive: type: boolean description: Agents may use this connector isConfigured: type: boolean description: Sync settings are saved; sync can be enabled isAuthenticated: type: boolean description: OAuth sign-in has completed isLocked: type: boolean description: A sync or deletion holds the connector status: type: string description: Lifecycle status, for example SYNCING or DELETING nullable: true example: IDLE pendingFullSync: type: boolean desktopOnline: type: boolean ownerDeviceId: type: string description: Local FS only. The desktop device that owns this connector nullable: true ownerDeviceName: type: string description: Local FS only. Display name of the desktop device that owns this connector nullable: true createdBy: type: string nullable: true updatedBy: type: string description: Null for an instance nobody has updated, for example the personal KB connector nullable: true createdAtTimestamp: type: integer description: Epoch milliseconds format: int64 nullable: true updatedAtTimestamp: type: integer description: Epoch milliseconds format: int64 nullable: true lastSyncStartedAtTimestamp: type: integer format: int64 nullable: true description: Epoch ms when the last sync run started; absent before the first run lastSyncFinishedAtTimestamp: type: integer format: int64 nullable: true description: Epoch ms when the last sync run ended, however it ended; absent until a run ends lastSyncOutcome: type: string nullable: true enum: - succeeded - failed - stopped - skipped description: How the last sync run ended lastSyncSummary: type: object nullable: true additionalProperties: true description: 'What the last run did, in the connector''s own terms. For a Web connector: pagesSaved (written this run), pagesStored (kept after it, which Maximum pages caps), pagesVisited, failed, failedByStatus, robotsSkipped, noindexSkipped, skippedMaxPages, deferred, pruned, sitemapUrls, stopReason (max_pages, time_limit, stalled, rate_limited or null) and resumed.' actionItems: type: array description: What the connector's admin has to do, shown as an action-needed badge on the instance card. Written by the connector at the end of each sync and when its auth settings are saved; empty when nothing is needed. items: type: object required: - code - severity - title - message properties: code: type: string description: Identifies the item for the connector, e.g. certificate_sign_in severity: type: string enum: - warning - error title: type: string message: type: string action: type: string nullable: true enum: - authenticate description: Where in the connector panel it is done; authenticate opens the Authenticate tab docsUrl: type: string format: uri nullable: true description: The setup steps for it, on the docs site config: $ref: '#/components/schemas/ConnectorSchema' ConnectorConfig: type: object properties: connector_id: type: string description: Connector instance ID name: type: string description: Instance name type: type: string description: Connector type appGroup: type: string authType: type: string nullable: true description: Authentication method chosen when the instance was created example: OAUTH scope: $ref: '#/components/schemas/ConnectorScope' createdBy: type: string nullable: true description: User ID who created this instance updatedBy: type: string nullable: true description: User ID who last changed this instance appDescription: type: string appCategories: type: array items: type: string supportsRealtime: type: boolean supportsSync: type: boolean supportsAgent: type: boolean iconPath: type: string config: $ref: '#/components/schemas/ConnectorStoredConfig' isActive: type: boolean isConfigured: type: boolean isAuthenticated: type: boolean createdAtTimestamp: type: integer format: int64 nullable: true description: Creation time, epoch milliseconds updatedAtTimestamp: type: integer format: int64 nullable: true description: Last update time, epoch milliseconds ConnectorSchema: type: object description: 'Configuration form for a connector type: the auth fields per auth type, the sync strategies and settings, and the sync and indexing filters.' properties: iconPath: type: string supportsRealtime: type: boolean supportsSync: type: boolean supportsAgent: type: boolean hideConnector: type: boolean description: Whether the type is hidden from the registry listing permissionModel: type: string description: | How records of this connector get their per-user visibility: `RECORD_LEVEL` (per-record permissions from the source) or `APP_LEVEL` (everyone who can reach the connector sees every record). enum: - RECORD_LEVEL - APP_LEVEL isAdminAccessRequired: type: boolean personalConnectorType: type: string nullable: true supportsConnectionCheck: type: boolean description: Whether POST /connectors/registry/{connectorType}/test-connection works for this type documentationLinks: type: array items: $ref: '#/components/schemas/ConnectorDocumentationLink' auth: type: object properties: supportedAuthTypes: type: array items: $ref: '#/components/schemas/ConnectorAuthType' schemas: type: object description: Auth form per auth type, keyed by ConnectorAuthType additionalProperties: $ref: '#/components/schemas/ConnectorAuthSchemaForType' values: type: object additionalProperties: true description: Default values, keyed by field name customFields: type: array items: {} customValues: type: object additionalProperties: true conditionalDisplay: type: object additionalProperties: true description: Rules that show a field only when another field has a given value, keyed by the field they apply to authorizeUrl: type: string description: Instance reads only tokenUrl: type: string description: Instance reads only oauthConfigs: type: object description: 'Instance reads only: provider endpoints per auth type' additionalProperties: type: object properties: authorizeUrl: type: string tokenUrl: type: string redirectUri: type: string scopes: type: array items: type: string description: Authentication form sync: type: object properties: supportedStrategies: type: array items: type: string enum: - MANUAL - SCHEDULED - WEBHOOK selectedStrategy: type: string enum: - MANUAL - SCHEDULED - WEBHOOK description: Strategy preselected in the form webhookConfig: type: object properties: supported: type: boolean webhookUrl: type: string events: type: array items: type: string verificationToken: type: string secretKey: type: string scheduledConfig: type: object properties: intervalMinutes: type: integer cronExpression: type: string timezone: type: string example: UTC startTime: type: integer nextTime: type: integer endTime: type: integer maxRepetitions: type: integer repetitionCount: type: integer realtimeConfig: type: object properties: supported: type: boolean connectionType: type: string example: WEBSOCKET customFields: type: array items: $ref: '#/components/schemas/ConnectorSyncCustomField' customValues: type: object additionalProperties: true values: type: object additionalProperties: true description: Sync options filters: type: object properties: sync: $ref: '#/components/schemas/ConnectorFilterSection' indexing: $ref: '#/components/schemas/ConnectorFilterSection' description: Filters the connector offers ConnectorAuthConfig: type: object description: 'Auth settings, flat: send each field listed under `auth.schemas..fields` in GET /connectors/registry/{connectorType}/schema by its `name`. For OAuth, an admin may send the OAuth app''s credentials here (the app is saved and linked), or anyone may send `oauthConfigId` to use a saved app.' additionalProperties: true properties: oauthConfigId: type: string description: A saved OAuth app (GET /oauth/{connectorType}) oauthInstanceName: type: string description: Name for the OAuth app saved from inline credentials connectorScope: type: string description: Scope of the instance, as the frontend sends it; the server fills it in when absent authType: type: string description: | On `PUT /connectors/{connectorId}/config` only: must equal the instance's `authType`, which cannot be changed (`400` otherwise). example: baseUrl: https://mycompany.atlassian.net email: admin@mycompany.com apiToken: ATATT3x... ConnectorSyncConfig: type: object description: 'Sync strategy plus connector-specific sync settings, flat: send each field listed under `sync.customFields` in the registry schema by its `name`.' additionalProperties: true properties: selectedStrategy: type: string enum: - MANUAL - SCHEDULED - WEBHOOK scheduledConfig: type: object properties: intervalMinutes: type: integer minimum: 1 timezone: type: string default: UTC startTime: type: integer description: Epoch milliseconds startDateTime: description: When the schedule starts, as the frontend sends it additionalProperties: true webhookConfig: type: object properties: webhookUrl: type: string events: type: array items: type: string ConnectorFiltersConfig: type: object properties: sync: type: object properties: values: type: object additionalProperties: $ref: '#/components/schemas/ConnectorFilterValue' indexing: type: object properties: values: type: object additionalProperties: $ref: '#/components/schemas/ConnectorFilterValue' CreateConnectorRequest: type: object required: - connectorType - instanceName - scope properties: connectorType: type: string minLength: 1 description: '`type` from the registry' instanceName: type: string minLength: 1 pattern: \S scope: $ref: '#/components/schemas/ConnectorScope' authType: allOf: - $ref: '#/components/schemas/ConnectorAuthType' description: One of the type's `supportedAuthTypes`; defaults to the first config: type: object properties: auth: $ref: '#/components/schemas/ConnectorAuthConfig' sync: $ref: '#/components/schemas/ConnectorSyncConfig' filters: $ref: '#/components/schemas/ConnectorFiltersConfig' baseUrl: type: string description: Origin of the page the OAuth provider redirects back to. The redirect URI is `{baseUrl}/{redirectUri}`; defaults to the PipesHub frontend URL. UpdateConnectorConfigRequest: type: object description: Request to update connector configuration (partial updates supported) properties: auth: $ref: '#/components/schemas/ConnectorAuthConfig' sync: $ref: '#/components/schemas/ConnectorSyncConfig' filters: $ref: '#/components/schemas/ConnectorFiltersConfig' baseUrl: type: string description: | Base URL for self-hosted instances. Any string is accepted; it is not checked to be a URL. example: https://confluence.mycompany.com UpdateConnectorAuthRequest: type: object required: - auth properties: auth: $ref: '#/components/schemas/ConnectorAuthConfig' baseUrl: type: string description: Recomputes the OAuth redirect URI UpdateConnectorFiltersSyncRequest: type: object minProperties: 1 description: At least one of `sync` and `filters`. `filters.sync` and `filters.indexing` replace the saved ones. properties: sync: $ref: '#/components/schemas/ConnectorSyncConfig' filters: $ref: '#/components/schemas/ConnectorFiltersConfig' anyOf: - required: - sync - required: - filters SaveConnectorFiltersRequest: type: object description: | Request to save filter selections for a connector. Fields other than `filters` are dropped without an error. required: - filters properties: filters: type: object description: Filter values to save (structure depends on connector's filter schema). Must not be empty. minProperties: 1 additionalProperties: true example: folders: - id: folder_123 name: Documents - id: folder_456 name: Reports fileTypes: - pdf - docx modifiedAfter: '2024-01-01' ConnectorToggleRequest: type: object required: - type properties: type: type: string enum: - sync - agent description: '''sync'' flips data synchronization; ''agent'' flips use by agents' fullSync: type: boolean description: When turning sync on, start from scratch default: false deviceId: type: string minLength: 1 maxLength: 255 description: Local FS only deviceName: type: string maxLength: 255 description: Local FS only LocalFsDesktopRefusal: type: object description: | 409 body returned when a Local FS connector cannot sync because of its owner device. required: - success - code - message - details properties: success: type: boolean example: false code: type: string enum: - DESKTOP_OFFLINE - DESKTOP_UNCLAIMED - DESKTOP_OWNED_BY_OTHER_DEVICE message: type: string example: No desktop is connected for connector conn_abc123. Open the Pipeshub desktop app on the machine that owns this folder. details: type: object required: - code - connectorId - retryable properties: code: type: string enum: - DESKTOP_OFFLINE - DESKTOP_UNCLAIMED - DESKTOP_OWNED_BY_OTHER_DEVICE connectorId: type: string retryable: type: boolean ownerDeviceName: type: string description: Name of the owner device, when the connector has one. FilterOptions: type: object description: | Option lists for the connector's filters, keyed by filter name, each a list of `{value, label}` choices. A connector type lists them only for filters whose choices come from the service; none of the current types does, so the object is empty. Filter fields themselves come from the connector's registry schema. additionalProperties: type: array items: type: object properties: value: type: string label: type: string additionalProperties: true OAuthConfig: type: object description: A saved OAuth app for a connector type. Admins also get `config` (secrets masked when HIDE_SECRET_CONFIG is on) and, in lists, the provider endpoints. properties: _id: type: string description: 'OAuth app ID: the `oauthConfigId` to link a connector to' format: uuid oauthInstanceName: type: string connectorType: type: string example: Confluence iconPath: type: string description: Icon of the connector type, copied from the OAuth registry when the configuration was created example: /icons/connectors/confluence.svg appGroup: type: string description: Application group of the connector type (empty when the type is not in the OAuth registry) example: Atlassian appDescription: type: string description: Description of the connector type's OAuth application appCategories: type: array items: type: string description: Categories of the connector type config: type: object description: OAuth app credentials; keys follow the connector type's OAuth fields additionalProperties: true properties: clientId: type: string clientSecret: type: string userId: type: string updatedBy: type: string createdAtTimestamp: type: integer description: Epoch milliseconds format: int64 updatedAtTimestamp: type: integer description: Epoch milliseconds format: int64 authorizeUrl: type: string tokenUrl: type: string redirectUri: type: string scopes: type: object description: Provider scopes requested per use additionalProperties: type: array items: type: string CreateOAuthConfigRequest: type: object description: Request to create OAuth configuration (admin only). Stores credentials that users select when setting up connectors. required: - oauthInstanceName - config properties: oauthInstanceName: type: string description: Display name for this OAuth configuration (e.g., 'Production Google OAuth'). Must be unique for the connector type within the organization. Leading and trailing whitespace is removed before validation, so a name of only whitespace is refused as empty. minLength: 1 pattern: \S example: Production Google OAuth Credentials config: type: object description: | OAuth application credentials, keyed by the `name` of each entry in the connector type's `authFields` (see `GET /oauth/registry/{connectorType}`), so the keys differ from one connector type to the next. Must not be empty; neither the keys nor their values are validated when the configuration is created, so a value of another type (a number, `null`, a nested object, an array of other items) is stored as sent, also for `clientId` and `clientSecret`. properties: clientId: type: string description: OAuth client ID from your OAuth application example: 123456789-abc.apps.googleusercontent.com clientSecret: type: string description: OAuth client secret; never in the create response, and returned by GET only to admins (masked when HIDE_SECRET_CONFIG is on) example: GOCSPX-xxxxxxxxxxxxx tenantId: type: string description: Azure tenant ID (required only for Microsoft connectors) example: 12345678-1234-1234-1234-123456789abc minProperties: 1 additionalProperties: description: Value of any other auth field the connector type declares, keyed by the field's `name` oneOf: - type: string - type: boolean - type: number - type: array items: type: string baseUrl: type: string description: 'PipesHub''s public origin, prefixed to the connector''s redirect path to build the stored redirectUri. When omitted or empty, the frontend public endpoint is used. The value is not checked to be a URL: whatever text is sent is used as the prefix. Not the provider''s URL; a self-hosted provider''s address goes in config.' example: https://pipeshub.mycompany.com ConnectorPagination: type: object required: - page - limit - totalCount - totalPages - hasPrev - hasNext properties: page: type: integer limit: type: integer search: type: string description: The search term the list was filtered by nullable: true totalCount: type: integer description: Number of items matching the request across all pages totalPages: type: integer description: Number of pages at the current `limit` hasPrev: type: boolean description: Whether a page exists before this one hasNext: type: boolean description: Whether a page exists after this one prevPage: type: integer nullable: true description: Previous page number, `null` on the first page nextPage: type: integer nullable: true description: Next page number, `null` on the last page ModelType: type: string enum: - llm - embedding - ocr - slm - reasoning - multiModal - imageGeneration - tts - stt - decision description: Type of AI model WebSearchProviderType: type: string enum: - duckduckgo - serper - tavily - exa - brave - searxng - firecrawl - crawl4ai description: Supported web search provider. crawl4ai only reads pages (it backs up the built-in page reader) and cannot be the default; firecrawl both searches and reads pages. searxng, firecrawl and crawl4ai take a baseUrl; firecrawl defaults to its cloud API. WebSearchProviderItem: type: object additionalProperties: false description: Web search provider configuration item returned by getWebSearchProviders required: - provider - providerKey - configuration - isDefault properties: provider: $ref: '#/components/schemas/WebSearchProviderType' providerKey: type: string description: Unique key for the provider configuration configuration: type: object description: | Provider-specific configuration as stored and returned by the gateway (open record). API providers include `apiKey` (masked for non-admins); searxng, firecrawl and crawl4ai include `baseUrl` (masked for non-admins too). Additional keys may be present. additionalProperties: true isDefault: type: boolean WebSearchSettings: type: object additionalProperties: false description: Normalized web search global settings returned by getWebSearchProviders required: - includeImages properties: includeImages: type: boolean description: Whether to include images in search results maxImages: type: integer minimum: 1 maximum: 500 description: Maximum number of images to return when includeImages is true WebSearchProvidersResponse: type: object additionalProperties: false description: Response for getWebSearchProviders required: - status - providers - settings - message properties: status: type: string enum: - success providers: type: array items: $ref: '#/components/schemas/WebSearchProviderItem' settings: $ref: '#/components/schemas/WebSearchSettings' message: type: string description: Human-readable status (empty list vs populated providers) OAuthConfigPagination: type: object required: - page - limit - totalItems - totalPages - hasPrev - hasNext properties: page: type: integer limit: type: integer search: type: string nullable: true totalItems: type: integer totalPages: type: integer hasPrev: type: boolean hasNext: type: boolean OAuthRegistryEntry: type: object description: A connector type that signs in with an OAuth app, with what an admin needs to register that app required: - name - type - appGroup - authType - appDescription - appCategories - iconPath - oauthScopes - redirectUri - authorizeUrl - tokenUrl - authFields - documentationLinks properties: name: type: string example: Jira type: type: string description: The `connectorType` to use in the OAuth app and connector routes example: Jira appGroup: type: string example: Atlassian authType: type: string description: Always OAUTH example: OAUTH appDescription: type: string appCategories: type: array items: type: string iconPath: type: string example: /icons/connectors/jira.svg oauthScopes: type: object description: Provider scopes requested for each way the connector is used required: - personalSync - teamSync - agent properties: personalSync: type: array items: type: string teamSync: type: array items: type: string agent: type: array items: type: string redirectUri: type: string description: Path the provider redirects back to; PipesHub's public origin is prefixed to it example: connectors/oauth/callback/Jira authorizeUrl: type: string tokenUrl: type: string additionalParams: type: object description: Extra query parameters sent on the authorize URL; present for some types only additionalProperties: type: string tokenAccessType: type: string description: Dropbox and Salesforce only example: offline authFields: type: array description: The fields an admin fills in when registering the OAuth app items: type: object required: - name - displayName - placeholder - description - fieldType - required - usage - defaultValue - validation - isSecret properties: name: type: string example: clientId displayName: type: string placeholder: type: string description: type: string fieldType: type: string enum: - TEXT - PASSWORD - URL - SELECT required: type: boolean usage: type: string enum: - CONFIGURE - BOTH defaultValue: type: string validation: type: object properties: minLength: type: integer maxLength: type: integer isSecret: type: boolean options: type: array description: Present on SELECT fields items: type: string documentationLinks: type: array items: type: object required: - title - url - type properties: title: type: string url: type: string type: type: string description: For example setup, docs or pipeshub (the casing varies) ConnectorFilterValue: type: object description: A saved filter. Sync parses only entries that carry `operator` and `type`; one without `type` is saved but skipped when the connector syncs. required: - operator - value - type properties: operator: type: string description: One of the field's `operators`, for example `in` or `is_after` value: description: The filter value; its type follows `type` type: type: string enum: - string - boolean - datetime - list - number - multiselect - select description: The field's `filterType` from the registry schema ConnectorStoredConfig: type: object description: A connector instance's saved settings. Secret auth fields come back masked; saving the mask back keeps the stored value. Provider OAuth tokens are never returned. properties: auth: type: object description: Auth fields for the instance's auth type, keyed by field name additionalProperties: true properties: authType: $ref: '#/components/schemas/ConnectorAuthType' connectorType: type: string description: Set by the server to the instance's connector type connectorScope: $ref: '#/components/schemas/ConnectorScope' oauthConfigId: type: string description: The shared OAuth app the instance signs in with oauthInstanceName: type: string redirectUri: type: string description: OAuth redirect URI the server built for this instance sync: type: object description: Sync strategy plus connector-specific sync settings additionalProperties: true properties: selectedStrategy: type: string description: '`MANUAL`, `SCHEDULED` or `WEBHOOK`' scheduledConfig: type: object additionalProperties: true description: Schedule used when `selectedStrategy` is `SCHEDULED` customValues: type: object additionalProperties: true description: Values of the connector's own sync fields filters: type: object additionalProperties: true description: Saved filter selections properties: sync: type: object additionalProperties: true description: What to fetch from the source. Selections are under `values`, keyed by filter name. indexing: type: object additionalProperties: true description: Which fetched records to index. Selections are under `values`, keyed by filter name. ConnectorCreated: type: object required: - connectorId - connectorType - instanceName - scope properties: connectorId: type: string description: ID of the new instance example: 7b387394-1248-4fb6-810d-e8ac9ac226eb connectorType: type: string description: Connector type the instance was created from instanceName: type: string description: Instance name, trimmed created: type: boolean scope: $ref: '#/components/schemas/ConnectorScope' createdBy: type: string description: User ID who created the instance isAuthenticated: type: boolean description: Always `false` on creation isConfigured: type: boolean description: Whether initial configuration was saved with the request description: Summary of a connector instance that was just created ConnectorSyncCustomField: type: object description: A connector-specific sync setting. Send its value in `config.sync` under `name`. properties: name: type: string displayName: type: string description: type: string fieldType: type: string required: type: boolean defaultValue: {} validation: type: object additionalProperties: true isSecret: type: boolean nonEditable: type: boolean options: type: array items: type: string ConnectorFilterSection: type: object properties: schema: type: object properties: fields: type: array items: $ref: '#/components/schemas/ConnectorFilterField' values: type: object additionalProperties: true ConnectorFilterField: type: object properties: name: type: string description: 'Filter key: the key in `filters..values`' displayName: type: string description: type: string filterType: type: string category: type: string required: type: boolean defaultValue: {} defaultOperator: type: string nullable: true fallbackOperator: type: string noImplicitOperatorDefault: type: boolean operators: type: array items: type: string optionSourceType: type: string enum: - manual - static - dynamic description: '`dynamic` options come from GET /connectors/{connectorId}/filters/{filterKey}/options' options: type: array items: {} ConnectorAuthSchemaForType: type: object properties: fields: type: array items: $ref: '#/components/schemas/ConnectorAuthField' redirectUri: type: string description: OAuth only. Path appended to the `baseUrl` the connector is created with; the result must be registered as a redirect URI with the provider. displayRedirectUri: type: boolean ConnectorAuthField: type: object description: One field of an auth form. Send its value in `config.auth` under `name`. properties: name: type: string displayName: type: string placeholder: type: string description: type: string fieldType: type: string description: Input type, for example TEXT, PASSWORD, URL, SELECT, CHECKBOX required: type: boolean usage: type: string defaultValue: description: Default value; its type follows `fieldType` validation: type: object additionalProperties: true isSecret: type: boolean description: Stored values come back masked options: type: array items: {} savedOnly: type: boolean description: 'Shown only on an instance that already has a value saved for it, never on a new one: a way of signing in kept for instances set up with it. Absent means false.' requiredUnlessSaved: type: string description: 'Name of another field: this field is required, except on an instance that has a value saved for that field. Absent means `required` alone applies.' ConnectorRegistryEntry: type: object description: A connector type the org can add. properties: name: type: string description: Display name example: OneDrive type: type: string description: 'Connector type: the value to pass as `connectorType`' example: OneDrive appGroup: type: string description: Product family the connector belongs to example: Microsoft 365 supportedAuthTypes: type: array description: Authentication methods an instance of this type can be created with items: $ref: '#/components/schemas/ConnectorAuthType' appDescription: type: string description: One-line description of what the connector syncs appCategories: type: array description: Categories the connector is listed under items: type: string example: - Storage iconPath: type: string description: Path of the connector icon example: /icons/connectors/onedrive.svg supportsRealtime: type: boolean description: Whether the connector can receive real-time updates supportsSync: type: boolean description: Whether the connector syncs data into the knowledge base supportsAgent: type: boolean description: Whether the connector can be used by AI agents documentationLinks: type: array items: $ref: '#/components/schemas/ConnectorDocumentationLink' isAdminAccessRequired: type: boolean description: Whether setting the connector up needs admin rights in the source system personalConnectorType: type: string description: For a team connector type, its personal counterpart, if any nullable: true example: Notion Personal connectorInfo: type: string description: Guidance shown before setup nullable: true scope: $ref: '#/components/schemas/ConnectorScope' ConnectorDocumentationLink: type: object description: A help link shown on a connector's setup page properties: title: type: string description: Link text example: Azure AD App Registration Setup url: type: string description: Link target type: type: string description: | Free-form link category set by the connector, for example `setup`, `pipeshub`, `api` or `docs`. example: setup MicrosoftCredentials: type: object additionalProperties: false description: | Credentials for `microsoft` sign-in. Only `idToken` is read: it is verified against Microsoft's signing keys, with the saved client ID as audience. Without it the sign-in answers `400`. properties: idToken: type: string minLength: 1 description: Microsoft ID token (JWT) accessToken: type: string description: Accepted but not read required: - idToken GoogleCredentials: type: object additionalProperties: false description: | Google ID token wrapped in an object, as Google's sign-in button returns it. Only the first token check reads `credential`; the second one (`authenticateWithGoogle`) is handed the whole object, so by code reading a sign-in sent this way ends in `500` even with a valid token. Send the bare token string instead. properties: credential: type: string description: Google ID token (JWT) required: - credential OAuthBearerTokenError: type: object additionalProperties: false description: | Error body written by the OAuth bearer-token middleware (RFC 6750 Section 3.1) on routes that accept only an OAuth access token, such as `GET /oauth2/userinfo`. The same `error`, `error_description` and `scope` values are repeated in the `WWW-Authenticate` response header. This is not the standard `ErrorResponse` envelope: `error` is a string. properties: error: type: string enum: - invalid_request - invalid_token - insufficient_scope - server_error description: | - `invalid_request` - no `Authorization: Bearer` header was sent - `invalid_token` - the token is malformed, expired, revoked, or its user no longer exists - `insufficient_scope` - the token is valid but lacks a required scope - `server_error` - the scope check itself failed error_description: type: string description: Human-readable explanation of the error example: Bearer token required scope: type: string description: Space-separated scopes the route requires. Only present with `insufficient_scope`. example: openid required: - error - error_description OAuthConfigCredentials: type: object description: | The credentials stored for an OAuth configuration, exactly as the admin submitted them and not masked. The keys are the `name` of each entry in the connector type's `authFields` (see `GET /oauth/registry/{connectorType}`), so they differ from one connector type to the next. Create and update do not check the values, so any JSON value that was sent (a number, `null`, a nested object, an array of any items) is stored and returned here as it was sent, even for `clientId` and `clientSecret`. properties: clientId: description: OAuth client ID; a string unless something else was sent allOf: - $ref: '#/components/schemas/OAuthConfigCredentialValue' clientSecret: description: OAuth client secret, in clear text; a string unless something else was sent allOf: - $ref: '#/components/schemas/OAuthConfigCredentialValue' tenantId: description: Directory (tenant) ID, for Microsoft connector types; a string unless something else was sent allOf: - $ref: '#/components/schemas/OAuthConfigCredentialValue' additionalProperties: $ref: '#/components/schemas/OAuthConfigCredentialValue' OAuthConfigCredentialValue: description: | A stored credential value, returned exactly as it was sent on create or update. Usually a string, boolean, number or list of strings, but the value is not checked, so `null`, a nested object or an array of other items comes back as well. nullable: true anyOf: - type: string - type: boolean - type: number - type: array items: {} - type: object additionalProperties: true OAuthConfigWithCredentials: description: | An OAuth configuration as `GET /oauth/{connectorType}/{configId}` returns it. An admin gets `config` with the stored credentials; any other caller gets the same object without `config`. allOf: - $ref: '#/components/schemas/OAuthConfig' - type: object properties: config: $ref: '#/components/schemas/OAuthConfigCredentials' inherited: type: boolean description: Present, and `true`, only when the configuration is inherited from another organization rather than owned by the caller's. OAuthConfigScopes: type: object description: OAuth scopes copied from the registry when the configuration was created, by use case properties: personal_sync: type: array items: type: string description: Scopes requested for a personal connector team_sync: type: array items: type: string description: Scopes requested for a team connector agent: type: array items: type: string description: Scopes requested for agent tools OAuthConfigRecord: description: | An OAuth configuration as `GET /oauth/{connectorType}` lists it. An admin gets the stored record in full, credentials included. Any other caller gets only the `OAuthConfig` fields. The provider fields (`authorizeUrl`, `tokenUrl`, `redirectUri`, `scopes` and the optional ones after them) are copied from the OAuth registry when the configuration is created, so a configuration created under a type that is not in the registry has none of them. allOf: - $ref: '#/components/schemas/OAuthConfig' - type: object properties: userId: type: string description: ID of the admin who created the configuration (admins only) orgId: type: string description: Organization that owns the configuration (admins only) createdBy: type: string description: ID of the admin who created the configuration (admins only) updatedBy: type: string description: ID of the admin who last updated the configuration (admins only) config: $ref: '#/components/schemas/OAuthConfigCredentials' authorizeUrl: type: string description: Provider authorization URL, from `config.authorizeUrl` when the admin supplied one and from the registry otherwise (admins only) tokenUrl: type: string description: Provider token URL, from `config.tokenUrl` when the admin supplied one and from the registry otherwise (admins only) redirectUri: type: string description: Redirect URI to register with the provider, built from `baseUrl` (or the frontend public endpoint) and the registry's redirect path. Empty when the registry has no redirect path. (admins only) example: http://localhost:3001/connectors/oauth/callback/Confluence scopes: $ref: '#/components/schemas/OAuthConfigScopes' tokenAccessType: type: string description: Token access type sent to the provider. Present only when the registry sets one. (admins only) example: offline additionalParams: $ref: '#/components/schemas/OAuthAuthorizeParams' scopeParameterName: type: string description: Name of the authorization-URL parameter that carries the scopes. Present only when it is not `scope`. (admins only) example: user_scope tokenResponsePath: type: string description: Key under which the provider nests the token in its token response. Present only when the registry sets one. (admins only) example: authed_user OAuthAuthorizeParams: type: object description: Extra query parameters added to the provider authorization URL. The keys are provider-specific. additionalProperties: type: string example: access_type: offline prompt: consent OAuthRegistryPagination: type: object description: Pagination block of `GET /oauth/registry` and `GET /oauth/{connectorType}` properties: page: type: integer description: Current page number limit: type: integer description: Items per page search: type: string nullable: true description: The search term that was applied, or null totalCount: type: integer description: Number of items that match totalPages: type: integer hasPrev: type: boolean hasNext: type: boolean prevPage: type: integer nullable: true description: Previous page number, or null on the first page nextPage: type: integer nullable: true description: Next page number, or null on the last page UpdateRecordResponseSchema: type: object additionalProperties: false description: Response returned by PUT /knowledgeBase/record/{recordId}. required: - message - record - fileUploaded - meta properties: message: type: string description: | `Record updated successfully` for a rename, `Record updated with new file version` when a replacement file was sent. record: description: The record's graph document as stored after the update. allOf: - $ref: '#/components/schemas/GraphRecordFields' - type: object properties: id: type: string description: Record ID. _key: type: string description: The record ID again. Present when the graph database is Neo4j. _id: type: string x-speakeasy-name-override: graphDocumentId description: | The record's graph document handle, `records/`. Not the record ID: use `id`. Named `graphDocumentId` in the SDKs, where `id` is the record ID. _rev: type: string description: Revision of the graph document. Present when the graph database is ArangoDB. isPlaceholder: type: boolean nullable: true description: True while the record is a stub whose content has not been synced yet. deletedAtTimestamp: type: integer format: int64 description: When the record was moved to the trash. Present only on a record that is in the trash. deleteBatchId: type: string description: The delete batch the record belongs to. Present only on a record that is in the trash. deleteSource: type: string description: Who moved the record to the trash (`USER` for a delete through the API). Present only on a record that is in the trash. example: USER restoredAtTimestamp: type: integer format: int64 description: When the record was last brought back from the trash. Absent if it never was. fileUploaded: type: boolean description: Whether the request carried a replacement file. meta: type: object additionalProperties: false required: - timestamp properties: requestId: type: string description: Identifier of this request. timestamp: type: string format: date-time description: When the response was generated. AgentAddMessageResponse: type: object additionalProperties: false description: | Envelope returned by `POST /agents/{agentKey}/conversations/{conversationId}/messages`: the updated agent conversation, the number of citation records used to ground the answer for this turn, and request metadata. required: - conversation - recordsUsed - meta properties: conversation: $ref: '#/components/schemas/AgentConversation' recordsUsed: type: integer minimum: 0 description: Number of citation records used to ground the answer generated for this turn. meta: type: object additionalProperties: false required: - timestamp - duration - recordsUsed properties: requestId: type: string description: Request correlation id. Omitted when no request id was set on the context. timestamp: type: string format: date-time description: Server timestamp when the response was sent. duration: type: integer minimum: 0 description: Total handler duration in milliseconds. recordsUsed: type: integer minimum: 0 description: Same value as the top-level `recordsUsed`. NavigateDateQuery: type: string description: | A date filter of `GET /connectors/navigate`: `YYYY-MM-DD` (UTC; start of the day for a lower bound, end of the day for an upper bound), or a date and time with a timezone offset (`Z` or `±HH:MM`). Empty, blank or timezone-naive values are refused with `400`. pattern: ^\d{4}-\d{2}-\d{2}([T ]\d{2}(:\d{2}(:\d{2}([.,]\d+)?)?)?([Zz]|[+-]\d{2}(:?\d{2})?))?$ example: '2026-01-15T08:00:00Z' AgentMcpServerRef: type: object additionalProperties: false description: | One MCP server instance attached to an agent: a reference only, no secrets. required: - instanceId - name properties: instanceId: type: string minLength: 1 maxLength: 256 pattern: \S description: MCP server instance id. Surrounding whitespace is trimmed. name: type: string minLength: 1 maxLength: 200 pattern: \S description: Server name. Surrounding whitespace is trimmed. displayName: type: string maxLength: 200 typeId: type: string maxLength: 200 description: Server type; at most one attached instance per type. allTools: type: boolean description: | Attach every tool the server offers at chat time, including ones it adds later. Without it, `tools` must name at least one tool, or the request is refused with `400` ("Choose at least one tool for MCP server(s) ..., or attach all of their tools."). tools: type: array maxItems: 5000 description: | Stored tool selection for this server. At most 500 tools of one server are kept: more are refused with `400`, unless `allTools` is set, when the first 500 are kept. items: $ref: '#/components/schemas/AgentCreateToolRef' responses: XssRejected: description: 'Refused before authentication because a query value, or any string in a JSON or form-encoded body, looks like markup to the app-wide XSS filter: an HTML tag, a script tag, `