Part of Connapse — open-source AI knowledge management platform.
Connapse exposes multiple API surfaces for programmatic access:
- REST API — HTTP endpoints for containers, files, search, auth, and settings
- MCP (Model Context Protocol) — Tool interface for AI agents
- CLI — Command-line interface (
connapse)
Base URL (development): https://localhost:5001
All endpoints return JSON. Errors follow RFC 7807 Problem Details format.
Connapse uses a multi-tier authentication system:
| Tier | Header / Mechanism | Used By | Lifetime |
|---|---|---|---|
| Cookie | ASP.NET Core Identity cookie | Blazor UI (browser sessions) | 14-day sliding |
| OAuth 2.1 | Authorization: Bearer <token> (via PKCE) |
CLI (browser-based login) | 60-90 min + refresh token |
| PAT | X-Api-Key: cnp_<token> |
REST API clients, scripts, automation | Until revoked |
| JWT Bearer | Authorization: Bearer <token> |
SDK clients, external integrations | 60-90 min + refresh |
Role-based access control (RBAC):
| Role | Permissions |
|---|---|
| Admin | Full access: all endpoints, user management, settings, agents |
| Editor | Upload, delete, search, manage containers and folders |
| Viewer | Search and browse only (no writes) |
| Agent | MCP tool access only (search + ingest via API keys) |
First-time setup: On a fresh install with no users, visit the login page — it automatically shows an admin account creation form. The first user becomes the system admin.
Subsequent users are invite-only. Admins create invitations at /admin/users. Invited users visit /register?token=<token> to set their password.
All list endpoints use paginated responses. Pagination parameters are required — omitting them returns 400.
Query Parameters:
| Parameter | Type | Default | Constraints |
|---|---|---|---|
skip |
int | 0 | min: 0 |
take |
int | 50 | min: 1, max: 200 |
Response Envelope (PagedResponse<T>):
{
"items": [ ... ],
"totalCount": 142,
"hasMore": true
}| Field | Description |
|---|---|
items |
Array of results for the current page |
totalCount |
Total number of matching records |
hasMore |
true if more records exist beyond skip + take |
Paginated endpoints: GET /api/containers, GET /api/containers/{id}/files, GET /api/v1/auth/users.
Example: GET /api/containers?skip=0&take=20
Base path: /api/v1/auth
Exchange email + password for a JWT access/refresh token pair.
Endpoint: POST /api/v1/auth/token
Request Body:
{
"email": "admin@example.com",
"password": "YourSecurePassword"
}Response (200 OK):
{
"accessToken": "eyJhbGci...",
"refreshToken": "dGhpcyBpcyBhIHJlZnJlc2g...",
"expiresIn": 5400,
"tokenType": "Bearer"
}Response (401 Unauthorized): Invalid credentials.
Notes:
- Use the
accessTokenasAuthorization: Bearer <token>on subsequent requests refreshTokencan be exchanged for a new token pair before expiry- Updates
LastLoginAton the user record + writes audit log entry
Exchange a valid refresh token for a new access/refresh token pair.
Endpoint: POST /api/v1/auth/token/refresh
Request Body:
{
"refreshToken": "dGhpcyBpcyBhIHJlZnJlc2g..."
}Response (200 OK): Same as POST /api/v1/auth/token.
Response (401 Unauthorized): Token invalid, expired, or already revoked.
Notes: Old refresh token is revoked on use (rotation). Each call issues a new pair.
Endpoint: GET /api/v1/auth/pats
Auth: Required (any authenticated user)
Response (200 OK):
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "CLI (LAPTOP-01)",
"prefix": "cnp_abc123",
"lastUsedAt": "2026-02-26T10:00:00Z",
"expiresAt": null,
"isRevoked": false,
"createdAt": "2026-02-20T09:00:00Z"
}
]Endpoint: POST /api/v1/auth/pats
Auth: Required (any authenticated user)
Request Body:
{
"name": "My Script Token",
"expiresAt": "2027-01-01T00:00:00Z"
}Fields:
name(required): Display labelexpiresAt(optional): ISO 8601 datetime; if omitted, token never expires
Response (200 OK):
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "My Script Token",
"token": "cnp_<random-64-chars>",
"expiresAt": "2027-01-01T00:00:00Z"
}Important: The
tokenvalue is shown once only. Store it securely immediately.
Endpoint: DELETE /api/v1/auth/pats/{id}
Auth: Required (own tokens only; admins can revoke any)
Response (204 No Content)
Connapse implements OAuth 2.1 with PKCE for CLI and third-party client authentication. These endpoints follow RFC 6749 and RFC 7636.
| Endpoint | Purpose |
|---|---|
GET /.well-known/oauth-protected-resource |
Protected resource metadata (resource URL, supported scopes) |
GET /.well-known/oauth-authorization-server |
Authorization server metadata (endpoints, supported grants, PKCE methods) |
GET /oauth/clients/cli.json — Static metadata document for the built-in CLI client. Returns the client ID, allowed redirect URIs, and grant types.
GET /oauth/authorize — Renders the OAuth consent page. The user authenticates (or is already authenticated via cookie) and approves the requested scopes.
Query Parameters:
| Parameter | Required | Description |
|---|---|---|
response_type |
Yes | Must be code |
client_id |
Yes | Client ID (URL to metadata document, or registered ID) |
redirect_uri |
Yes | Must match a registered redirect URI |
code_challenge |
Yes | PKCE code challenge (S256) |
code_challenge_method |
Yes | Must be S256 |
state |
Recommended | CSRF protection value |
scope |
Optional | Space-separated scopes (e.g., knowledge:read knowledge:write) |
resource |
Optional | Resource indicator (RFC 8707) |
POST /oauth/token — Exchange an authorization code or refresh token for an access token.
Content-Type: application/x-www-form-urlencoded (required)
Grant type authorization_code:
| Parameter | Required | Description |
|---|---|---|
grant_type |
Yes | authorization_code |
code |
Yes | Authorization code from the consent page redirect |
redirect_uri |
Yes | Must match the original authorize request |
client_id |
Yes | Client ID |
code_verifier |
Yes | PKCE code verifier |
Grant type refresh_token:
| Parameter | Required | Description |
|---|---|---|
grant_type |
Yes | refresh_token |
refresh_token |
Yes | The refresh token to exchange |
client_id |
Optional | If provided, must match the original grant's client |
Response (200 OK):
{
"access_token": "eyJhbGci...",
"token_type": "bearer",
"expires_in": 5400,
"refresh_token": "new-refresh-token...",
"scope": "knowledge:read knowledge:write"
}Notes:
- Refresh tokens are rotated on each use (old token is revoked)
- Replay detection: reusing a consumed refresh token revokes the entire token family
- The
resourceparameter in the authorize request is validated against the server's base URL
POST /oauth/register — Register a new OAuth client dynamically.
Request Body:
{
"client_name": "My App",
"redirect_uris": ["http://127.0.0.1:9999/callback"],
"application_type": "native"
}Response (201 Created):
{
"client_id": "generated-client-id",
"client_name": "My App",
"redirect_uris": ["http://127.0.0.1:9999/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"application_type": "native",
"token_endpoint_auth_method": "none"
}Supported scopes: knowledge:read, knowledge:write
Endpoint: GET /api/v1/auth/users?skip=0&take=50
Auth: Admin role required
Query Parameters: skip, take (see Pagination)
Response (200 OK):
{
"items": [
{
"id": "user-guid",
"email": "viewer@example.com",
"roles": ["Viewer"],
"isSystemAdmin": false,
"lastLoginAt": "2026-02-25T14:30:00Z",
"createdAt": "2026-02-20T09:00:00Z"
}
],
"totalCount": 1,
"hasMore": false
}Endpoint: PUT /api/v1/auth/users/{id}/roles
Auth: Admin role required
Request Body:
{
"roles": ["Editor"]
}Notes:
Ownerrole cannot be assigned or removed via this endpointAgentrole cannot be assigned to user accounts (use Agent API instead)
Response (200 OK)
Base path: /api/v1/agents
All agent endpoints require Admin role.
Agents are non-human identities (CI/CD pipelines, automation scripts) that authenticate via API keys and access the MCP server. For details on what agent API keys can access, see Agent Permissions and Scope.
Endpoint: GET /api/v1/agents
Response (200 OK):
[
{
"id": "agent-guid",
"name": "CI Pipeline",
"description": "Indexes docs on every merge",
"isEnabled": true,
"activeKeyCount": 2,
"createdAt": "2026-02-20T09:00:00Z"
}
]Endpoint: POST /api/v1/agents
Request Body:
{
"name": "CI Pipeline",
"description": "Indexes docs on every merge"
}Response (200 OK): Returns the created Agent object.
Endpoint: GET /api/v1/agents/{id}
Response (200 OK | 404 Not Found)
Endpoint: PUT /api/v1/agents/{id}/active
Request Body:
{ "isEnabled": false }Response (200 OK)
Endpoint: DELETE /api/v1/agents/{id}
Deletes the agent and all its API keys.
Response (204 No Content)
Endpoint: GET /api/v1/agents/{id}/keys
Response (200 OK):
[
{
"id": "key-guid",
"name": "Production Key",
"prefix": "cnp_abc123",
"lastUsedAt": "2026-02-26T08:00:00Z",
"isRevoked": false,
"createdAt": "2026-02-20T09:00:00Z"
}
]Endpoint: POST /api/v1/agents/{id}/keys
Request Body:
{ "name": "Production Key" }Response (200 OK):
{
"id": "key-guid",
"name": "Production Key",
"token": "cnp_<random-64-chars>"
}Important: The
tokenis shown once only. Store it securely immediately.
Endpoint: DELETE /api/v1/agents/{agentId}/keys/{keyId}
Response (204 No Content)
Agents are MCP-only identities. They authenticate with cnp_-prefixed API keys and interact with Connapse exclusively through the MCP endpoint. Agent keys cannot access REST API endpoints.
| Interface | Access | Details |
|---|---|---|
MCP (POST /mcp) |
Allowed | All 11 MCP tools available |
REST API (/api/v1/*) |
403 Forbidden | Agent role is not included in REST API authorization policies (Viewer/Editor/Admin) |
| Web UI | Not accessible | Agent keys are API-only |
This is by design. Agents interact exclusively through the MCP protocol, which provides a structured tool interface suited to non-human consumers. REST API endpoints use role-based policies (Viewer, Editor, Admin) that do not include the Agent role.
When an agent authenticates via API key, it receives two implicit scopes. These scopes govern MCP tool behavior only — they do not grant REST API access.
| Scope | Grants |
|---|---|
knowledge:read |
Search and read documents across all containers via MCP |
agent:ingest |
Upload and delete files via MCP (subject to connector write guards) |
Agents have unrestricted access to all containers in the system. There is no per-container access control for agent API keys.
Agents interact with Connapse through the MCP endpoint (POST /mcp). All 11 MCP tools documented in the Model Context Protocol section are available to agents.
- Create, list, and delete containers (via MCP)
- Upload and delete files, subject to connector write guards (via MCP)
- Search across any container (via MCP)
- List files and retrieve document content (via MCP)
- Access any REST API endpoint (returns 403)
- Manage users or roles
- Change system settings (embedding, search, LLM configuration)
- Create or manage other agents
- Access the web UI
Security note: Since agents have system-wide container access via MCP, treat agent API keys with the same care as admin credentials. Revoke unused keys promptly.
All container endpoints require authentication. RBAC rules:
GETendpoints require Viewer role minimumPOST,DELETE, reindex require Editor role minimum- Settings endpoints require Admin role
Endpoint: POST /api/containers
Request Body:
{
"name": "my-project",
"description": "Project knowledge base",
"connectorType": "MinIO",
"connectorConfig": null
}Fields:
name(required): lowercase alphanumeric + hyphens, 2-128 chars, globally uniquedescription(optional): Container descriptionconnectorType(optional, default:MinIO):MinIO(Managed Storage — MinIO by default, overridable per deployment) |Filesystem|S3|AzureBlobconnectorConfig(conditional): JSON string with connector-specific config
Connector Config Requirements:
| Connector | Required Fields | Example |
|---|---|---|
| MinIO | None (global config) | — |
| Filesystem | rootPath |
{"rootPath":"C:\\docs"} |
| S3 | bucketName, region |
{"bucketName":"docs","region":"us-east-1"} |
| AzureBlob | storageAccountName, containerName |
{"storageAccountName":"acct","containerName":"docs"} |
Response (201 Created):
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "my-project",
"description": "Project knowledge base",
"connectorType": "MinIO",
"documentCount": 0,
"createdAt": "2026-02-26T10:00:00Z",
"updatedAt": "2026-02-26T10:00:00Z"
}Endpoint: GET /api/containers?skip=0&take=50
Query Parameters: skip, take (see Pagination)
Response (200 OK):
{
"items": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "my-project",
"description": "Project knowledge base",
"connectorType": "MinIO",
"documentCount": 42,
"createdAt": "2026-02-26T10:00:00Z",
"updatedAt": "2026-02-26T10:00:00Z"
}
],
"totalCount": 1,
"hasMore": false
}Endpoint: GET /api/containers/{id}
Response (200 OK | 404 Not Found)
Endpoint: DELETE /api/containers/{id}
Notes: Fails with 400 if the container still has files or folders. Must be empty first.
Response (204 No Content | 400 Bad Request)
Endpoint: GET /api/containers/{id}/stats
Auth: Viewer minimum
Response (200 OK):
{
"containerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"containerName": "my-project",
"connectorType": "MinIO",
"documents": {
"total": 42,
"ready": 40,
"processing": 1,
"failed": 1
},
"totalChunks": 1200,
"totalSizeBytes": 52428800,
"embeddingModels": [
{ "modelId": "nomic-embed-text", "dimensions": 768, "vectorCount": 1200 }
],
"lastIndexedAt": "2026-03-09T10:00:00Z",
"createdAt": "2026-02-26T10:00:00Z"
}Upload one or more files into a container.
Endpoint: POST /api/containers/{id}/files
Content-Type: multipart/form-data
Form Fields:
files(required): One or more file partspath(optional): Destination folder path (e.g.,/docs/2026/)strategy(optional): Chunking strategy (Semantic|FixedSize|Recursive, default:Semantic)
Response (200 OK):
{
"documents": [
{
"documentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fileName": "report.pdf",
"fileSize": 245678,
"contentType": "application/pdf",
"status": "Pending",
"error": null
}
],
"successCount": 1,
"failureCount": 0
}Notes:
- Files stream directly to MinIO (not buffered in memory)
- Ingestion is asynchronous — track progress via SignalR or poll the file endpoint
- Duplicate filenames in the same folder auto-increment:
file (1).pdf,file (2).pdf - Write guard: S3 and AzureBlob containers block uploads (read-only). Filesystem containers respect the
allowUploadflag. Returns400with{ "error": "write_denied" }when blocked. See connectors.md — Write Guards.
Endpoint: GET /api/containers/{id}/files?skip=0&take=50
Query Parameters:
skip,take(required): See Paginationpath(optional): Browse a specific folder (e.g.,?path=/docs/)
Response (200 OK):
{
"items": [
{
"type": "file",
"documentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fileName": "report.pdf",
"path": "/docs/report.pdf",
"contentType": "application/pdf",
"sizeBytes": 245678,
"status": "Ready",
"createdAt": "2026-02-26T10:00:00Z"
}
],
"totalCount": 1,
"hasMore": false
}Endpoint: GET /api/containers/{id}/files/{fileId}
Returns file metadata including current indexing status (Pending, Processing, Ready, Failed).
Endpoint: GET /api/containers/{id}/files/{fileId}/content
Auth: Viewer minimum
Returns the full text content of a file. For text files, the original content is returned. For binary formats (PDF, DOCX, PPTX), the extracted text is returned.
Content Negotiation:
Accept: text/plain— returns raw text content onlyAccept: application/json(default) — returns structured response with metadata
Response (200 OK, JSON):
{
"documentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fileName": "report.pdf",
"path": "/docs/report.pdf",
"contentType": "application/pdf",
"sizeBytes": 245678,
"createdAt": "2026-02-26T10:00:00Z",
"content": "Machine learning is a subset of artificial intelligence..."
}Errors:
- 400
document_not_ready— file is still being ingested - 400
document_failed— file failed ingestion - 400
no_parser— no parser available for the file type - 404 — file or container not found
Endpoint: GET /api/containers/{id}/files/{fileId}/reindex-check
Returns whether the file needs reindexing and the reason.
Endpoint: DELETE /api/containers/{id}/files/{fileId}
Cascade deletes chunks, vectors, and removes the file from MinIO.
Write guard: S3 and AzureBlob containers block deletes. Filesystem containers respect the allowDelete flag. Returns 400 with { "error": "write_denied" } when blocked. See connectors.md — Write Guards.
Response (204 No Content)
Endpoint: POST /api/containers/{id}/folders
Request Body:
{ "path": "/docs/2026/" }Write guard: S3 and AzureBlob containers block folder creation. Filesystem containers respect the allowCreateFolder flag. Returns 400 with { "error": "write_denied" } when blocked.
Response (200 OK): Returns Folder object.
Endpoint: DELETE /api/containers/{id}/folders?path=/docs/2026/
Cascade deletes all nested files, subfolders, chunks, vectors, and MinIO objects.
Write guard: S3 and AzureBlob containers block folder deletion. Filesystem containers respect the allowDelete flag. Returns 400 with { "error": "write_denied" } when blocked.
Response (204 No Content)
Endpoint: GET /api/containers/{id}/search
Query Parameters:
q(required): Search querymode(default:Hybrid):Semantic|Keyword|HybridtopK(default:10): Number of results (max: 100)minScore(optional): Minimum similarity score 0.0-1.0 (default: from settings, typically 0.5)path(optional): Restrict search to a folder subtree
Response (200 OK):
{
"hits": [
{
"chunkId": "7f8e9d1c-2b3a-4c5d-8e9f-1a2b3c4d5e6f",
"documentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"content": "Machine learning is a subset of artificial intelligence...",
"score": 0.92,
"metadata": {
"fileName": "report.pdf",
"chunkIndex": "5",
"pageNumber": "12"
}
}
],
"totalMatches": 1,
"durationMs": 87
}Endpoint: POST /api/containers/{id}/search
Request Body:
{
"query": "machine learning algorithms",
"mode": "Hybrid",
"topK": 20,
"minScore": 0.6,
"path": "/docs/",
"filters": {
"documentType": "pdf"
}
}Response: Same as GET search.
Endpoint: POST /api/containers/{id}/reindex
Request Body (optional):
{
"force": false,
"detectSettingsChanges": true,
"documentIds": null
}Fields:
force(default:false): Re-process all files even if content hash unchangeddetectSettingsChanges(default:true): Force reindex if embedding/chunking settings changeddocumentIds(optional): Reindex specific files only
Response (202 Accepted):
{
"batchId": "9d3e7c1a-4b2f-4e8d-9c5a-6f8e1b3d7c2a",
"totalDocuments": 42,
"enqueuedCount": 38,
"skippedCount": 4,
"status": "Pending"
}All settings endpoints require Admin role.
Endpoint: GET /api/settings/{category}
Categories: embedding | chunking | search | llm | upload | awssso | azuread
Response (200 OK) — Example for embedding:
{
"provider": "Ollama",
"baseUrl": "http://ollama:11434",
"model": "nomic-embed-text",
"dimensions": 768,
"timeout": 30,
"batchSize": 4
}Endpoint: PUT /api/settings/{category}
Notes: Changes take effect immediately (live reload via IOptionsMonitor<T>). No restart required.
Response (200 OK):
{
"message": "Settings updated successfully",
"category": "chunking",
"updatedAt": "2026-02-26T11:45:00Z"
}Endpoint: POST /api/settings/test-connection
Categories: Embedding | Llm | AwsSso | AzureAd | CrossEncoder
Request Body:
{
"category": "Embedding",
"settings": {
"provider": "Ollama",
"baseUrl": "http://localhost:11434",
"model": "nomic-embed-text"
},
"timeoutSeconds": 10
}Response (200 OK):
{
"success": true,
"message": "Connected to Ollama (3 models available)",
"details": { "modelCount": 3 },
"duration": "00:00:00.4530000"
}Endpoint: GET /api/batches/{id}/status
Auth: Viewer minimum
Response (200 OK): Ingestion progress for all jobs in the batch.
Hub URL: /hubs/ingestion
Auth: Required — pass JWT via ?access_token=<token> query string, or use cookie session.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/ingestion?access_token=eyJ...")
.build();
// Subscribe
await connection.invoke("SubscribeToJob", "job-id");
// Listen for updates
connection.on("IngestionProgress", (update) => {
// update: { jobId, state, currentPhase, percentComplete, errorMessage, startedAt, completedAt }
console.log(`${update.currentPhase}: ${update.percentComplete}%`);
});
await connection.start();States: Pending | Processing | Completed | Failed
Phases: Parsing | Chunking | Embedding | Storing | Complete
Connapse exposes an MCP server for AI agent integration.
Endpoint: POST /mcp
Auth: Requires a valid Agent API key via X-Api-Key: cnp_<token> header.
Tool list: GET /mcp/tools
| Tool | Required Parameters | Optional Parameters | Description |
|---|---|---|---|
container_create |
name |
description |
Create a new container for organizing files |
container_list |
— | — | List all containers with document counts |
container_delete |
containerId |
— | Delete a container (must be empty for MinIO) |
container_stats |
containerId |
— | Get statistics: document counts by status, chunk count, storage size, embedding model, last indexed time |
upload_file |
containerId, fileName, + content or textContent |
path, strategy |
Upload a single file (base64 or raw text) |
bulk_upload |
containerId, files (JSON array) |
— | Upload up to 100 files in one operation |
list_files |
containerId |
path |
List files and folders at a given path |
get_document |
containerId, fileId |
— | Retrieve full parsed text content by document ID or path |
delete_file |
containerId, fileId |
— | Delete a file and its associated chunks and vectors |
bulk_delete |
containerId, fileIds (JSON array) |
— | Delete up to 100 files in one operation |
search_knowledge |
query, containerId |
mode, topK, path, minScore |
Semantic, keyword, or hybrid search within a container |
Notes:
containerIdaccepts either a container GUID or container name (resolved automatically).upload_fileaccepts eithercontent(base64) for binary files ortextContent(raw text) for text files — provide one, not both.bulk_uploadfilesis a JSON array of objects:{"filename":"name.txt", "content":"...", "encoding":"text|base64", "folderPath":"/optional/"}.bulk_deletefileIdsis a JSON array of document ID strings:["id1","id2"].get_documentfileIdaccepts either a document UUID or a virtual path (e.g.,/docs/readme.md).
Write operations (upload_file, bulk_upload, delete_file, bulk_delete) are subject to container write guards:
| Connector Type | Upload | Delete | Notes |
|---|---|---|---|
| Managed Storage | Allowed | Allowed | Default connector (MinIO wire value); full read/write |
| InMemory | Allowed | Allowed | Ephemeral storage |
| Filesystem | Configurable | Configurable | Per-container allowUpload/allowDelete flags (default: allowed) |
| S3 | Blocked | Blocked | Read-only; files are synced from the source bucket |
| AzureBlob | Blocked | Blocked | Read-only; files are synced from the source container |
When a write is blocked, the tool returns an error message explaining why (e.g., "S3 containers are read-only. Files are synced from the source.").
Upload up to 100 files in a single operation. Each file is written to storage and queued for ingestion (parsing, chunking, embedding).
Parameters:
containerId(required): Container ID (UUID) or namefiles(required): JSON array of file objects (max 100)
File object schema:
{
"filename": "report.pdf",
"content": "base64-encoded-content-here",
"encoding": "base64",
"folderPath": "/docs/"
}| Field | Required | Description |
|---|---|---|
filename |
Yes | Target filename |
content |
Yes | File content (raw text or base64-encoded) |
encoding |
No | "text" (default) or "base64" for binary files |
folderPath |
No | Destination folder path (default: "/") |
Success response:
Uploaded 3 of 3 file(s) to container 'my-project'.
All files queued for ingestion (parsing, chunking, embedding).
Partial failure response:
Uploaded 2 of 3 file(s) to container 'my-project'.
Failures:
- report.pdf: invalid base64 content
Errors:
| Error | Cause |
|---|---|
'files' must be a valid JSON array |
Malformed JSON |
'files' array must not be empty |
Empty array |
Maximum 100 files per bulk_upload call |
Too many files |
Container '{id}' not found |
Invalid container ID/name |
missing 'filename' |
File object missing filename |
missing 'content' |
File object missing content |
invalid base64 content |
Bad base64 when encoding is "base64" |
Notes:
- All files share a single batch ID for tracking
- Uses
Semanticchunking strategy - Intermediate folders are created automatically
- Subject to write guards
Delete up to 100 files from a container in a single operation. Removes database records and backing storage files.
Parameters:
containerId(required): Container ID (UUID) or namefileIds(required): JSON array of document ID strings (max 100)
Example input:
["3fa85f64-5717-4562-b3fc-2c963f66afa6", "7f8e9d1c-2b3a-4c5d-8e9f-1a2b3c4d5e6f"]Success response:
Deleted 2 of 2 file(s).
Partial failure response:
Deleted 1 of 2 file(s).
Warnings (1):
- 7f8e9d1c (...): storage cleanup failed
Failures:
- abc123: not found
Errors:
| Error | Cause |
|---|---|
'fileIds' must be a valid JSON array of strings |
Malformed JSON |
'fileIds' array must not be empty |
Empty array |
Maximum 100 files per bulk_delete call |
Too many files |
Container '{id}' not found |
Invalid container ID/name |
Notes:
- Files not found or belonging to a different container are reported as failures
- Storage deletion failures are non-fatal warnings (DB record is still removed)
- Does not clean up empty parent folders
- Subject to write guards
The connapse command-line client lives in its own repository: Destrayon/connapse-cli. Install via:
dotnet tool install -g Connapse.CLI
# or: download a binary from https://github.com/Destrayon/connapse-cli/releases/latestSee the connapse-cli README for the full command reference and authentication flow.
connapse auth login --server https://your-server
connapse --helpAll API errors follow RFC 7807 Problem Details format.
| Status | Description |
|---|---|
| 400 | Invalid request parameters or body |
| 401 | Missing or invalid authentication |
| 403 | Insufficient role/permissions |
| 404 | Resource not found |
| 409 | Conflict (duplicate name, already revoked, etc.) |
| 413 | File exceeds UploadSettings.MaxFileSizeMb |
| 415 | File type not in UploadSettings.AllowedExtensions |
| 429 | Rate limit exceeded (see rate limiting) |
| 500 | Unexpected server error |
| 503 | External service (Ollama, MinIO) unreachable |
Test a cloud connector configuration before creating a container.
Endpoint: POST /api/containers/test-connection
Request Body:
{
"connectorType": "S3",
"connectorConfig": "{\"bucketName\":\"docs\",\"region\":\"us-east-1\"}",
"timeoutSeconds": 15
}Response (200 OK):
{
"success": true,
"message": "Connected successfully",
"details": { "bucketName": "docs", "region": "us-east-1", "objectsFound": 3 },
"elapsed": "00:00:01.234"
}Supported connectors: S3, AzureBlob, Managed Storage (wire value: MinIO).
Trigger an on-demand sync for cloud containers.
Endpoint: POST /api/containers/{id}/sync
Notes:
- Returns 400 for Filesystem ("live watch is enough")
- Returns 404 for non-existent containers
- Cloud scope enforcement: user must have a linked identity for the container's cloud provider
Response (200 OK):
{
"batchId": "abc123",
"totalFiles": 42,
"enqueuedCount": 5,
"skippedCount": 37
}Endpoint: GET /api/containers/{id}/settings
Returns per-container settings overrides. Null values mean "use global setting".
Response (200 OK):
{
"chunking": null,
"embedding": { "provider": "OpenAI", "model": "text-embedding-3-small", "dimensions": 1536 },
"search": null,
"upload": null
}Endpoint: PUT /api/containers/{id}/settings
Request Body:
{
"embedding": { "provider": "OpenAI", "model": "text-embedding-3-small", "dimensions": 1536 }
}Null or omitted categories are cleared (reset to global).
Response (200 OK)
Discover which embedding models have vectors in the database.
Endpoint: GET /api/settings/embedding-models
Response (200 OK):
{
"currentModel": "nomic-embed-text",
"models": [
{ "modelId": "nomic-embed-text", "dimensions": 768, "vectorCount": 1200 },
{ "modelId": "text-embedding-3-small", "dimensions": 1536, "vectorCount": 50 }
]
}Endpoint: GET /api/containers/{id}/search/models
Same response as above, but scoped to a specific container.
Endpoint: POST /api/settings/reindex
Fire-and-forget reindex with settings change detection.
Response (202 Accepted)
Endpoint: GET /api/settings/reindex/status
Response (200 OK):
{
"queueDepth": 12,
"isActive": true
}Base path: /api/v1/auth/cloud
All endpoints require authentication.
Endpoint: POST /api/v1/auth/cloud/aws/device-auth
Returns a user code and verification URL for the IAM Identity Center device auth flow.
Response (200 OK):
{
"userCode": "ABCD-EFGH",
"verificationUri": "https://device.sso.us-east-1.amazonaws.com/",
"verificationUriComplete": "https://device.sso.us-east-1.amazonaws.com/?user_code=ABCD-EFGH",
"deviceCode": "...",
"expiresInSeconds": 600,
"intervalSeconds": 5
}Error (400): aws_sso_not_configured if admin hasn't set Issuer URL + Region.
Endpoint: POST /api/v1/auth/cloud/aws/device-auth/poll
Request Body:
{ "deviceCode": "..." }Response (200 OK — pending):
{ "status": "pending" }Response (200 OK — complete):
{
"status": "complete",
"identity": {
"id": "...",
"provider": "AWS",
"data": { "principalArn": "123456789012", "accountId": "123456789012", "displayName": "My Account" },
"createdAt": "...",
"lastUsedAt": null
}
}Endpoint: GET /api/v1/auth/cloud/azure/connect
Redirects to Azure AD authorize endpoint with PKCE challenge. Sets __connapse_az_state and __connapse_az_pkce cookies (10-min TTL).
Response: 302 Redirect to Azure AD.
Endpoint: GET /api/v1/auth/cloud/azure/callback
Handles the OAuth2 callback from Azure AD. Validates state, exchanges code for ID token, stores identity.
Response: 302 Redirect to /profile.
Endpoint: GET /api/v1/auth/cloud
Response (200 OK): Array of linked cloud identities.
Endpoint: DELETE /api/v1/auth/cloud/{provider}
Path Parameters: provider = AWS | Azure
Removes the linked identity and evicts cached scope entries.
Response (204 No Content | 404 Not Found)
Current Version: v0.3.2
Auth endpoints are under /api/v1/auth/ and /api/v1/agents/. Container endpoints remain under /api/containers/. Cloud identity endpoints are under /api/v1/auth/cloud/.
- architecture.md — System architecture and design
- deployment.md — Deployment and configuration
- .claude/state/api-surface.md — Internal interface documentation