API overview
Habitat exposes its hardware operations through multiple interfaces. All interfaces provide equivalent access to the same underlying hardware — choose the one that fits your environment.
| Interface | Status | Best for |
|---|---|---|
| REST | Available | Any language, curl, HTTP clients |
| MCP | Available | AI agents — drive it in natural language |
REST conventions
Habitat's REST API is served by FastAPI. All endpoints accept and return JSON.
Base URL
Each Habitat device serves its API on your local network at http://<device-name>.local:8000/
— for example http://mistyforest.local:8000/. Replace mistyforest with your device's
name (ask your Open Culture contact if you're unsure). All examples in these docs use
the demo device mistyforest.
Authentication
Whether the API needs a token depends on how your Habitat device is provisioned:
- Open mode — a device with no tokens issued accepts every request without credentials. Fine on a trusted local network.
- Enforced mode — once your device has been given tokens, every request
needs one; only the health checks (
/healthz,/readyz) stay open.
Your Open Culture contact issues you an API token. Send it as a Bearer header on every request:
A convenient pattern is to keep the token in an environment variable — Habitat's
own tooling uses the name HABITAT_API_TOKEN — and reference it:
export HABITAT_API_TOKEN="<your-token>"
curl -H "Authorization: Bearer $HABITAT_API_TOKEN" \
http://mistyforest.local:8000/pumps/
Streaming (SSE) endpoints — browsers' EventSource can't set headers, so
pass the token in the query string instead:
…/system/events?auth_token=<your-token>.
The MCP connector authenticates separately, through its own sign-in.
Units
- Volumes are always in microliters (µL). Field suffix:
_ul. - Volumes at the API and action layer are floats. Drivers convert to hardware-specific increments internally.
- Time is reported in seconds where applicable.
Pump addresses
Pump addresses are zero-indexed integers. /pumps/{addr} expects the integer address. The pump role (dispense / aspirate) is defined in the YAML config, not inferred from the address.
Error responses
Failures come back as
application/problem+json with type,
title, status, and detail fields — plus structured context (addr,
chip_id, step) where relevant.
| Status | Meaning |
|---|---|
400 |
Invalid parameters, or a precondition wasn't met |
404 |
Unknown resource (pump, job, protocol, …) |
409 |
Conflict — a safety interlock blocked the command, or an idempotency key was reused with a different body |
422 |
Request body failed schema validation |
500 |
Hardware-level failure |
System & configuration endpoints
Read-only status and configuration live at the root of the API.
| Method | Endpoint | Purpose |
|---|---|---|
GET |
/health |
Device identity and driver status |
GET |
/system/status |
Runtime status |
GET |
/system/metrics |
Per-route latency metrics |
GET |
/system/timezone |
Display timezone |
GET |
/config |
Full configuration snapshot (includes the port map) |
GET |
/config/schema |
JSON Schema for the configuration |
See Configuration for the port map the snapshot contains.
Endpoint reference
The full endpoint surface is grouped by area:
- System — device health and status
- Pumps — CENTRIS syringe pump control
- Peristaltic — Pololu Tic peristaltic pumps
- Valves — multi-position smart valves
- Atomics — single-step primitives
- Routines — multi-step workflows
- Protocols — protocol library and execution
- Schedule — event scheduling
- Jobs — job submission and status
- Devices — device registry and telemetry
- Reagents — reagent registry
- Config — device configuration
- Entities — chips, samples, and reagents
- Files — logs, exports, and support bundles