Spatial API
Every endpoint below carries a status badge — working is exercised end-to-end on api.colpero.com, partial works on the happy path with known gaps, stub accepts the request but returns placeholder data until its worker ships, broken has a handler but is missing a default-config dependency, planned is on the roadmap, and archived shipped historically but was removed. Paid routes are gated by x402. Every response — success or error — follows the same shape described in Errors.
Getting Started
Colpero nodes expose spatial queries over HTTP. Free endpoints respond 200 immediately. Paid endpoints respond 402 Payment Required on the first hit, and 200 on the retry that includes a signed x402 envelope.
/healthworkingworking: Returns node_id, version, capabilities, allowed_scenes, payment_enabled, network.
Check node status
Returns liveness, node id, configured capabilities, and whether x402 payment enforcement is enabled on this node. Always free.
{
"status": "ok",
"version": "0.2.0",
"node_id": "default",
"capabilities": ["domain", "spatial_compute"],
"allowed_scenes": "all",
"payment_enabled": true,
"network": "base-sepolia"
}curl https://api.colpero.com/healthAuthentication — x402
Paid endpoints are gated by x402, Coinbase's HTTP-native micropayment standard. The flow is always the same: request → 402 → retry with X-PAYMENT. There are no API keys, no sessions, no accounts.
/scenes/{id}/distanceworking$0.005 USDCworking: Spawns compute_distance.py. Validates body via zod (object_a, object_b). P1.1a hardened the error path (404 instead of 500 when an object is missing).
The three-step x402 flow
The first request arrives without payment. The server responds 402 with an accepts array describing acceptable USDC denominations. The client signs a transfer, attaches it as X-PAYMENT, and retries. The server verifies on-chain and returns the real response.
{ "from": "Bed", "to": "Desk" }// Step 1 →
POST /scenes/lidar_room/distance
{ "from": "Bed", "to": "Desk" }
// Step 1 ←
402 Payment Required
{
"accepts": [
{
"scheme": "exact",
"asset": "USDC",
"amount": "0.005",
"network":"base-sepolia",
"payTo": "0x2cFa…5FC96"
}
]
}
// Step 2 →
POST /scenes/lidar_room/distance
X-PAYMENT: <base64 signed envelope>
{ "from": "Bed", "to": "Desk" }
// Step 2 ←
200 OK
{ "distance_m": 2.41 }# 1. First request returns 402
curl -X POST https://api.colpero.com/scenes/lidar_room/distance \
-H "Content-Type: application/json" \
-d '{"from":"Bed","to":"Desk"}'
# 2. Retry with X-PAYMENT
curl -X POST https://api.colpero.com/scenes/lidar_room/distance \
-H "X-PAYMENT: <signed_envelope>" \
-H "Content-Type: application/json" \
-d '{"from":"Bed","to":"Desk"}'| status | code | note |
|---|---|---|
| 402 | payment_required | Expected on the first request. Retry with X-PAYMENT. |
| 402 | payment_invalid | Envelope failed on-chain verification. |
Scenes
/scenesworkingworking: Lists .usda files in SCENES_DIR, filtered by ALLOWED_SCENES if set.
List available scenes
Free. Returns all USD scenes served by this node, with metadata and links to their query endpoints.
[
{
"id": "lidar_room",
"name": "Lidar Room",
"description": "Real LiDAR scan (Polycam) of a 3.4x2.5m bedroom",
"size_bytes": 4532,
"last_modified": "2026-04-10T21:37:47Z",
"endpoints": {
"objects": "/scenes/lidar_room/objects",
"distance": "/scenes/lidar_room/distance",
"nearest": "/scenes/lidar_room/nearest",
"area": "/scenes/lidar_room/area",
"path": "/scenes/lidar_room/path",
"query": "/scenes/lidar_room/query"
}
}
]curl https://api.colpero.com/scenes/scenes/{id}/objectsworking$0.001 USDCworking: Spawns python list_objects.py over the resolved USD scene.
List every object in a scene
Returns every prim in the USD scene with type, semantic label, zone, position, scale and bounding box.
{
"count": 47,
"objects": [
{
"name": "/Warehouse/Rack_A",
"type": "Mesh",
"label": "rack",
"zone": "storage",
"position": { "x": 4.0, "y": 0.0, "z": -2.0 },
"scale": { "x": 1.0, "y": 1.0, "z": 1.0 }
}
]
}curl -H "X-PAYMENT: <envelope>" \
https://api.colpero.com/scenes/lidar_room/objects| status | code | note |
|---|---|---|
| 402 | payment_required | Retry with X-PAYMENT. |
| 404 | scene_not_found | Unknown scene id. |
Spatial Queries
/scenes/{id}/distanceworking$0.005 USDCworking: Spawns compute_distance.py. Validates body via zod (object_a, object_b). P1.1a hardened the error path (404 instead of 500 when an object is missing).
Euclidean distance between two named objects
Computes the Euclidean distance in metres between the bounding-box centres of two named objects.
{ "from": "Rack_A", "to": "Dock_2" }{ "distance_m": 4.2 }curl -X POST -H "X-PAYMENT: <envelope>" \
-H "Content-Type: application/json" \
-d '{"from":"Rack_A","to":"Dock_2"}' \
https://api.colpero.com/scenes/warehouse_demo/distance| status | code | note |
|---|---|---|
| 402 | payment_required | Retry with X-PAYMENT. |
| 404 | object_not_found | Either 'from' or 'to' is unknown in this scene. |
| 404 | scene_not_found | Unknown scene id. |
/scenes/{id}/nearestworking$0.005 USDCworking: Spawns find_nearest.py. Body: target, optional count (default 3, max 20).
Find the nearest object to a point
Returns the object whose bounding-box centre is closest to the supplied world-space point. Useful for 'what am I standing next to?' queries.
{ "point": { "x": 2.1, "y": 0.0, "z": 1.4 } }{
"object": "Desk",
"distance_m": 0.31
}curl -X POST -H "X-PAYMENT: <envelope>" \
-H "Content-Type: application/json" \
-d '{"point":{"x":2.1,"y":0,"z":1.4}}' \
https://api.colpero.com/scenes/lidar_room/nearest/scenes/{id}/pathworking$0.008 USDCworking: Spawns find_path.py. Body: from, to.
Plan a path around obstacles
A* over the free-space grid derived from the scene. Returns a list of waypoints in metres.
{
"from": { "x": 0.2, "y": 0.0, "z": 0.2 },
"to": { "x": 3.0, "y": 0.0, "z": 2.8 }
}{
"waypoints": [
{ "x": 0.2, "y": 0, "z": 0.2 },
{ "x": 1.1, "y": 0, "z": 1.4 },
{ "x": 3.0, "y": 0, "z": 2.8 }
],
"length_m": 4.31
}curl -X POST -H "X-PAYMENT: <envelope>" \
-H "Content-Type: application/json" \
-d '{"from":{"x":0.2,"y":0,"z":0.2},"to":{"x":3,"y":0,"z":2.8}}' \
https://api.colpero.com/scenes/lidar_room/path/scenes/{id}/queryarchived$0.010 USDCarchived: Removed on the 2026-04-21 pivot to retrieval-only. The Claude NL layer is gone; use /scenes/{id}/search for metadata or wait for /query-semantic.
Natural-language query (archived — retrieval-only pivot)
Historical: accepted a free-form English question and resolved it via Claude tool-use against the scene's spatial primitives. Removed in the 2026-04-21 pivot to a retrieval-only backend (no LLM in the hot path). Replaced by /query-semantic on the roadmap.
{ "question": "How far is the bed from the desk?" }{
"answer": "The bed is 2.41 metres from the desk.",
"calls": [
{ "tool": "distance", "args": { "from": "Bed", "to": "Desk" }, "result": 2.41 }
],
"cost_usdc": 0.010
}curl -X POST -H "X-PAYMENT: <envelope>" \
-H "Content-Type: application/json" \
-d '{"question":"How far is the bed from the desk?"}' \
https://api.colpero.com/scenes/lidar_room/queryGaussian Splatting
/scenes/{id}/gsworking$0.020 USDCworking: Format negotiation via Accept-Format header. Default: gs_full.
Fetch the raw splat (PLY or JSON)
Returns the full Gaussian splat reconstruction. Content type is negotiated via the Accept header: application/octet-stream yields a binary PLY; application/json yields metadata.
// PLY (binary)
// …or JSON metadata:
{
"format": "ply",
"size_bytes": 2_400_101,
"gaussians": 184_221,
"bounding_box": {
"min": { "x": 0, "y": 0, "z": 0 },
"max": { "x": 3.26, "y": 2.50, "z": 3.16 }
}
}curl -H "X-PAYMENT: <envelope>" \
-H "Accept: application/octet-stream" \
--output room.ply \
https://api.colpero.com/scenes/lidar_room/gsRegistry
Discovery lives on-chain in the ColperoRegistry contract on Base Sepolia. The node exposes free HTTP read helpers so clients don't need an RPC provider.
/nodesbrokenbroken: Returns 503 registry_not_configured unless REGISTRY_CONTRACT_ADDRESS is set. When configured, queries the on-chain ColperoRegistry. Filters: capability, minReputation, maxPricePerQuery|Compute|Path (wei).
List active domain nodes
Free. Reads the on-chain ColperoRegistry contract and returns every active node with capabilities and reputation.
{
"count": 2,
"nodes": [
{
"node_id": "0xDeAd…1234",
"capabilities": ["domain", "spatial_compute"],
"reputation": 97,
"endpoint": "https://node1.colpero.com",
"active": true,
"price_per_query": 0.001
}
]
}curl https://api.colpero.com/nodesRoadmap
Endpoints the spatial-api will expose but that are not live yet, and one stub that ships a real response but not real data until its worker dependency lands. Status badges reflect today's reality, not the marketing surface.
/ingestworking$0.100 USDCUpload a scan, receive a scene_id
Accepts mp4/frames + metadata, persists to R2, dispatches a worker job (LingBot-Map → splat + scene_graph + poses). Returns scene_id immediately; poll /scenes/:id for status=ready.
multipart/form-data: video=<mp4>, metadata={...}{ "scene_id": "abc123", "status": "queued" }curl -X POST -H "X-PAYMENT: <envelope>" \
-F "video=@room.mp4" \
-F 'metadata={"label":"lab"}' \
https://api.colpero.com/ingest/scenes/{id}/query-semanticplanned$0.020 USDCplanned: Open-vocab retrieval over scene metadata. Roadmap.
Text → 3D bounding box + evidence crop
Takes a free-form description ("the red box on the top shelf"), runs SAM 3.1 over indexed keyframes, lifts the 2D mask to a 3D bbox using scene depth, and returns ranked matches with evidence crops. Retrieval-only — no LLM in the hot path.
{ "query": "caixa vermelha", "top_k": 5 }{
"matches": [
{
"bbox_3d": { "min": {...}, "max": {...} },
"centroid": { "x": 2.1, "y": 0.4, "z": 1.8 },
"score": 0.87,
"evidence_crop_url": "https://r2.../evidence/abc.jpg"
}
]
}curl -X POST -H "X-PAYMENT: <envelope>" \
-H "Content-Type: application/json" \
-d '{"query":"caixa vermelha","top_k":5}' \
https://api.colpero.com/scenes/lidar_room/query-semantic/find-and-routeplanned$0.050 USDCplanned: Composability endpoint: query → path → viewer URL in a single call. Roadmap (P3.8).
Bundled: semantic query + navmesh path to each match
Orchestrator endpoint. Runs /query-semantic, then /path over the navmesh to each match, and returns the full plan plus a deep link to the 3D viewer with the path highlighted.
{
"scene_id": "lidar_room",
"query": "desk",
"agent_pose": { "x": 0, "y": 0, "z": 0 },
"radius": 5.0
}{
"matches": [
{
"bbox": { "min": {...}, "max": {...} },
"path_waypoints": [ ... ],
"distance_m": 2.4,
"viewer_url": "https://colpero.com/scenes/.../view?..."
}
]
}curl -X POST -H "X-PAYMENT: <envelope>" \
-H "Content-Type: application/json" \
-d '{"scene_id":"lidar_room","query":"desk"}' \
https://api.colpero.com/find-and-route/scenes/{id}/attestworking$0.005 USDCworking: RFC-COL-001 v1.1 Phase 1. Verifies Ed25519 sig over canonical digest (see docs/rfcs/COL-001-v1.1.md §3.2), persists to JSONL store at ATTESTATIONS_PATH (default data/attestations.jsonl). Returns 400 signature_invalid on forged sig, 400 invalid_format on malformed hex.
Sign a scene with the node's Ed25519 key
Computes sha256 over the canonical (PLY + scene_graph + manifest) tuple, signs with the node's Ed25519 key, persists proof.json and returns the proof URL. Phase 2 appends to a Merkle tree and submits the epoch root on-chain.
{ "metadata": { ... } }{
"proof_id": "proof_abc123",
"proof_url": "https://.../proofs/abc123.json",
"verify_url": "https://colpero.com/verify/abc123",
"onchain_tx": null
}curl -X POST -H "X-PAYMENT: <envelope>" \
-H "Content-Type: application/json" \
-d '{"metadata":{"captured_by":"luca"}}' \
https://api.colpero.com/scenes/lidar_room/attestErrors
Every error uses the same shape. Code is a stable machine-readable slug; message is human-readable; details is optional route-specific context.
(error shape)workingCanonical error envelope
Every non-2xx response on every route follows this shape. Clients should branch on error.code, never on error.message.
{
"error": {
"code": "scene_not_found",
"message": "Scene 'x' not found",
"details": { "requested": "x" }
}
}curl https://api.colpero.com/scenes/nonexistent/objects| status | code | note |
|---|---|---|
| 400 | bad_request | Malformed input. |
| 402 | payment_required | Retry with X-PAYMENT. |
| 404 | scene_not_found | Unknown scene id. |
| 404 | object_not_found | Object not present in the scene. |
| 503 | registry_unavailable | On-chain registry unreachable. |