HTTP API
OpalServe exposes a REST API at http://127.0.0.1:3456/api/v1 by default. In team-server mode, all endpoints (except health) require authentication via JWT token or API key.
Authentication
In team-server mode, include one of these headers with every request:
# JWT token (obtained from login)
Authorization: Bearer eyJhbGciOiJIUzI1NiI...
# API key
Authorization: Bearer osk_abc123...POST /api/v1/auth/login
Authenticate and receive a JWT token.
Request:
{
"email": "alice@company.com",
"password": "your-password"
}Response:
{
"ok": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiI...",
"user": {
"id": "usr_a1b2c3",
"email": "alice@company.com",
"role": "developer",
"team": "Acme Engineering"
},
"expiresAt": "2026-04-13T00:00:00.000Z"
}
}POST /api/v1/auth/logout
Invalidate the current JWT token.
Response:
{
"ok": true,
"data": { "message": "Logged out" }
}GET /api/v1/auth/me
Get the current authenticated user.
Response:
{
"ok": true,
"data": {
"id": "usr_a1b2c3",
"email": "alice@company.com",
"role": "developer",
"team": "Acme Engineering",
"apiKeys": 2,
"createdAt": "2026-04-01T00:00:00.000Z"
}
}POST /api/v1/auth/api-keys
Create a new API key for the authenticated user.
Request:
{
"name": "CI/CD Pipeline",
"expiresIn": 2592000
}Response:
{
"ok": true,
"data": {
"id": "key_d4e5f6",
"name": "CI/CD Pipeline",
"key": "osk_abc123...",
"expiresAt": "2026-05-12T00:00:00.000Z"
}
}WARNING
The key field is only returned once at creation time. Store it securely.
DELETE /api/v1/auth/api-keys/:id
Revoke an API key.
Response: 204 No Content
Health
GET /api/v1/health
Server health and statistics. Does not require authentication.
Response:
{
"ok": true,
"data": {
"status": "running",
"version": "3.0.0",
"mode": "team-server",
"uptime": 3600,
"servers": { "total": 4, "connected": 4 },
"tools": { "total": 29 },
"users": { "total": 8, "active": 5 }
}
}Servers
GET /api/v1/servers
List all registered MCP servers.
Response:
{
"ok": true,
"data": [
{
"name": "github",
"description": "GitHub repositories and issues",
"status": "connected",
"transport": "stdio",
"toolCount": 8,
"tags": ["code", "github"],
"lastSeen": "2026-04-12T10:30:00.000Z",
"error": null,
"serverInfo": { "name": "github-mcp-server", "version": "0.6.2" }
}
]
}POST /api/v1/servers
Register and connect a new MCP server. Requires admin role.
Request:
{
"name": "my-files",
"description": "Shared filesystem",
"transport": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/opt/shared"]
},
"tags": ["files"]
}Response: 201 Created
{
"ok": true,
"data": {
"name": "my-files",
"status": "connected",
"toolCount": 12
}
}DELETE /api/v1/servers/:name
Remove a server and its indexed tools. Requires admin role.
Response: 204 No Content
POST /api/v1/servers/:name/reconnect
Disconnect and reconnect to a specific server. Requires admin role.
Response:
{
"ok": true,
"data": {
"name": "github",
"status": "connected",
"toolCount": 8
}
}GET /api/v1/servers/:name/health
Health check for a specific server.
Response:
{
"ok": true,
"data": {
"name": "github",
"status": "connected",
"latency": 45,
"toolCount": 8,
"lastSeen": "2026-04-12T10:30:00.000Z"
}
}Tools
GET /api/v1/tools
List all discovered tools.
Query params:
| Param | Type | Description |
|---|---|---|
server | string | Filter by server name(s), comma-separated |
tags | string | Filter by server tags, comma-separated |
Response:
{
"ok": true,
"data": [
{
"id": "github:create_issue",
"serverName": "github",
"name": "create_issue",
"description": "Create a new issue in a repository",
"inputSchema": {
"type": "object",
"properties": {
"owner": { "type": "string" },
"repo": { "type": "string" },
"title": { "type": "string" },
"body": { "type": "string" }
},
"required": ["owner", "repo", "title"]
},
"discoveredAt": "2026-04-12T10:00:00.000Z",
"lastVerified": "2026-04-12T10:30:00.000Z"
}
]
}GET /api/v1/tools/search?q=<query>
Full-text search across all tools.
Query params:
| Param | Type | Default | Description |
|---|---|---|---|
q | string | required | Search query |
limit | number | 20 | Max results |
server | string | — | Filter by server name(s) |
Response:
{
"ok": true,
"data": [
{
"id": "github:create_issue",
"name": "create_issue",
"serverName": "github",
"description": "Create a new issue in a repository",
"score": 0.95
}
]
}GET /api/v1/tools/:id
Get a specific tool by ID. Tool IDs use the format serverName:toolName (URL-encode the colon as %3A).
POST /api/v1/tools/:id/call
Proxy a tool call to the backend MCP server.
Request: Tool arguments as a JSON object.
curl -X POST "http://localhost:3456/api/v1/tools/github%3Acreate_issue/call" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"owner": "my-org",
"repo": "my-repo",
"title": "Bug: login form broken",
"body": "Steps to reproduce..."
}'Response:
{
"ok": true,
"data": {
"content": [
{ "type": "text", "text": "Created issue #42: Bug: login form broken" }
]
}
}Context / Knowledge Base
GET /api/v1/context
List all documents in the knowledge base.
Query params:
| Param | Type | Description |
|---|---|---|
tags | string | Filter by tags, comma-separated |
limit | number | Max results (default: 50) |
offset | number | Pagination offset |
Response:
{
"ok": true,
"data": [
{
"id": "doc_a1b2c3",
"title": "Deployment Guide",
"tags": ["ops", "deployment"],
"size": 4200,
"chunks": 8,
"createdAt": "2026-04-10T00:00:00.000Z",
"updatedAt": "2026-04-12T08:00:00.000Z"
}
],
"total": 12
}POST /api/v1/context
Add a document to the knowledge base. Requires admin or developer role.
Request:
{
"title": "Database Schema",
"content": "# Users Table\n\n| Column | Type |\n|--------|------|\n| id | uuid |\n| email | text |\n...",
"tags": ["database", "schema"]
}Response: 201 Created
{
"ok": true,
"data": {
"id": "doc_g7h8i9",
"title": "Database Schema",
"chunks": 5,
"size": 2048
}
}GET /api/v1/context/search?q=<query>
Search the knowledge base.
Query params:
| Param | Type | Default | Description |
|---|---|---|---|
q | string | required | Search query |
limit | number | 10 | Max results |
tags | string | — | Filter by tags |
Response:
{
"ok": true,
"data": [
{
"documentId": "doc_a1b2c3",
"title": "Deployment Guide",
"chunk": "To deploy to production, merge your PR to main. The CI pipeline will automatically...",
"score": 0.92,
"tags": ["ops", "deployment"]
}
]
}DELETE /api/v1/context/:id
Remove a document from the knowledge base. Requires admin role.
Response: 204 No Content
Stats
GET /api/v1/stats
Usage statistics. Requires admin role.
Query params:
| Param | Type | Default | Description |
|---|---|---|---|
period | string | 24h | Time window: 1h, 24h, 7d, 30d |
Response:
{
"ok": true,
"data": {
"period": "24h",
"totalRequests": 847,
"toolCalls": 312,
"activeUsers": 5,
"uniqueTools": 15,
"topTools": [
{ "id": "github:create_issue", "calls": 47 },
{ "id": "files:read_file", "calls": 38 }
],
"topUsers": [
{ "email": "alice@company.com", "requests": 234 },
{ "email": "bob@company.com", "requests": 189 }
],
"errorRate": 0.02
}
}GET /api/v1/stats/users
Per-user usage breakdown. Requires admin role.
Response:
{
"ok": true,
"data": [
{
"email": "alice@company.com",
"role": "developer",
"requests": 234,
"toolCalls": 89,
"lastActive": "2026-04-12T10:30:00.000Z"
}
]
}Webhooks
POST /api/v1/webhooks/github
GitHub webhook receiver. Validates the webhook signature using webhookSecret and processes events based on configuration.
Headers:
X-Hub-Signature-256— HMAC signatureX-GitHub-Event— Event type
No manual authentication needed — the webhook secret serves as the auth mechanism.
POST /api/v1/slack/commands
Slack slash command receiver. Validates requests using the Slack signing secret.
POST /api/v1/slack/events
Slack Events API receiver for real-time message ingestion.
Admin Routes
GET /api/v1/admin/users
List all users. Requires admin role.
POST /api/v1/admin/invite
Create a user invitation. Requires admin role.
Request:
{
"email": "newuser@company.com",
"role": "developer",
"expiresIn": 604800
}Response:
{
"ok": true,
"data": {
"inviteUrl": "https://opalserve.company.com/invite/abc123xyz",
"expiresAt": "2026-04-19T00:00:00.000Z"
}
}PUT /api/v1/admin/users/:id/role
Update a user's role. Requires admin role.
Request:
{
"role": "admin"
}PUT /api/v1/admin/users/:id/limits
Set rate limits for a specific user. Requires admin role.
Request:
{
"maxRequests": 200,
"toolCallLimit": 100,
"windowMs": 60000
}DELETE /api/v1/admin/users/:id
Delete a user account. Requires admin role.
Error Responses
All errors follow a consistent format:
{
"ok": false,
"error": "Descriptive error message",
"code": "RATE_LIMIT_EXCEEDED"
}HTTP Status Codes
| Code | Description |
|---|---|
400 | Invalid request — bad input or missing parameters |
401 | Unauthorized — missing or invalid authentication |
403 | Forbidden — insufficient permissions for this action |
404 | Not found — resource does not exist |
429 | Rate limited — too many requests |
500 | Internal server error |
Error Codes
| Code | Description |
|---|---|
INVALID_INPUT | Request body validation failed |
UNAUTHORIZED | No valid auth token provided |
FORBIDDEN | User lacks required role/permission |
NOT_FOUND | Server, tool, or document not found |
RATE_LIMIT_EXCEEDED | User hit their rate limit |
SERVER_DISCONNECTED | MCP server is not connected |
TOOL_CALL_FAILED | Backend tool returned an error |