REST API Reference¶
Base URL: https://api.gremia.io
All endpoints require authentication via JWT in the Authorization: Bearer header unless otherwise noted.
Health & Metrics¶
GET /health¶
Health check endpoint. No authentication required.
Response:
GET /metrics¶
Prometheus-format metrics. No authentication required.
Response: text/plain; version=0.0.4
gremia_http_requests_total{method="POST",path="/api/v1/chat",status="200"} 42
gremia_http_request_duration_seconds{method="POST",path="/api/v1/chat"} 1.234
gremia_ws_connections_active 3
gremia_ws_connections_total 15
Chat Endpoints¶
POST /api/v1/chat¶
Stream an architect conversation response via Server-Sent Events (SSE).
Rate limit: 60 requests/minute
Request body:
{
"session_id": "uuid-string",
"messages": [
{ "role": "user", "content": "I need agents for my e-commerce business" }
],
"team_id": "optional-team-uuid"
}
| Field | Type | Required | Description |
|---|---|---|---|
session_id |
string | Yes | Unique session identifier |
messages |
array | Yes | Conversation messages (min 1) |
messages[].role |
string | Yes | "user" or "assistant" |
messages[].content |
string | Yes | Message content |
team_id |
string | No | Team to associate the session with |
Response: text/event-stream
data: {"node":"architect","data":{"text":"I'll design a team..."}}
data: {"node":"finalize","data":{"manifest_json":{...}}}
data: [DONE]
Each SSE chunk is a JSON object with:
| Field | Description |
|---|---|
node |
Current graph node (architect, tools, finalize) |
data |
Node-specific payload |
On error:
Manifest Endpoints¶
GET /api/v1/manifests¶
List manifests for the authenticated user.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
team_id |
string | User's org | Filter by team |
Response:
[
{
"id": "manifest-uuid",
"name": "E-Commerce Team",
"description": "Customer support and inventory",
"manifest": { "version": "1.0", "agents": [...] },
"user_id": "user-uuid",
"org_id": "org-uuid",
"created_at": "2026-02-15T10:00:00Z"
}
]
GET /api/v1/manifests/{manifest_id}¶
Retrieve a single manifest by ID.
Response: Same as list item above.
Errors:
| Status | Detail |
|---|---|
| 404 | Manifest not found |
POST /api/v1/manifests¶
Create a new manifest.
Request body (option A — wrapped):
{
"name": "My Team",
"description": "Team description",
"manifest": {
"version": "1.0",
"team_id": "uuid",
"agents": [...],
"workflows": [...],
"mcp_requirements": [...]
}
}
Request body (option B — raw manifest):
{
"version": "1.0",
"team_id": "uuid",
"agents": [...],
"workflows": [...],
"mcp_requirements": [...]
}
Response: 201 Created
{
"id": "new-manifest-uuid",
"name": "My Team",
"manifest": { ... },
"created_at": "2026-02-15T10:00:00Z"
}
PUT /api/v1/manifests/{manifest_id}¶
Update an existing manifest. Same body format as POST.
Errors:
| Status | Detail |
|---|---|
| 400 | Invalid manifest format |
| 404 | Manifest not found |
DELETE /api/v1/manifests/{manifest_id}¶
Delete a manifest.
Response: 204 No Content
Auth Endpoints¶
POST /api/v1/auth/verify¶
Verify a JWT token and return the decoded payload.
Request body:
Response:
{
"valid": true,
"payload": {
"sub": "user-uuid",
"email": "user@example.com",
"team_id": "team-uuid",
"exp": 1739620000
},
"error": null
}
On invalid token:
Certificate Endpoints¶
POST /api/v1/certs/sign¶
Sign a Shell's CSR and return an ephemeral 24-hour certificate.
Rate limit: 10 requests/minute
Request body:
Response:
{
"certificate_pem": "-----BEGIN CERTIFICATE-----\n...",
"ca_certificate_pem": "-----BEGIN CERTIFICATE-----\n...",
"valid_hours": 24
}
Errors:
| Status | Detail |
|---|---|
| 400 | Invalid CSR format |
| 500 | CA certificates not configured |
Execution Endpoints¶
POST /api/v1/executions¶
Create a new task execution session.
Request body:
{
"shell_id": "shell-uuid",
"manifest_id": "manifest-uuid",
"task_description": "Generate the weekly sales report"
}
Response:
{
"execution_id": "exec-uuid",
"status": "created",
"task_description": "Generate the weekly sales report",
"steps_count": 0,
"final_output": "",
"error": null
}
GET /api/v1/executions/{execution_id}¶
Get the current status of an execution.
Response: Same as create response, with updated status and output.
GET /api/v1/executions/{execution_id}/events¶
Stream execution events via SSE.
Response: text/event-stream
data: {"status": "waiting"}
data: {"status": "completed", "output": "Report generated", "steps": 5}
data: [DONE]
GET /api/v1/executions¶
List recent executions.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
shell_id |
string | null | Filter by Shell |
limit |
int | 20 | Max results |
Billing Endpoints¶
GET /api/v1/billing/plan¶
Get current plan and credit information.
Response:
{
"plan": "pro",
"credits_remaining_cents": 2500,
"credits_total_cents": 3000,
"allowed_models": ["fast", "balanced"],
"period_start": "2026-02-01T00:00:00Z",
"period_end": "2026-03-01T00:00:00Z"
}
GET /api/v1/billing/usage¶
Get daily usage history.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
days |
int | 30 | Number of days to retrieve |
Response:
POST /api/v1/billing/checkout¶
Create a Stripe Checkout session for subscription upgrade.
Request body:
{
"plan": "pro",
"success_url": "https://app.gremia.io/billing/success",
"cancel_url": "https://app.gremia.io/billing/cancel"
}
Response:
POST /api/v1/billing/portal¶
Create a Stripe Customer Portal session.
Response:
POST /api/v1/billing/webhook¶
Process Stripe webhook events. Called by Stripe, not by clients.
Headers required:
| Header | Description |
|---|---|
Stripe-Signature |
Stripe webhook signature |
Privacy Endpoints¶
POST /api/v1/privacy/consent¶
Grant consent for a processing purpose.
Request body:
Response:
{
"consent_id": "consent-uuid",
"purpose": "analytics",
"granted": true,
"granted_at": "2026-02-15T10:00:00Z",
"revoked_at": null
}
DELETE /api/v1/privacy/consent/{purpose}¶
Revoke consent for a specific purpose.
GET /api/v1/privacy/consent¶
List all consent records for the authenticated user.
POST /api/v1/privacy/erasure¶
Request erasure of all personal data (GDPR Article 17).
GET /api/v1/privacy/erasure/{request_id}¶
Check the status of an erasure request.
GET /api/v1/privacy/data-export¶
Export all personal data (DSAR).
Response:
{
"user_id": "user-uuid",
"consent_records": [...],
"audit_entries": [...],
"erasure_requests": [...]
}
Admin Endpoints¶
POST /api/v1/admin/rotate-key¶
Rotate the encryption key and re-encrypt all sensitive data.
Request body:
Response:
Errors:
| Status | Detail |
|---|---|
| 500 | Key rotation failed |
Common Headers¶
Request Headers¶
| Header | Required | Description |
|---|---|---|
Authorization |
Yes* | Bearer {jwt_token} |
Content-Type |
Yes | application/json |
X-Request-ID |
No | Client-generated request ID for tracing |
*Not required for /health and /metrics.
Response Headers¶
| Header | Description |
|---|---|
X-Request-ID |
Request tracing ID (echoed or generated) |
Content-Encoding |
gzip for responses > 1KB |
Error Format¶
All errors follow a consistent format:
| Status Code | Meaning |
|---|---|
| 400 | Bad request (validation error) |
| 401 | Unauthorized (invalid/missing JWT) |
| 403 | Forbidden (insufficient permissions) |
| 404 | Resource not found |
| 422 | Unprocessable entity (semantic error) |
| 429 | Rate limit exceeded |
| 500 | Internal server error |