Spatial Memory API
The Spatial Memory API keeps, for each space you create, explicit records of the objects seen in it: an identity for each object, its positions over time and the labels you confirm. Send point clouds from a depth camera or LiDAR; the API matches each object to the ones it has seen before, creates a new record when nothing matches, and says so when a match is ambiguous. It does not control any device. Every observation, label and deletion is written to a receipt chain for that space, which you can export and verify.
Limitations
- How objects are matched. Objects are matched by their position and motion history, with an optional simple shape descriptor that only separates candidates already close in position. An object that moves far while out of view is recorded as a new provisional object, and the earlier one is marked unconfirmed if its position lies in a declared coverage box. Objects that are turned while out of view are not told apart by shape.
- Validate on your own sensor and scene. Results depend on the sensor, the segmentation and the scene. Check the service on recordings from your own setup before you rely on it, particularly for cluttered scenes, objects that touch each other and objects handled by people.
- Segmentation. Automatic segmentation groups points into connected 2.5 centimetre voxels by default, so objects that touch or stand closer than about one voxel become one segment, and one object seen in separated parts becomes several. Callers with their own segmentation can send segments instead.
- How the memory is updated. By default the memory changes only on repeated evidence: a new object is provisional until it is seen consistently, a single contradicting observation is kept as an alternative, a move is accepted after it persists, and an object that keeps being missing inside a declared coverage box becomes unconfirmed. The consolidated position of an object in continuous motion therefore lags behind it; for such objects select the last-observation rule through the policy endpoint. Thresholds are set per space.
- No object recognition. The service does not name, classify or recognise objects. A label is whatever the caller confirms for an instance.
- No robot or device control. The service stores records and answers questions about them. It does not control robots or other devices, and its answers are not rated for safety-relevant decisions.
- Memory is explicit records, not training. Each space holds explicit records: positions, descriptor values, labels and their history. Nothing is trained on customer data, and records of one account are never used for another.
- What a find answer means. A find answer gives the consolidated position and the time the object was last seen; it does not prove that the object is still there. When the evidence does not support a position, the answer is not_seen, ambiguous or unconfirmed instead of a guess. Labels are matched exactly.
- Missing objects are detected only inside a declared coverage box. Only when an observation declares a coverage box does the service count a miss for objects inside it that were not seen; without a box no object is ever marked missing. A box larger than the real view will wrongly mark hidden objects as unconfirmed.
- Coordinates and time. Coordinates are metres in the space's own frame. A sensor pose sent with an observation is applied as given; the service does not estimate sensor poses or align clouds. Observations must arrive in time order within a space.
- Size limits. An observation may hold up to 200,000 points, or fewer if the plan sets a lower limit, and the request body may not exceed 10 MB, so large clouds should use the binary format. At most 64 segments are kept per observation and 1,000 instances per space.
- Typical response times. An observation of a few thousand points is processed in tens of milliseconds and one of 200,000 points in under half a second in binary form. Matching time grows with the number of instances in a space.
- What the receipts prove. Every observation, update action, label, policy change and forget appends a SHA-256 receipt to the space's chain in the published receipt-chain format. Receipts carry identifiers, counts and hashes of the state, not positions, so forgetting an instance removes its history while the chain stays verifiable. A chain proves order and integrity only when its head is also held by another party. Deleting a space deletes its chain, so export it first if you need it.
POST /v1/spatial/spaces
Create a space
Creates an empty space with the default policy.The example data are illustrative.
Request body (application/json)
| Field | Type | Description |
|---|
name required | string | |
Responses
| 201 | Success. |
| 400 | invalid_json: The request body is not valid JSON. invalid_limit_header: A limit header is not a whole number. invalid_name: The space name must be 1 to 80 characters. invalid_request: The request does not have the documented shape. |
| 401 | Missing or invalid API key. |
| 403 | space_limit_reached: The account has as many spaces as its plan allows. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X POST "https://api.qriton.com/v1/spatial/spaces" \
-H "Authorization: Bearer $QRITON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Workshop bench"}'
Example response
{
"space_id": "sp_32177cc0db9fe1a6",
"name": "Workshop bench",
"created_at": "2026-11-14T21:04:55.255Z",
"observations": 0,
"latest_observation_at": null,
"instances": 0,
"provisional_instances": 0,
"unconfirmed_instances": 0,
"receipts": 0,
"receipt_head": null
}
GET /v1/spatial/spaces
List spaces
Lists the spaces of the account.
Responses
| 200 | Success. |
| 400 | invalid_limit_header: A limit header is not a whole number. |
| 401 | Missing or invalid API key. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X GET "https://api.qriton.com/v1/spatial/spaces" \
-H "Authorization: Bearer $QRITON_API_KEY"
Example response
{
"spaces": [
{
"space_id": "sp_32177cc0db9fe1a6",
"name": "Workshop bench",
"created_at": "2026-11-14T21:04:55.255Z",
"observations": 4,
"latest_observation_at": "2026-11-14T09:30:06.000Z",
"instances": 1,
"provisional_instances": 0,
"unconfirmed_instances": 0,
"receipts": 15,
"receipt_head": "0d8469f5310c48a76a22b9614cebd1e6d5a4fbee5366bd4ad89f88bb72accbb6"
}
],
"count": 1,
"max_spaces": 3
}
GET /v1/spatial/spaces/{space_id}
Get a space
Returns counts and the receipt head of one space.
Parameters
| Name | In | Type | Description |
|---|
space_id required | path | string | |
Responses
| 200 | Success. |
| 400 | Missing or invalid API key. |
| 401 | Missing or invalid API key. |
| 404 | space_not_found: No space with this id exists for this account. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X GET "https://api.qriton.com/v1/spatial/spaces/{space_id}" \
-H "Authorization: Bearer $QRITON_API_KEY"
Example response
{
"space_id": "sp_32177cc0db9fe1a6",
"name": "Workshop bench",
"created_at": "2026-11-14T21:04:55.255Z",
"observations": 4,
"latest_observation_at": "2026-11-14T09:30:06.000Z",
"instances": 1,
"provisional_instances": 0,
"unconfirmed_instances": 0,
"receipts": 15,
"receipt_head": "0d8469f5310c48a76a22b9614cebd1e6d5a4fbee5366bd4ad89f88bb72accbb6"
}
DELETE /v1/spatial/spaces/{space_id}
Delete a space
Deletes the space with all its instances, history and receipt chain. Export the chain first if you need it.
Parameters
| Name | In | Type | Description |
|---|
space_id required | path | string | |
Responses
| 200 | Success. |
| 400 | Missing or invalid API key. |
| 401 | Missing or invalid API key. |
| 404 | space_not_found: No space with this id exists for this account. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X DELETE "https://api.qriton.com/v1/spatial/spaces/{space_id}" \
-H "Authorization: Bearer $QRITON_API_KEY"
Example response
{
"deleted": true,
"space_id": "sp_32177cc0db9fe1a6",
"final_receipt_head": "0d8469f5310c48a76a22b9614cebd1e6d5a4fbee5366bd4ad89f88bb72accbb6",
"receipts_deleted": 15
}
GET /v1/spatial/spaces/{space_id}/policy
Get the update policy
Returns the policy settings of the space with their defaults, ranges and meaning.
Parameters
| Name | In | Type | Description |
|---|
space_id required | path | string | |
Responses
| 200 | Success. |
| 400 | Missing or invalid API key. |
| 401 | Missing or invalid API key. |
| 404 | space_not_found: No space with this id exists for this account. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X GET "https://api.qriton.com/v1/spatial/spaces/{space_id}/policy" \
-H "Authorization: Bearer $QRITON_API_KEY"
Example response
{
"space_id": "sp_32177cc0db9fe1a6",
"policy": {
"update_rule": "adaptive",
"match_radius_m": 0.25,
"ambiguity_margin_m": 0.03,
"shape_weight": 0.25,
"position_sigma_m": 0.02,
"surprise_sigma": 3,
"change_radius_m": 0.05,
"revise_after_observations": 3,
"revise_after_seconds": 0,
"consolidate_after_observations": 3,
"consolidate_after_seconds": 1,
"fast_half_life_seconds": 1,
"slow_half_life_observations": 5,
"unconfirmed_after_misses": 3,
"velocity_horizon_seconds": 2
},
"settings": [
{
"name": "update_rule",
"default": "adaptive",
"description": "How the consolidated state is updated: adaptive, last_observation (every match overwrites it) or first_observation (it never changes after the first sighting).",
"choices": [
"adaptive",
"last_observation",
"first_observation"
]
},
{
"name": "match_radius_m",
"default": 0.25,
"description": "A segment can match an instance only if it lies within this distance of one of the instance's reference positions.",
"minimum": 0.01,
"maximum": 5
},
{
"name": "ambiguity_margin_m",
"default": 0.03,
"description": "If the two best candidates differ in cost by less than this, the service abstains.",
"minimum": 0,
"maximum": 1
},
{
"name": "shape_weight",
"default": 0.25,
"description": "Weight of the shape descriptor distance in the match cost; 0 disables the shape term.",
"minimum": 0,
"maximum": 1
},
{
"name": "position_sigma_m",
"default": 0.02,
"description": "Expected spread of the observed position of an object at rest.",
"minimum": 0.001,
"maximum": 1
},
{
"name": "surprise_sigma",
"default": 3,
"description": "An observation is surprising when its distance from the consolidated position exceeds this many position sigmas.",
"minimum": 0.5,
"maximum": 20
},
{
"name": "change_radius_m",
"default": 0.05,
"description": "Surprising observations count as the same change when they lie within this distance of each other.",
"minimum": 0.005,
"maximum": 2
},
{
"name": "revise_after_observations",
"default": 3,
"description": "Number of agreeing surprising observations needed before the consolidated state is revised.",
"minimum": 1,
"maximum": 100
},
{
"name": "revise_after_seconds",
"default": 0,
"description": "Minimum time the change must persist before the consolidated state is revised.",
"minimum": 0,
"maximum": 86400
},
{
"name": "consolidate_after_observations",
"default": 3,
"description": "Number of sightings needed to consolidate a provisional instance.",
"minimum": 1,
"maximum": 100
},
{
"name": "consolidate_after_seconds",
"default": 1,
"description": "Minimum time between the first and the consolidating sighting.",
"minimum": 0,
"maximum": 86400
},
{
"name": "fast_half_life_seconds",
"default": 1,
"description": "Half-life of the smoothed recent position.",
"minimum": 0.01,
"maximum": 3600
},
{
"name": "slow_half_life_observations",
"default": 5,
"description": "A consistent observation moves the consolidated position by 1 - 0.5^(1/this) of the residual.",
"minimum": 1,
"maximum": 1000
},
{
"name": "unconfirmed_after_misses",
"default": 3,
"description": "Consecutive observations whose coverage box contains an instance without seeing it before the instance is marked unconfirmed.",
"minimum": 1,
"maximum": 1000
},
{
"name": "velocity_horizon_seconds",
"default": 2,
"description": "Longest time over which the recent velocity is extrapolated for matching.",
"minimum": 0,
"maximum": 60
}
]
}
PUT /v1/spatial/spaces/{space_id}/policy
Change the update policy
Changes some policy settings; the others keep their values. The change is a receipt.
Parameters
| Name | In | Type | Description |
|---|
space_id required | path | string | |
Request body (application/json)
| Field | Type | Description |
|---|
update_rule | string: "adaptive", "last_observation", "first_observation" | How the consolidated state is updated: adaptive, last_observation (every match overwrites it) or first_observation (it never changes after the first sighting). |
match_radius_m | number | A segment can match an instance only if it lies within this distance of one of the instance's reference positions. |
ambiguity_margin_m | number | If the two best candidates differ in cost by less than this, the service abstains. |
shape_weight | number | Weight of the shape descriptor distance in the match cost; 0 disables the shape term. |
position_sigma_m | number | Expected spread of the observed position of an object at rest. |
surprise_sigma | number | An observation is surprising when its distance from the consolidated position exceeds this many position sigmas. |
change_radius_m | number | Surprising observations count as the same change when they lie within this distance of each other. |
revise_after_observations | integer | Number of agreeing surprising observations needed before the consolidated state is revised. |
revise_after_seconds | number | Minimum time the change must persist before the consolidated state is revised. |
consolidate_after_observations | integer | Number of sightings needed to consolidate a provisional instance. |
consolidate_after_seconds | number | Minimum time between the first and the consolidating sighting. |
fast_half_life_seconds | number | Half-life of the smoothed recent position. |
slow_half_life_observations | integer | A consistent observation moves the consolidated position by 1 - 0.5^(1/this) of the residual. |
unconfirmed_after_misses | integer | Consecutive observations whose coverage box contains an instance without seeing it before the instance is marked unconfirmed. |
velocity_horizon_seconds | number | Longest time over which the recent velocity is extrapolated for matching. |
Responses
| 200 | Success. |
| 400 | invalid_json: The request body is not valid JSON. invalid_policy: A policy setting is unknown or out of range. |
| 401 | Missing or invalid API key. |
| 404 | space_not_found: No space with this id exists for this account. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X PUT "https://api.qriton.com/v1/spatial/spaces/{space_id}/policy" \
-H "Authorization: Bearer $QRITON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"revise_after_observations":4,"match_radius_m":0.2}'
Example response
{
"space_id": "sp_32177cc0db9fe1a6",
"policy": {
"update_rule": "adaptive",
"match_radius_m": 0.2,
"ambiguity_margin_m": 0.03,
"shape_weight": 0.25,
"position_sigma_m": 0.02,
"surprise_sigma": 3,
"change_radius_m": 0.05,
"revise_after_observations": 4,
"revise_after_seconds": 0,
"consolidate_after_observations": 3,
"consolidate_after_seconds": 1,
"fast_half_life_seconds": 1,
"slow_half_life_observations": 5,
"unconfirmed_after_misses": 3,
"velocity_horizon_seconds": 2
},
"changed": [
"match_radius_m",
"revise_after_observations"
],
"receipt": {
"sequence": 13,
"timestamp_utc": "2026-11-14T21:04:55.272Z",
"operation": "edit",
"previous_hash": "907ce5ce0bc3e224866145e9a6bc5601e2548d9f9e4b16d1edfbd3d5ce06995b",
"payload": {
"kind": "policy",
"fact_id": "sp_32177cc0db9fe1a6",
"changed": [
"match_radius_m",
"revise_after_observations"
],
"policy_sha256": "2daf6190687eef971d6c05e12fab6b53873356d0644cfd7b7d44b09767acbd69"
},
"receipt_hash": "fb7df1a82ff9eaa6f5c7bd0d1b0949de07d52e94ec992adb4aeea1cef8ee9df2"
}
}
POST /v1/spatial/spaces/{space_id}/observations
Send an observation
Sends one point cloud. The service segments it (or uses the given segments), matches each segment to the instances of the space by position and motion history with an optional shape term, creates a provisional instance when nothing is within the match radius, and abstains when two candidates are too close to separate. Each segment's novelty, surprise, change evidence, action and evidence are returned, and every action is appended to the receipt chain. The body may also be application/octet-stream: a 16-byte little-endian header (the 4 bytes QSPC, uint32 version 1, uint32 point count, uint32 flags; flag 1 means per-point uint32 segment ids follow the points, 4294967295 for none) and then the points as float32 x, y, z; timestamp, sensor_pose (7 comma-separated numbers x,y,z,w,qx,qy,qz), coverage (6 numbers: min then max), voxel_size_m, min_segment_points and remove_dominant_plane are then query parameters. The example data are illustrative.
Parameters
| Name | In | Type | Description |
|---|
space_id required | path | string | |
Request body (application/json, application/octet-stream)
| Field | Type | Description |
|---|
points required | array of array of number | Points in metres. |
timestamp | string | ISO 8601 UTC ending in Z. |
sensor_pose | object | Applied as given to turn sensor coordinates into space coordinates. |
segments | array of array of integer | Optional pre-segmented clusters as lists of point indices. Without it the service segments the cloud itself. |
coverage | object | Axis-aligned box the sensor fully covered. Only with it are objects that were expected but not seen counted as missed. |
options | object | |
Responses
| 200 | Success. |
| 400 | invalid_coverage: The coverage box is malformed. invalid_json: The request body is not valid JSON. invalid_limit_header: A limit header is not a whole number. invalid_points: The points are not finite [x, y, z] triples within range. invalid_request: The request does not have the documented shape. invalid_segments: The given segments are malformed, overlapping or out of range. invalid_sensor_pose: The sensor pose is malformed. invalid_timestamp: A time is not ISO 8601 UTC ending in Z.unsupported_content_type: The body is neither application/json nor application/octet-stream. |
| 401 | Missing or invalid API key. |
| 403 | instance_limit_reached: The observation would exceed the instance limit of the space. |
| 404 | space_not_found: No space with this id exists for this account. |
| 409 | timestamp_out_of_order: The observation is older than the latest observation of the space. |
| 413 | body_too_large: The request body is larger than 10 MB. too_many_points: The observation has more points than allowed. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X POST "https://api.qriton.com/v1/spatial/spaces/{space_id}/observations" \
-H "Authorization: Bearer $QRITON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"points":[[-0.2615,0.0949,0.0289],[-0.1604,0.0679,0.0193],[-0.2618,0.1118,0.0372],[-0.2255,0.1343,0.0058],[-0.1623,0.075,0.0558],[-0.1423,0.1175,0.0052],[-0.21,0.128,0.0356],[-0.2584,0.0882,0.0223],[-0.1784,0.0769,0.0495],[-0.2438,0.0705,0.0239],[-0.2351,0.072,0.0079],[-0.1393,0.0705,0.0348],[-0.167,0.1306,0.0221],[-0.2558,0.113,0.0198],[-0.1769,0.0678,0.0175],[-0.1658,0.1088,0.0465],[-0.2031,0.1309,0.0159],[-0.1927,0.0758,0.0501],[-0.2606,0.0782,0.0054],[-0.246,0.1329,0.043],[-0.1803,0.1304,0.046],[-0.2042,0.0665,0.0356],[-0.1365,0.1008,0.015],[-0.1407,0.1158,0.0464],[-0.2607,0.1107,0.0401],[-0.1901,0.1154,0.0509],[-0.1894,0.0709,0.0241],[-0.2241,0.0865,0.0486],[-0.2543,0.0734,0.0516],[-0.208,0.1323,0.0466],[-0.2306,0.1315,0.0196],[-0.1401,0.0922,0.0091],[-0.1546,0.0713,0.0139],[-0.234,0.0701,0.0265],[-0.2543,0.0706,0.0111],[-0.225,0.1168,0.0506],[-0.2246,0.1163,0.0532],[-0.1847,0.0688,0.0372],[-0.1963,0.0989,0.0507],[-0.1642,0.1289,0.033],[0.2833,-0.0245,0.0938],[0.2851,-0.0719,0.1163],[0.2547,-0.0095,0.0621],[0.2868,-0.0409,0.0202],[0.2109,-0.0652,0.0021],[0.2241,-0.021,0.0302],[0.2709,-0.0834,0.0169],[0.269,-0.0878,0.0773],[0.2368,-0.0144,0.0148],[0.2909,-0.0649,0.0583],[0.2366,-0.0129,0.0842],[0.2238,-0.052,0.1172],[0.2439,-0.0099,0.0688],[0.2877,-0.0642,0.0242],[0.272,-0.0152,0.0976],[0.287,-0.0392,0.0862],[0.2191,-0.0367,0.121],[0.2894,-0.0522,0.0903],[0.2771,-0.0778,0.0128],[0.245,-0.0914,0.0163],[0.2804,-0.0749,0.1135],[0.284,-0.075,0.0504],[0.2449,-0.0523,0.1188],[0.2309,-0.0152,0.0354],[0.2571,-0.0862,0.0559],[0.2297,-0.0861,0.0805],[0.2217,-0.0251,0.1165],[0.2819,-0.0287,0.0322],[0.2479,-0.0891,0.1106],[0.2475,-0.0094,0.0056],[0.2862,-0.0684,0.0688],[0.2501,-0.0346,0.1208],[0.2784,-0.0214,0.08],[0.2737,-0.0848,0.013],[0.275,-0.0189,0.0175],[0.2671,-0.0133,0.0486],[0.2144,-0.0775,0.1137],[0.2773,-0.0789,0.0754],[0.2654,-0.0142,0.1127],[0.2344,-0.0134,0.0969]],"timestamp":"2026-11-14T09:30:00Z","coverage":{"min":[-0.6,-0.4,-0.05],"max":[0.6,0.4,0.5]},"options":{"min_segment_points":20}}'
Example response
{
"observation_id": "ob_229af90f8d16ef16",
"space_id": "sp_32177cc0db9fe1a6",
"timestamp": "2026-11-14T09:30:00.000Z",
"point_count": 80,
"points_sha256": "fc2478b170ae701b4cf117b43598bff6beb6ea4b6f4b2808a7acfb760eb119a7",
"segmentation": {
"method": "voxel_connected_components",
"voxel_size_m": 0.025,
"plane_points_removed": 0,
"segments_dropped": 0,
"points_in_small_clusters": 0
},
"update_rule": "adaptive",
"segments": [
{
"segment": 0,
"point_count": 40,
"position": [
-0.203585,
0.09784,
0.031407
],
"extent": [
0.1253,
0.0678,
0.0506
],
"outcome": "new",
"novelty": 1,
"instance_id": "in_7019266d587b2c13",
"action": "provisional",
"status": "provisional",
"match_score": null,
"surprise": null,
"change_evidence": null,
"change_evidence_needed": 3,
"evidence": {
"match_radius_m": 0.25,
"ambiguity_margin_m": 0.03,
"nearest": null
}
},
{
"segment": 1,
"point_count": 40,
"position": [
0.258553,
-0.04756,
0.066935
],
"extent": [
0.08,
0.082,
0.1189
],
"outcome": "new",
"novelty": 1,
"instance_id": "in_46b3645f7a7fbf95",
"action": "provisional",
"status": "provisional",
"match_score": null,
"surprise": null,
"change_evidence": null,
"change_evidence_needed": 3,
"evidence": {
"match_radius_m": 0.25,
"ambiguity_margin_m": 0.03,
"nearest": null
}
}
],
"missing": [],
"receipts": {
"appended": 3,
"first_sequence": 0,
"head": "b10d30b12801dce2365b6da62dc1636cb0563fd66fd6e5c7197dea338d9c4d2a"
}
}
GET /v1/spatial/spaces/{space_id}/instances
List instances
Lists the instances of the space. include_provisional=false leaves provisional instances out.
Parameters
| Name | In | Type | Description |
|---|
space_id required | path | string | |
include_provisional | query | boolean | |
Responses
| 200 | Success. |
| 400 | invalid_request: The request does not have the documented shape. |
| 401 | Missing or invalid API key. |
| 404 | space_not_found: No space with this id exists for this account. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X GET "https://api.qriton.com/v1/spatial/spaces/{space_id}/instances" \
-H "Authorization: Bearer $QRITON_API_KEY"
Example response
{
"space_id": "sp_32177cc0db9fe1a6",
"instances": [
{
"instance_id": "in_46b3645f7a7fbf95",
"label": null,
"status": "consolidated",
"unconfirmed": false,
"misses": 0,
"created_at": "2026-11-14T09:30:00.000Z",
"sightings": 4,
"last_seen_at": "2026-11-14T09:30:06.000Z",
"last_seen_position": [
0.250225,
-0.042938,
0.075302
],
"consolidated_position": [
0.252981,
-0.048943,
0.070916
],
"revision_pending": false
},
{
"instance_id": "in_7019266d587b2c13",
"label": "torque wrench",
"status": "consolidated",
"unconfirmed": false,
"misses": 0,
"created_at": "2026-11-14T09:30:00.000Z",
"sightings": 4,
"last_seen_at": "2026-11-14T09:30:06.000Z",
"last_seen_position": [
-0.05073,
0.09772,
0.031725
],
"consolidated_position": [
-0.200129,
0.096731,
0.032546
],
"revision_pending": true
}
],
"count": 2
}
GET /v1/spatial/spaces/{space_id}/instances/{instance_id}
Get an instance and its history
Returns the instance's consolidated and recent state, any pending alternative, and its history of positions and actions with times, newest first.
Parameters
| Name | In | Type | Description |
|---|
space_id required | path | string | |
instance_id required | path | string | |
limit | query | integer | |
offset | query | integer | |
Responses
| 200 | Success. |
| 400 | invalid_request: The request does not have the documented shape. |
| 401 | Missing or invalid API key. |
| 404 | instance_not_found: No instance with this id exists in this space. space_not_found: No space with this id exists for this account. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X GET "https://api.qriton.com/v1/spatial/spaces/{space_id}/instances/{instance_id}" \
-H "Authorization: Bearer $QRITON_API_KEY"
Example response
{
"instance_id": "in_7019266d587b2c13",
"label": "torque wrench",
"status": "consolidated",
"unconfirmed": false,
"misses": 0,
"created_at": "2026-11-14T09:30:00.000Z",
"sightings": 4,
"last_seen_at": "2026-11-14T09:30:06.000Z",
"last_seen_position": [
-0.05073,
0.09772,
0.031725
],
"consolidated_position": [
-0.200129,
0.096731,
0.032546
],
"revision_pending": true,
"recent_position": [
-0.086975,
0.097695,
0.032137
],
"alternative": {
"position": [
-0.05073,
0.09772,
0.031725
],
"change_evidence": 1,
"first_seen_at": "2026-11-14T09:30:06.000Z",
"last_seen_at": "2026-11-14T09:30:06.000Z"
},
"provisional_support": null,
"space_id": "sp_32177cc0db9fe1a6",
"history": [
{
"timestamp": "2026-11-14T09:30:06.000Z",
"observation_id": "ob_fc421f581cbff2d9",
"action": "alternative",
"observed_position": [
-0.05073,
0.09772,
0.031725
],
"consolidated_position": [
-0.200129,
0.096731,
0.032546
],
"status": "consolidated",
"unconfirmed": false,
"novelty": 0.5269937649536282,
"surprise": 7.470234805337447,
"change_evidence": 1,
"match_score": 0.4535983097841727
},
{
"timestamp": "2026-11-14T09:30:04.000Z",
"observation_id": "ob_40ad8a9e0f981ddf",
"action": "consolidate",
"observed_position": [
-0.19304,
0.098555,
0.033762
],
"consolidated_position": [
-0.200129,
0.096731,
0.032546
],
"status": "consolidated",
"unconfirmed": false,
"novelty": 0.047207260034871815,
"surprise": 0.5711720970540536,
"change_evidence": 0,
"match_score": 0.9311471603914161
},
{
"timestamp": "2026-11-14T09:30:02.000Z",
"observation_id": "ob_104870ff6a3f1461",
"action": "provisional",
"observed_position": [
-0.203763,
0.093798,
0.032467
],
"consolidated_position": null,
"status": "provisional",
"unconfirmed": false,
"novelty": 0.016731724358236392,
"surprise": 0.20914655447795488,
"change_evidence": 0,
"match_score": 0.9687116719138604
}
],
"history_total": 4,
"limit": 3,
"offset": 0
}
DELETE /v1/spatial/spaces/{space_id}/instances/{instance_id}
Forget an instance
Deletes the instance's state and history. The receipt chain keeps a forget receipt; earlier receipts hold no positions.
Parameters
| Name | In | Type | Description |
|---|
space_id required | path | string | |
instance_id required | path | string | |
Responses
| 200 | Success. |
| 400 | Missing or invalid API key. |
| 401 | Missing or invalid API key. |
| 404 | instance_not_found: No instance with this id exists in this space. space_not_found: No space with this id exists for this account. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X DELETE "https://api.qriton.com/v1/spatial/spaces/{space_id}/instances/{instance_id}" \
-H "Authorization: Bearer $QRITON_API_KEY"
Example response
{
"forgotten": true,
"instance_id": "in_46b3645f7a7fbf95",
"history_records_deleted": 4,
"receipt": {
"sequence": 14,
"timestamp_utc": "2026-11-14T21:04:55.273Z",
"operation": "forget",
"previous_hash": "fb7df1a82ff9eaa6f5c7bd0d1b0949de07d52e94ec992adb4aeea1cef8ee9df2",
"payload": {
"kind": "forget",
"fact_id": "in_46b3645f7a7fbf95",
"instance_id": "in_46b3645f7a7fbf95",
"answer": null,
"reverted_to_base": true,
"history_records_deleted": 4
},
"receipt_hash": "0d8469f5310c48a76a22b9614cebd1e6d5a4fbee5366bd4ad89f88bb72accbb6"
}
}
POST /v1/spatial/spaces/{space_id}/instances/{instance_id}/label
Confirm a label
Sets the operator-confirmed label of an instance. Labels are unique within a space. The change is a receipt.
Parameters
| Name | In | Type | Description |
|---|
space_id required | path | string | |
instance_id required | path | string | |
Request body (application/json)
| Field | Type | Description |
|---|
label required | string | |
Responses
| 200 | Success. |
| 400 | invalid_json: The request body is not valid JSON. invalid_label: The label does not meet the label rules. invalid_request: The request does not have the documented shape. |
| 401 | Missing or invalid API key. |
| 404 | instance_not_found: No instance with this id exists in this space. space_not_found: No space with this id exists for this account. |
| 409 | label_in_use: Another instance in the space already has this label. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X POST "https://api.qriton.com/v1/spatial/spaces/{space_id}/instances/{instance_id}/label" \
-H "Authorization: Bearer $QRITON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"label":"torque wrench"}'
Example response
{
"instance_id": "in_7019266d587b2c13",
"label": "torque wrench",
"status": "consolidated",
"unconfirmed": false,
"misses": 0,
"created_at": "2026-11-14T09:30:00.000Z",
"sightings": 3,
"last_seen_at": "2026-11-14T09:30:04.000Z",
"last_seen_position": [
-0.19304,
0.098555,
0.033762
],
"consolidated_position": [
-0.200129,
0.096731,
0.032546
],
"revision_pending": false,
"receipt": {
"sequence": 9,
"timestamp_utc": "2026-11-14T21:04:55.264Z",
"operation": "edit",
"previous_hash": "9c1ae697ec316d7cc622d9f93cc86abd2a8b72d75fc6a1ede9aabe7365887ee0",
"payload": {
"kind": "label",
"fact_id": "in_7019266d587b2c13",
"instance_id": "in_7019266d587b2c13",
"previous_answer": null,
"answer": "torque wrench"
},
"receipt_hash": "5aba6f58e8c8fbaf0f2b8b4449c8a082a2594f9eec469dbfb655a00e1b63f1c4"
}
}
POST /v1/spatial/spaces/{space_id}/find
Find an object
Answers from consolidated memory by exact label or instance id: the consolidated position and when the object was last seen, or not_seen, ambiguous or unconfirmed instead of a guess. A pending revision is reported when recent observations disagree. Provisional instances are left out unless include_provisional is true. The example data are illustrative.
Parameters
| Name | In | Type | Description |
|---|
space_id required | path | string | |
Request body (application/json)
| Field | Type | Description |
|---|
label | string | |
instance_id | string | |
include_provisional | boolean | |
Responses
| 200 | Success. |
| 400 | invalid_json: The request body is not valid JSON. invalid_label: The label does not meet the label rules. invalid_request: The request does not have the documented shape. |
| 401 | Missing or invalid API key. |
| 404 | space_not_found: No space with this id exists for this account. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X POST "https://api.qriton.com/v1/spatial/spaces/{space_id}/find" \
-H "Authorization: Bearer $QRITON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"label":"torque wrench"}'
Example response
{
"space_id": "sp_32177cc0db9fe1a6",
"query": {
"label": "torque wrench"
},
"instance_id": "in_7019266d587b2c13",
"label": "torque wrench",
"status": "consolidated",
"last_seen_at": "2026-11-14T09:30:06.000Z",
"seen_in_latest_observation": true,
"answer": "seen",
"position": [
-0.200129,
0.096731,
0.032546
],
"revision_pending": true,
"alternative": {
"position": [
-0.05073,
0.09772,
0.031725
],
"change_evidence": 1,
"change_evidence_needed": 3
}
}
GET /v1/spatial/spaces/{space_id}/at
State of the space at a time
Returns each instance that existed at time t with its consolidated position and status as recorded at or before t.
Parameters
| Name | In | Type | Description |
|---|
space_id required | path | string | |
t required | query | string | |
Responses
| 200 | Success. |
| 400 | invalid_timestamp: A time is not ISO 8601 UTC ending in Z. |
| 401 | Missing or invalid API key. |
| 404 | space_not_found: No space with this id exists for this account. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X GET "https://api.qriton.com/v1/spatial/spaces/{space_id}/at" \
-H "Authorization: Bearer $QRITON_API_KEY"
Example response
{
"space_id": "sp_32177cc0db9fe1a6",
"t": "2026-11-14T09:30:03.000Z",
"instances": [
{
"instance_id": "in_46b3645f7a7fbf95",
"label": null,
"status": "provisional",
"unconfirmed": false,
"consolidated_position": null,
"last_seen_at": "2026-11-14T09:30:02.000Z",
"last_event": "provisional",
"last_event_at": "2026-11-14T09:30:02.000Z"
},
{
"instance_id": "in_7019266d587b2c13",
"label": "torque wrench",
"status": "provisional",
"unconfirmed": false,
"consolidated_position": null,
"last_seen_at": "2026-11-14T09:30:02.000Z",
"last_event": "provisional",
"last_event_at": "2026-11-14T09:30:02.000Z"
}
],
"count": 2,
"note": "Labels are the current labels; positions are the consolidated state recorded at or before t."
}
GET /v1/spatial/spaces/{space_id}/receipts
Read the receipt chain
Returns receipts in order with the current head.
Parameters
| Name | In | Type | Description |
|---|
space_id required | path | string | |
limit | query | integer | |
offset | query | integer | |
Responses
| 200 | Success. |
| 400 | invalid_request: The request does not have the documented shape. |
| 401 | Missing or invalid API key. |
| 404 | space_not_found: No space with this id exists for this account. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X GET "https://api.qriton.com/v1/spatial/spaces/{space_id}/receipts" \
-H "Authorization: Bearer $QRITON_API_KEY"
Example response
{
"space_id": "sp_32177cc0db9fe1a6",
"count": 15,
"head": "0d8469f5310c48a76a22b9614cebd1e6d5a4fbee5366bd4ad89f88bb72accbb6",
"limit": 2,
"offset": 0,
"receipts": [
{
"operation": "inject",
"payload": {
"fact_id": "ob_229af90f8d16ef16",
"kind": "observation",
"observation_id": "ob_229af90f8d16ef16",
"outcomes": {
"ambiguous": 0,
"matched": 0,
"new": 2
},
"point_count": 80,
"points_sha256": "fc2478b170ae701b4cf117b43598bff6beb6ea4b6f4b2808a7acfb760eb119a7",
"result_sha256": "ef6ab83bd606d553640f8eae2ce551cf51727ac77be7f1f4f17905347a1985db",
"segment_count": 2,
"timestamp": "2026-11-14T09:30:00.000Z"
},
"previous_hash": null,
"receipt_hash": "3a1af60c24e8ecddaa51af72cbe90ec5df57f8583ece41099331abcdd8c9abd7",
"sequence": 0,
"timestamp_utc": "2026-11-14T21:04:55.258Z"
},
{
"operation": "edit",
"payload": {
"action": "provisional",
"change_evidence": null,
"fact_id": "in_7019266d587b2c13",
"instance_id": "in_7019266d587b2c13",
"kind": "action",
"novelty_milli": 1000,
"observation_id": "ob_229af90f8d16ef16",
"outcome": "new",
"previous_state_sha256": null,
"segment": 0,
"state_sha256": "a480a70057f39e5639281f9af06d045fae084d685dff71e4d291fc71badb347d",
"surprise_milli": null
},
"previous_hash": "3a1af60c24e8ecddaa51af72cbe90ec5df57f8583ece41099331abcdd8c9abd7",
"receipt_hash": "14983a8efc7ef96fd85ec6f73f8be0933a5c5e1840f841e7b65194e594bfe155",
"sequence": 1,
"timestamp_utc": "2026-11-14T21:04:55.258Z"
}
]
}
GET /v1/spatial/spaces/{space_id}/receipts/export
Export the receipt chain
Returns the whole chain as JSON Lines, one receipt per line, which the published verifiers check unchanged. The X-Receipt-Head header carries the head.
Parameters
| Name | In | Type | Description |
|---|
space_id required | path | string | |
Responses
| 200 | Success. |
| 400 | Missing or invalid API key. |
| 401 | Missing or invalid API key. |
| 404 | space_not_found: No space with this id exists for this account. |
| 500 | internal_error: The service failed to handle the request. |
Example request
curl -X GET "https://api.qriton.com/v1/spatial/spaces/{space_id}/receipts/export" \
-H "Authorization: Bearer $QRITON_API_KEY"
Example response
"{\"operation\": \"inject\", \"payload\": {\"fact_id\": \"ob_229af90f8d16ef16\", \"kind\": \"observation\", \"observation_id\": \"ob_229af90f8d16ef16\", \"outcomes\": {\"ambiguous\": 0, \"matched\": 0, \"new\": 2}, \"point_count\": 80, \"points_sha256\": \"fc2478b170ae701b4cf117b43598bff6beb6ea4b6f4b2808a7acfb760eb119a7\", \"result_sha256\": \"ef6ab83bd606d553640f8eae2ce551cf51727ac77be7f1f4f17905347a1985db\", \"segment_count\": 2, \"timestamp\": \"2026-11-14T09:30:00.000Z\"}, \"previous_hash\": null, \"receipt_hash\": \"3a1af60c24e8ecddaa51af72cbe90ec5df57f8583ece41099331abcdd8c9abd7\", \"sequence\": 0, \"timestamp_utc\": \"2026-11-14T21:04:55.258Z\"}\n{\"operation\": \"edit\", \"payload\": {\"action\": \"provisional\", \"change_evidence\": null, \"fact_id\": \"in_7019266d587b2c13\", \"instance_id\": \"in_7019266d587b2c13\", \"kind\": \"action\", \"novelty_milli\": 1000, \"observation_id\": \"ob_229af90f8d16ef16\", \"outcome\": \"new\", \"previous_state_sha256\": null, \"segment\": 0, \"state_sha256\": \"a480a70057f39e5639281f9af06d045fae084d685dff71e4d291fc71badb347d\", \"surprise_milli\": null}, \"previous_hash\": \"3a1af60c24e8ecddaa51af72cbe90ec5df57f8583ece41099331abcdd8c9abd7\", \"receipt_hash\": \"14983a8efc7ef96fd85ec6f73f8be0933a5c5e1840f841e7b65194e594bfe155\", \"sequence\": 1, \"timestamp_utc\": \"2026-11-14T21:04:55.258Z\"}\n"