Docs / API reference
HTTP API reference.
What the Python and TypeScript SDKs post on the wire. Production base URL is https://api.mesedi.ai. Local development is http://localhost:8080. If you're using one of the SDKs, you never need this page; it's here for non-Python, non-Node stacks and for self-hosters who want to verify what their agents are sending.
Authentication
Every protected endpoint requires a bearer token. Mint one in the dashboard under API Keys. The raw key is shown once at mint time; we cannot recover it for you later.
Authorization: Bearer mesedi_sk_...Requests without Authorization: Bearer are rejected with 401. Requests with an unrecognized key are rejected with 401. Requests with a project_id in the body that does not match the auth-context project are rejected with 403.
Schema versioning
Every request should include X-Mesedi-Schema-Version: 1. The backend policy:
- Header missing: accepted (assumed current version, soft-mode for bring-up flows and curl smoke tests).
- Header equals
1: accepted. - Header present and not
1: rejected with 400 and a message naming the supported version(s).
When v2 ships, the policy tightens to missing → 400 so unversioned callers surface loudly instead of silently coasting on a stale wire format.
POST /executions
Record the start of an agent execution.
curl -X POST https://api.mesedi.ai/executions \
-H "Authorization: Bearer $MESEDI_API_KEY" \
-H "X-Mesedi-Schema-Version: 1" \
-H "Content-Type: application/json" \
-d '{
"execution_id": "exec-001",
"status": "started",
"sdk_language": "python",
"sdk_version": "0.1.0"
}'
PATCH /executions/{id}
Update an execution. Used by the SDKs to mark completion or crash.
# completed
curl -X PATCH https://api.mesedi.ai/executions/exec-001 \
-H "Authorization: Bearer $MESEDI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "completed",
"duration_ms": 1234,
"total_tokens_in": 50,
"total_tokens_out": 200
}'
# crashed
curl -X PATCH https://api.mesedi.ai/executions/exec-001 \
-H "Authorization: Bearer $MESEDI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "crashed",
"crash_signature": "sha256:abcdef...",
"duration_ms": 412
}'
POST /events
Ship a batch of events for an existing execution. The SDKs batch up to ~100 events every few hundred milliseconds.
curl -X POST https://api.mesedi.ai/events \
-H "Authorization: Bearer $MESEDI_API_KEY" \
-H "Content-Type: application/json" \
-d '[
{
"event_id": "evt-001",
"execution_id": "exec-001",
"event_type": "llm_call",
"sequence": 1,
"timestamp": "2026-05-24T14:00:00Z",
"payload": {
"model": "claude-opus-4-6",
"tokens_in": 12,
"tokens_out": 80
}
}
]'
Accepted event_type values: llm_call, tool_call, checkpoint, exception, validator_result, drift_signal, injection_alert, infrastructure_event, dlp_scan_result, mcp_call, eval_score, memory_operation, agent_handoff, human_intervention, egress, and environment_declaration.
The two newest types carry the egress-visibility detectors. An egress payload records one outbound network contact: destination (host or host:port, normalized client-side so paths, query strings, and embedded credentials never leave your process), plus optional protocol, tool_name, bytes_out, and note. An environment_declaration payload states the run's intended mode: mode (well-known values live, simulation, staging), plus optional declared_by and note. The first declaration on an execution wins.
Rate limiting
Every authenticated request consumes 1 token from a per-project bucket. Defaults: burst capacity 100 tokens, refill 10 tokens/second. A well-behaved SDK (events buffered client-side, flushed in batches) never sees a 429. An infinite-loop agent without backoff hits 429 within ~10 seconds.
Every response (200 and 429 alike) includes:
X-RateLimit-Limit: bucket capacityX-RateLimit-Remaining: tokens left after this requestX-RateLimit-Reset: Unix timestamp when the bucket refills to full
On 429, AWS-style Retry-After: 1 is also set.
Webhook delivery contract
When a failure-class detector fires for the first time in a project (or after a configured cooldown), Mesedi POSTs a JSON payload to each configured webhook URL. See Webhooks in the dashboard to configure URLs.
POST <your webhook url>
Content-Type: application/json
X-Mesedi-Signature: sha256=...
X-Mesedi-Event: failure_group.first_occurrence
{
"event": "failure_group.first_occurrence",
"project_id": "proj_...",
"failure_group": {
"id": "fg_...",
"class": "prompt_injection",
"signature": "...",
"first_seen_at": "2026-05-24T14:00:00Z",
"severity": "high"
},
"playbook_url": "https://app.mesedi.ai/app/playbooks/prompt_injection"
}
The signature is HMAC-SHA256 of the raw request body keyed by the webhook secret shown to you at create time. Slack and Discord URLs are auto-detected and receive native attachments / embeds; plain URLs get the JSON above.
GET /health
Public, no auth. Liveness only. It reports that the process is running and answering requests. It does not touch the database, deliberately: our load balancer polls this endpoint, and cycling machines on a brief database problem would turn a partial outage into a total one.
Because it cannot fail while the process is alive, do not point uptime monitoring at it. Use /ready below.
curl https://api.mesedi.ai/health
# {"ok":true,"service":"mesedi-backend","version":"...","time":"..."}
GET /ready
Public, no auth. Readiness. Pings the database and checks that every migration this build expects has actually been applied. Returns 200 when both hold and 503 when either does not. This is the endpoint to monitor, and it is the one our own external uptime monitoring polls. Our current service status is published at status.mesedi.ai.
The migration check matters as much as the ping. A database that accepts connections while a migration has half-applied answers every probe happily and fails every write.
curl https://api.mesedi.ai/ready
# 200
# {"ok":true,"service":"mesedi-backend","version":"...",
# "checks":{"database":"ok","schema":"ok"},
# "migrations":{"embedded":56,"applied":56},"time":"..."}
# 503, database unreachable
# {"ok":false,"checks":{"database":"unreachable","schema":"unknown"},
# "reason":"database_unreachable","time":"..."}
reason is one of database_unreachable, schema_behind or schema_unreadable. Driver errors are never included in the response body, since the endpoint is unauthenticated; they go to the server log instead.
What's next?
Failure classes and playbooks explains what each detector is looking for in the telemetry you ship.
Self-hosting guide covers running the Go backend behind your own infrastructure.