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)

FieldTypeDescription
name requiredstring

Responses

201Success.
400invalid_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.
401Missing or invalid API key.
403space_limit_reached: The account has as many spaces as its plan allows.
500internal_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

200Success.
400invalid_limit_header: A limit header is not a whole number.
401Missing or invalid API key.
500internal_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

NameInTypeDescription
space_id requiredpathstring

Responses

200Success.
400Missing or invalid API key.
401Missing or invalid API key.
404space_not_found: No space with this id exists for this account.
500internal_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

NameInTypeDescription
space_id requiredpathstring

Responses

200Success.
400Missing or invalid API key.
401Missing or invalid API key.
404space_not_found: No space with this id exists for this account.
500internal_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

NameInTypeDescription
space_id requiredpathstring

Responses

200Success.
400Missing or invalid API key.
401Missing or invalid API key.
404space_not_found: No space with this id exists for this account.
500internal_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

NameInTypeDescription
space_id requiredpathstring

Request body (application/json)

FieldTypeDescription
update_rulestring: "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_mnumberA segment can match an instance only if it lies within this distance of one of the instance's reference positions.
ambiguity_margin_mnumberIf the two best candidates differ in cost by less than this, the service abstains.
shape_weightnumberWeight of the shape descriptor distance in the match cost; 0 disables the shape term.
position_sigma_mnumberExpected spread of the observed position of an object at rest.
surprise_sigmanumberAn observation is surprising when its distance from the consolidated position exceeds this many position sigmas.
change_radius_mnumberSurprising observations count as the same change when they lie within this distance of each other.
revise_after_observationsintegerNumber of agreeing surprising observations needed before the consolidated state is revised.
revise_after_secondsnumberMinimum time the change must persist before the consolidated state is revised.
consolidate_after_observationsintegerNumber of sightings needed to consolidate a provisional instance.
consolidate_after_secondsnumberMinimum time between the first and the consolidating sighting.
fast_half_life_secondsnumberHalf-life of the smoothed recent position.
slow_half_life_observationsintegerA consistent observation moves the consolidated position by 1 - 0.5^(1/this) of the residual.
unconfirmed_after_missesintegerConsecutive observations whose coverage box contains an instance without seeing it before the instance is marked unconfirmed.
velocity_horizon_secondsnumberLongest time over which the recent velocity is extrapolated for matching.

Responses

200Success.
400invalid_json: The request body is not valid JSON. invalid_policy: A policy setting is unknown or out of range.
401Missing or invalid API key.
404space_not_found: No space with this id exists for this account.
500internal_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

NameInTypeDescription
space_id requiredpathstring

Request body (application/json, application/octet-stream)

FieldTypeDescription
points requiredarray of array of numberPoints in metres.
timestampstringISO 8601 UTC ending in Z.
sensor_poseobjectApplied as given to turn sensor coordinates into space coordinates.
segmentsarray of array of integerOptional pre-segmented clusters as lists of point indices. Without it the service segments the cloud itself.
coverageobjectAxis-aligned box the sensor fully covered. Only with it are objects that were expected but not seen counted as missed.
optionsobject

Responses

200Success.
400invalid_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.
401Missing or invalid API key.
403instance_limit_reached: The observation would exceed the instance limit of the space.
404space_not_found: No space with this id exists for this account.
409timestamp_out_of_order: The observation is older than the latest observation of the space.
413body_too_large: The request body is larger than 10 MB. too_many_points: The observation has more points than allowed.
500internal_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

NameInTypeDescription
space_id requiredpathstring
include_provisionalqueryboolean

Responses

200Success.
400invalid_request: The request does not have the documented shape.
401Missing or invalid API key.
404space_not_found: No space with this id exists for this account.
500internal_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

NameInTypeDescription
space_id requiredpathstring
instance_id requiredpathstring
limitqueryinteger
offsetqueryinteger

Responses

200Success.
400invalid_request: The request does not have the documented shape.
401Missing or invalid API key.
404instance_not_found: No instance with this id exists in this space. space_not_found: No space with this id exists for this account.
500internal_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

NameInTypeDescription
space_id requiredpathstring
instance_id requiredpathstring

Responses

200Success.
400Missing or invalid API key.
401Missing or invalid API key.
404instance_not_found: No instance with this id exists in this space. space_not_found: No space with this id exists for this account.
500internal_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

NameInTypeDescription
space_id requiredpathstring
instance_id requiredpathstring

Request body (application/json)

FieldTypeDescription
label requiredstring

Responses

200Success.
400invalid_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.
401Missing or invalid API key.
404instance_not_found: No instance with this id exists in this space. space_not_found: No space with this id exists for this account.
409label_in_use: Another instance in the space already has this label.
500internal_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

NameInTypeDescription
space_id requiredpathstring

Request body (application/json)

FieldTypeDescription
labelstring
instance_idstring
include_provisionalboolean

Responses

200Success.
400invalid_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.
401Missing or invalid API key.
404space_not_found: No space with this id exists for this account.
500internal_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

NameInTypeDescription
space_id requiredpathstring
t requiredquerystring

Responses

200Success.
400invalid_timestamp: A time is not ISO 8601 UTC ending in Z.
401Missing or invalid API key.
404space_not_found: No space with this id exists for this account.
500internal_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

NameInTypeDescription
space_id requiredpathstring
limitqueryinteger
offsetqueryinteger

Responses

200Success.
400invalid_request: The request does not have the documented shape.
401Missing or invalid API key.
404space_not_found: No space with this id exists for this account.
500internal_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

NameInTypeDescription
space_id requiredpathstring

Responses

200Success.
400Missing or invalid API key.
401Missing or invalid API key.
404space_not_found: No space with this id exists for this account.
500internal_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"