TRAM REST API Reference
Base URL: http://localhost:8765 (configurable via TRAM_HOST/TRAM_PORT)
All responses are JSON unless noted. Errors return {"detail": "message"}.
Health & Meta
GET /api/health
Liveness probe. Returns 200 immediately if daemon is running.
{"status": "ok"}
GET /api/ready
Readiness probe. Returns 200 once startup is complete, 503 if DB or scheduler is unavailable.
{
"status": "ready",
"db": "ok",
"db_engine": "sqlite",
"db_path": "/data/tram.db",
"scheduler": "running",
"cluster": "manager · 3/3 workers",
"pipelines_loaded": 3,
"uptime": "2h 15m 30s"
}
cluster values: "manager · N/M workers" in manager mode, "standalone" in standalone mode.
GET /api/meta
Build and version information.
{"version": "1.4.0", "build_time": "2026-05-01T12:00:00+00:00", "python_version": "3.13.0"}
GET /api/plugins
All registered plugin keys by category.
{
"sources": ["kafka", "webhook", "websocket", "..."],
"sinks": ["kafka", "opensearch", "elasticsearch", "..."],
"serializers": ["json", "csv", "xml", "avro", "parquet", "msgpack", "protobuf"],
"transforms": ["rename", "cast", "filter", "..."]
}
GET /api/cluster/nodes
Worker pool status (manager mode) or standalone indicator.
Standalone:
{"mode": "standalone", "workers": []}
Manager mode:
{
"mode": "manager",
"workers": [
{
"url": "http://trishul-ram-worker-0...:8766",
"ok": true,
"active_runs": 0,
"running_pipelines": [],
"assigned_pipelines": ["snmp_ifmib_to_sftp"]
}
]
}
GET /api/cluster/streams
Active stream placement and throughput summary.
{
"mode": "manager",
"streams": [
{
"pipeline_name": "prom-ingest",
"placement_group_id": "prom-ingest-20260417-ab12",
"status": "degraded",
"target_count": "all",
"started_at": "2026-04-17T15:00:00+00:00",
"slot_count": 3,
"active_slots": 2,
"records_in": 7102,
"records_out": 7102,
"records_skipped": 0,
"dlq_count": 0,
"error_count": 1,
"bytes_in": 4505600,
"bytes_out": 4505600,
"records_in_per_sec": 1420.4,
"records_out_per_sec": 1420.4,
"bytes_in_per_sec": 901120.0,
"bytes_out_per_sec": 901120.0,
"slots": [
{
"worker_index": 0,
"worker_id": "tram-worker-0",
"worker_url": "http://trishul-ram-worker-0.trishul-ram-worker.default.svc.cluster.local:8766",
"run_id_prefix": "prom-ingest-20260417-ab12-w0",
"current_run_id": "prom-ingest-20260417-ab12-w0-r1",
"status": "running",
"restart_count": 1,
"stats": {
"schedule_type": "stream",
"timestamp": "2026-04-17T15:00:05+00:00",
"uptime_seconds": 5.0,
"records_in": 3551,
"records_out": 3551,
"records_skipped": 0,
"dlq_count": 0,
"error_count": 0,
"bytes_in": 2252800,
"bytes_out": 2252800,
"errors_last_window": [],
"stale": false,
"records_in_per_sec": 710.2,
"records_out_per_sec": 710.2,
"bytes_in_per_sec": 450560.0,
"bytes_out_per_sec": 450560.0
}
},
{
"worker_index": 1,
"worker_id": "tram-worker-1",
"worker_url": "http://trishul-ram-worker-1.trishul-ram-worker.default.svc.cluster.local:8766",
"run_id_prefix": "prom-ingest-20260417-ab12-w1",
"current_run_id": "prom-ingest-20260417-ab12-w1-r0",
"status": "stale",
"restart_count": 0,
"stats": {
"schedule_type": "stream",
"timestamp": "2026-04-17T14:58:00+00:00",
"uptime_seconds": 5.0,
"records_in": 3551,
"records_out": 3551,
"records_skipped": 0,
"dlq_count": 0,
"error_count": 1,
"bytes_in": 2252800,
"bytes_out": 2252800,
"errors_last_window": [
"timeout talking to sink"
],
"stale": true,
"records_in_per_sec": 0.0,
"records_out_per_sec": 0.0,
"bytes_in_per_sec": 0.0,
"bytes_out_per_sec": 0.0
}
}
]
}
]
}
Pipelines
GET /api/pipelines
List all registered pipelines with live status.
[
{
"name": "pm-ingest",
"enabled": true,
"status": "running",
"schedule_type": "interval",
"last_run": "2026-03-03T12:00:00Z",
"last_run_status": "success"
}
]
POST /api/pipelines
Register a new pipeline. Body: raw YAML text (Content-Type: text/plain or application/yaml) or JSON with yaml_text field.
curl -X POST http://localhost:8765/api/pipelines \
-H "Content-Type: text/plain" \
--data-binary @my-pipeline.yaml
Response: 201 Created — pipeline state dict.
Auto-saves a pipeline version to SQLite and auto-starts if enabled: true and schedule is not manual.
GET /api/pipelines/{name}
Get pipeline config and live status.
GET /api/pipelines/{name}/placement
Per-slot placement view for an active multi-worker stream, or a synthetic single-slot view for an active standalone stream pipeline (v1.3.2+).
Returns 404 when: the pipeline has no active multi-worker placement (manager mode), or the
stream is not yet reporting stats / has stopped (standalone mode), or the pipeline is not a
stream type.
{
"pipeline_name": "prom-ingest",
"placement_group_id": "prom-ingest-20260417-ab12",
"status": "running",
"target_count": "all",
"started_at": "2026-04-17T15:00:00+00:00",
"slot_count": 1,
"active_slots": 1,
"records_in": 7102,
"records_out": 7102,
"records_skipped": 0,
"dlq_count": 0,
"error_count": 0,
"bytes_in": 4505600,
"bytes_out": 4505600,
"records_in_per_sec": 710.2,
"records_out_per_sec": 710.2,
"bytes_in_per_sec": 450560.0,
"bytes_out_per_sec": 450560.0,
"slots": [
{
"worker_index": 0,
"worker_id": "tram-worker-0",
"worker_url": "http://trishul-ram-worker-0.trishul-ram-worker.default.svc.cluster.local:8766",
"run_id_prefix": "prom-ingest-20260417-ab12-w0",
"current_run_id": "prom-ingest-20260417-ab12-w0-r1",
"status": "running",
"restart_count": 0,
"stats": {
"schedule_type": "stream",
"timestamp": "2026-04-17T15:00:05+00:00",
"uptime_seconds": 10.0,
"records_in": 7102,
"records_out": 7102,
"records_skipped": 0,
"dlq_count": 0,
"error_count": 0,
"bytes_in": 4505600,
"bytes_out": 4505600,
"errors_last_window": [],
"stale": false,
"records_in_per_sec": 710.2,
"records_out_per_sec": 710.2,
"bytes_in_per_sec": 450560.0,
"bytes_out_per_sec": 450560.0
}
}
]
}
PUT /api/pipelines/{name}
Update/replace a registered pipeline’s YAML config in-place (v1.0.4). Stops the pipeline, re-registers with the new config, and restarts it if enabled: true. Body: raw YAML text (Content-Type: application/yaml or text/plain).
curl -X PUT http://localhost:8765/api/pipelines/pm-ingest \
-H "Content-Type: application/yaml" \
--data-binary @pm-ingest-updated.yaml
DELETE /api/pipelines/{name}
Deregister pipeline (stops it first). Returns 204 No Content.
POST /api/pipelines/{name}/start
Start scheduling or stream execution.
POST /api/pipelines/{name}/stop
Stop pipeline gracefully.
POST /api/pipelines/{name}/run
Trigger one immediate batch run (not valid for stream pipelines).
{"name": "pm-ingest", "status": "triggered"}
Queued response (v1.4.0) — in manager+worker mode with no healthy workers and queued runs enabled (TRAM_QUEUE_MANUAL_RUNS=1, the default), the run is durably queued instead of failing: it survives manager restarts and is dispatched automatically when worker capacity returns. The response is 202 Accepted with the stable run_id and absolute TTL:
{"name": "pm-ingest", "status": "queued", "run_id": "…", "expires_at": "…"}
Flush runs (v1.4.0) — ?flush=true makes stateful transforms emit their open windows as partials (window_complete: false) and clear them from the saved state. The flag is not carried through the queue: a queued flush run executes as a normal run when capacity returns — re-issue ?flush=true once capacity is back to flush.
POST /api/pipelines/reload
Re-scan TRAM_PIPELINE_DIR, reload all YAML files.
{"reloaded": 3, "total": 3}
Pipeline Dry-Run (v1.0.7)
POST /api/pipelines/dry-run
Validate and parse a pipeline YAML without registering it. Instantiates all transforms and connectors to catch config errors early.
Body: raw YAML text (Content-Type: text/plain or application/yaml).
curl -X POST http://localhost:8765/api/pipelines/dry-run \
-H "Content-Type: text/plain" \
--data-binary @my-pipeline.yaml
Response 200 (valid):
{"valid": true, "issues": []}
Response 200 (invalid — always 200, check valid field):
{"valid": false, "issues": ["serializer_in: unknown type 'xtf'"]
}
Pipeline Templates (v1.1.0)
GET /api/templates
List all bundled pipeline YAML templates from the pipelines/ directory.
[
{"name": "kafka-to-opensearch", "description": "...", "yaml": "pipeline:\n name: ..."},
{"name": "snmp-poll-ifmib-to-influxdb", "description": "...", "yaml": "..."}
]
Pipeline Alerts (v1.0.0 / UI v1.1.0)
Alert rules evaluate simpleeval expressions after every batch run and fire webhook or email actions.
GET /api/pipelines/{name}/alerts
List alert rules for a pipeline.
[
{
"name": "high-error-rate",
"condition": "error_rate > 0.05",
"action": "webhook",
"webhook_url": "https://hooks.example.com/alert",
"cooldown_seconds": 300
}
]
POST /api/pipelines/{name}/alerts
Add a new alert rule.
{
"name": "low-output",
"condition": "records_out < 10",
"action": "email",
"email_to": "ops@example.com",
"subject": "Low output on pm-ingest",
"cooldown_seconds": 600
}
PUT /api/pipelines/{name}/alerts/{rule_name}
Update an existing alert rule. Body: same structure as POST.
DELETE /api/pipelines/{name}/alerts/{rule_name}
Delete an alert rule. Returns 204 No Content.
Alert condition variables: records_in, records_out, records_skipped, error_rate, status, failed, duration_seconds.
Pipeline Versioning (v0.5.0)
GET /api/pipelines/{name}/versions
List all saved versions for a pipeline (requires SQLite persistence).
[
{"id": 2, "name": "pm-ingest", "version": 2, "created_at": "2026-03-03T12:05:00Z", "is_active": 1},
{"id": 1, "name": "pm-ingest", "version": 1, "created_at": "2026-03-03T12:00:00Z", "is_active": 0}
]
POST /api/pipelines/{name}/rollback?version=N
Restore pipeline to a previously saved version. Stops if running, reloads config, restarts if enabled.
{
"name": "pm-ingest",
"status": "stopped",
"rolled_back_to_version": 1
}
Runs
GET /api/runs
Run history. Query params:
| Param | Default | Description |
|---|---|---|
pipeline |
— | Filter by pipeline name |
limit |
100 | Max records to return |
status |
— | Filter: success | failed | aborted |
offset |
0 | Pagination offset (v0.7.0) |
from_dt |
— | ISO8601 lower bound on started_at (v0.7.0) |
format |
— | Set to csv to get text/csv export (v1.0.0) |
With SQLite/DB persistence, run history survives daemon restarts.
[
{
"run_id": "abc12345",
"pipeline": "pm-ingest",
"node_id": "tram-0",
"status": "success",
"started_at": "2026-03-03T12:00:00Z",
"finished_at": "2026-03-03T12:00:05Z",
"records_in": 1500,
"records_out": 1487,
"records_skipped": 13,
"bytes_in": 2359296,
"bytes_out": 2341888,
"dlq_count": 0,
"error": null,
"errors": ["Records skipped — no sink wrote successfully (condition filtered all records)"]
}
]
error— top-level fatal error string if the whole run crashed;nullon successerrors— per-record error/skip-reason messages accumulated during the run; non-empty even onstatus: "success"when individual records were skipped or failed withon_error: continue
GET /api/runs/{run_id}
Get a single run result.
Internal Transform State (v1.4.0)
Durable per-pipeline state for stateful transforms (counter_delta, window_aggregate). In manager+worker mode the worker GETs the state at run start and PUTs it back only after a successful run (retries re-hydrate from the same in-run snapshot); in standalone mode the state lives in the local transform_state table. Requires TRAM_STATEFUL_TRANSFORMS=1 (default); 0 disables both the transforms and these endpoints (404). update()/delete() on the pipeline purge the row; a config-hash mismatch discards the stored state so the new transform identities start fresh.
GET /api/internal/transform-state/{pipeline}
Returns the persisted state blob and its config hash.
{"pipeline": "pm-counters", "state": {"counter_delta:0": {"…identity…": {"v": 1500, "t": 1789534920.0}}}, "config_sha256": "1c3036a4cf884027"}
PUT /api/internal/transform-state/{pipeline}
Replaces the state. Bodies over TRAM_STATE_MAX_BYTES (default 20 MiB) are rejected with 413.
Live Stats (v1.1.0)
GET /api/stats
Per-pipeline aggregated stats for the last hour (records in/out, error rate, avg duration). Used by the Live Metrics Dashboard.
[
{
"pipeline": "pm-ingest",
"records_in": 45000,
"records_out": 44823,
"error_rate": 0.004,
"avg_duration_seconds": 1.23,
"run_count": 60
}
]
Authentication (v1.0.0)
When TRAM_API_KEY is set (or apiKey in Helm values), all protected /api/* requests must include the key via the X-API-Key header:
curl -H "X-API-Key: mysecret" http://localhost:8765/api/pipelines
The ?api_key= query param is removed — keys in URLs end up in access/proxy logs and browser history.
Exempt paths (always unauthenticated): /api/health, /api/ready, /agent/health, /metrics, /, /api/auth/login, /favicon.ico, /docs, /redoc, /openapi.json, and the /webhooks/* and /ui prefixes.
Internal machine-to-machine surfaces (/api/internal/* on the manager, /agent/* on workers) honor TRAM_INTERNAL_AUTH_MODE:
off— requests pass through with no check and no logwarn(default) — missing/invalid keys are logged at WARNING but requests are still servedenforce— missing/invalid keys are rejected with401; requiresTRAM_API_KEYto be set (without a key configured, internal surfaces pass through)
An invalid TRAM_INTERNAL_AUTH_MODE value is logged at WARNING and falls back to warn.
Returns 401 Unauthorized when the key is missing or wrong.
Browser Authentication (v1.0.8)
Set TRAM_AUTH_USERS (comma-separated username:password pairs) to bootstrap browser login. If TRAM_DB_URL is configured, changed passwords are stored in the user_passwords table and continue to work even after TRAM_AUTH_USERS is removed. Machine clients continue to use X-API-Key; browser users get 8-hour session tokens.
POST /api/auth/login
Authenticate with username and password. Verification prefers the DB-stored hash when present; otherwise it falls back to TRAM_AUTH_USERS.
curl -X POST http://localhost:8765/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "secret"}'
Response:
{"token": "eyJ...", "username": "admin"}
Use the token as Authorization: Bearer <token> on subsequent requests.
GET /api/auth/me
Returns the currently authenticated user from the Bearer token. Returns 401 if unauthenticated. Works for both env-bootstrapped and DB-backed browser auth.
{"username": "admin"}
POST /api/auth/change-password (v1.1.0)
Change the password for the currently authenticated user. Requires TRAM_DB_URL. New passwords are stored as scrypt$<salt>$<digest> hashes in the user_passwords DB table, persist across restarts, and override TRAM_AUTH_USERS for that user.
curl -X POST http://localhost:8765/api/auth/change-password \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"current_password": "old", "new_password": "new-secure-pass"}'
Response 200:
{"ok": true, "username": "admin"}
Connector Test (v1.1.0)
POST /api/connectors/test
Test connectivity for a single connector config. Returns whether the connection succeeded and any error message.
curl -X POST http://localhost:8765/api/connectors/test \
-H "Content-Type: application/json" \
-d '{"type": "kafka", "bootstrap_servers": "kafka:9092", "topic": "test"}'
Response:
{"ok": true, "latency_ms": 12, "error": null}
On failure: {"ok": false, "latency_ms": null, "error": "Connection refused"}
POST /api/connectors/test-pipeline
Test all source and sink connectors declared in a pipeline YAML. Returns per-connector results.
curl -X POST http://localhost:8765/api/connectors/test-pipeline \
-H "Content-Type: text/plain" \
--data-binary @my-pipeline.yaml
Response:
{
"source": {"type": "sftp", "ok": true, "latency_ms": 45, "error": null},
"sinks": [
{"type": "kafka", "ok": false, "latency_ms": null, "error": "Connection refused"}
]
}
AI Assist (v1.1.0)
GET /api/ai/status
Returns AI configuration and availability.
{"available": true, "provider": "anthropic", "model": "claude-sonnet-4-6"}
Returns {"available": false} when TRAM_AI_API_KEY is not set.
POST /api/ai/suggest
Generate or explain a pipeline YAML using AI.
| Field | Description |
|---|---|
mode |
"generate" — create a new pipeline from a description; "explain" — explain existing YAML |
prompt |
Natural language description (for generate) or question (for explain) |
yaml |
Existing pipeline YAML (required for explain mode) |
# Generate
curl -X POST http://localhost:8765/api/ai/suggest \
-H "Content-Type: application/json" \
-d '{"mode": "generate", "prompt": "Poll SNMP IF-MIB every 60s and write to InfluxDB"}'
# Explain
curl -X POST http://localhost:8765/api/ai/suggest \
-H "Content-Type: application/json" \
-d '{"mode": "explain", "prompt": "What does this do?", "yaml": "pipeline:\n name: ..."}'
Response:
{"result": "pipeline:\n name: snmp-to-influxdb\n ..."}
Configure via env vars:
| Env Var | Description |
|---|---|
TRAM_AI_API_KEY |
API key for the AI provider |
TRAM_AI_PROVIDER |
anthropic, openai, or bedrock (default: anthropic) |
TRAM_AI_MODEL |
Model name (defaults: claude-haiku-4-5-20251001 for Anthropic, gpt-4o-mini for OpenAI, us.anthropic.claude-sonnet-4-6 for Bedrock) |
TRAM_AI_BASE_URL |
Custom base URL (e.g. for Ollama or Azure OpenAI) |
Webhooks (v0.5.0)
POST /webhooks/{path}
Forward a raw HTTP POST body to a registered webhook source pipeline.
- Returns
202 Acceptedif queued successfully - Returns
404 Not Foundif no source is registered for{path} - Returns
401 Unauthorizedif the source has asecretconfigured and theAuthorization: Bearer <token>header doesn’t match - Returns
503 Service Unavailableif the queue is full
curl -X POST http://localhost:8765/webhooks/my-events \
-H "Content-Type: application/json" \
-d '{"ne_id": "node-1", "severity": 2}'
To enable: add a pipeline with source.type: webhook and source.path: my-events.
Metrics (v0.5.0)
GET /metrics
Prometheus metrics in text exposition format (text/plain; version=0.0.4).
Returns 503 with JSON error if prometheus_client is not installed.
# HELP tram_records_in_total Total records read from source
# TYPE tram_records_in_total counter
tram_records_in_total{pipeline="pm-ingest"} 45000.0
# HELP tram_records_out_total Total records written to sink
# TYPE tram_records_out_total counter
tram_records_out_total{pipeline="pm-ingest"} 44823.0
# HELP tram_records_skipped_total Total records skipped
# TYPE tram_records_skipped_total counter
tram_records_skipped_total{pipeline="pm-ingest"} 177.0
# HELP tram_errors_total Total processing errors
# TYPE tram_errors_total counter
tram_errors_total{pipeline="pm-ingest"} 0.0
# HELP tram_chunk_duration_seconds Time spent processing one chunk
# TYPE tram_chunk_duration_seconds histogram
tram_chunk_duration_seconds_bucket{le="0.01",pipeline="pm-ingest"} 120.0
...
SNMP MIBs (v1.0.3)
Manages compiled pysnmp MIB .py files in TRAM_MIB_DIR (default /mibs).
Standard MIBs (IF-MIB, ENTITY-MIB, HOST-RESOURCES-MIB, IP-MIB, TCP-MIB, UDP-MIB, IANAifType-MIB) are pre-compiled in the Docker image.
GET /api/mibs
List all compiled MIB modules in TRAM_MIB_DIR.
[
{"name": "IF-MIB", "file": "IF-MIB.py", "size_bytes": 14823},
{"name": "ENTITY-MIB", "file": "ENTITY-MIB.py", "size_bytes": 22104}
]
POST /api/mibs/upload
Upload a raw .mib text file and compile it. Requires tram[mib]; returns 501 if not installed.
curl -X POST http://localhost:8765/api/mibs/upload \
-F "file=@MY-CUSTOM-MIB.mib"
Response:
{"compiled": ["MY-CUSTOM-MIB"], "mib_dir": "/mibs", "results": {"MY-CUSTOM-MIB": "compiled"}}
POST /api/mibs/download
Download and compile MIB modules by name from mibs.pysnmp.com. Requires internet access and tram[mib].
curl -X POST http://localhost:8765/api/mibs/download \
-H "Content-Type: application/json" \
-d '{"names": ["CISCO-ENTITY-FRU-CONTROL-MIB", "CISCO-TC-MIB"]}'
DELETE /api/mibs/{name}
Delete a compiled MIB module from TRAM_MIB_DIR.
curl -X DELETE http://localhost:8765/api/mibs/MY-CUSTOM-MIB
Schemas (v1.0.3)
Manages serialization schema files (.proto, .avsc, .json, .xsd, .yaml, .yml)
in TRAM_SCHEMA_DIR (default /schemas). No compilation — files are stored as-is.
Pipeline executors compile or read them at run time.
GET /api/schemas
List all schema files under TRAM_SCHEMA_DIR recursively.
[
{
"path": "cisco/GenericRecord.proto",
"type": "protobuf",
"size_bytes": 3421,
"schema_file": "/schemas/cisco/GenericRecord.proto"
},
{
"path": "events.avsc",
"type": "avro",
"size_bytes": 892,
"schema_file": "/schemas/events.avsc"
}
]
schema_file is the absolute path ready to paste into a pipeline schema_file: field.
type is inferred from the extension: protobuf, avro, json, xml, yaml, other.
GET /api/schemas/{filepath}
Return the raw text content of a schema file. filepath is relative to TRAM_SCHEMA_DIR.
curl http://localhost:8765/api/schemas/cisco/GenericRecord.proto
Returns 404 if not found, 400 if the path escapes TRAM_SCHEMA_DIR.
POST /api/schemas/upload
Upload a schema file. Accepts .proto, .avsc, .json, .xsd, .yaml, .yml.
Returns 400 for other extensions.
| Query param | Description |
|---|---|
subdir |
Optional subdirectory within TRAM_SCHEMA_DIR (e.g. cisco). Must not contain ... |
Upload all Cisco EMS proto files to a shared subdirectory:
for f in *.proto; do
curl -F "file=@$f" \
"http://localhost:8765/api/schemas/upload?subdir=cisco"
done
Response:
{
"path": "cisco/GenericRecord.proto",
"type": "protobuf",
"size_bytes": 3421,
"schema_file": "/schemas/cisco/GenericRecord.proto",
"schema_dir": "/schemas"
}
DELETE /api/schemas/{filepath}
Delete a schema file. filepath is relative to TRAM_SCHEMA_DIR.
curl -X DELETE http://localhost:8765/api/schemas/cisco/GenericRecord.proto
Returns 404 if not found, 400 on path-traversal attempt.
Schema Registry Proxy (v1.0.4)
Transparent reverse proxy to an external Confluent-compatible schema registry (e.g. Confluent Schema Registry, Apicurio Registry). Enabled by setting TRAM_SCHEMA_REGISTRY_URL.
All HTTP methods (GET, POST, PUT, DELETE, PATCH) are proxied. Request headers, query params, and body are forwarded as-is. This lets UI tools and serializer clients reach the external registry through a single origin (TRAM).
# List subjects
curl http://localhost:8765/api/schemas/registry/subjects
# Get latest schema for a subject
curl http://localhost:8765/api/schemas/registry/subjects/device-event-value/versions/latest
# Register a new schema version
curl -X POST http://localhost:8765/api/schemas/registry/subjects/device-event-value/versions \
-H "Content-Type: application/vnd.schemaregistry.v1+json" \
-d '{"schema": "{\"type\":\"record\",\"name\":\"DeviceEvent\",\"fields\":[]}"}'
Returns 503 Service Unavailable when TRAM_SCHEMA_REGISTRY_URL is not set.
Returns 502 Bad Gateway when the upstream registry is unreachable.
Configuration:
| Env Var | Description |
|---|---|
TRAM_SCHEMA_REGISTRY_URL |
Base URL of the external registry (e.g. http://schema-registry:8081) |
Serializer auto-fallback — when TRAM_SCHEMA_REGISTRY_URL is set, Avro and Protobuf serializers automatically use it as their registry URL without requiring schema_registry_url: in pipeline YAML. Pipeline-level schema_registry_url: overrides the env default per-pipeline.
Daemon
GET /api/daemon/status
Scheduler state, active streams, next scheduled runs.
POST /api/daemon/stop
Graceful shutdown.
Error Responses
| Code | Meaning |
|---|---|
| 400 | Invalid pipeline YAML or config |
| 401 | Missing/invalid X-API-Key header (the legacy ?api_key= query param was removed in v1.4.0); or missing/invalid Authorization: Bearer for webhook secret |
| 404 | Pipeline, run, or webhook path not found |
| 409 | Pipeline already registered |
| 422 | Pydantic validation error |
| 429 | Rate limit exceeded (v1.0.0) — retry after the TRAM_RATE_LIMIT_WINDOW window resets |
| 503 | DB unavailable (readiness check); prometheus_client not installed (/metrics) |