Asynchronous operations
Commands to a product travel from the API through a Gateway and over Bluetooth, which
takes seconds, not milliseconds. So the API answers straight away with an event_id,
and tells you the result later, over the realtime connection.
This page covers the whole lifecycle.
It applies to POST /v2/products/{id}/state and
POST /v2/products/{id}/settings.
The lifecycle
| Step | Who | What happens |
|---|---|---|
| 1 | You | Connect to realtime, and wait until you're connected. |
| 2 | You → API | Send the command, for example {"operation": "open"}. |
| 3 | API | Checks the command can run. If not, answers with an HTTP error (below) and stops. |
| 4 | API → you | Answers 200 with {"success": true, "data": {"event_id": "a1b2c3d4"}}. The device hasn't moved yet. |
| 5 | API → Gateway | Sends the command to the first candidate Gateway and waits up to 30 seconds for its answer. |
| 6 | Gateway → product | Relays the command over Bluetooth; the product acts and reports back. |
| 7a | API → you | Success: a lot_change event with the same event_id and the product's new state. |
| 7b | API → you | Failure: a gateway_error event with the same event_id. |
event_id is 8 hexadecimal characters.
How the Gateway is chosen
The candidates are the Gateways that currently see the product over Bluetooth. The API tries them one at a time, strongest signal first, waiting up to 30 seconds for each:
- If a Gateway answers that the operation worked, you get
lot_change. - If it times out, or fails for a reason another Gateway might not hit (for example a Bluetooth error), the API moves on to the next Gateway.
- If the product itself reports a problem that no other Gateway can fix (
type0, 1, 5, 6, 7 or 8 below: for example jammed, or a car on top), it stops there and you getgateway_error. - If every Gateway fails, you get
gateway_errorwith the last error.
So with several Gateways in range, a result can take a multiple of 30 seconds.
Errors before anything is sent
These come back as HTTP errors in step 3, and nothing is sent to the device:
| HTTP | Code | Meaning | What to do |
|---|---|---|---|
| 403 | 30 | The product's subscription doesn't include remote control. | See Plans and permissions. |
| 403 | 4 | The product isn't visible to your token. | Check the product is on a lot your token can see; see Data model. |
| 404 | 6 | No product with that id. | Check the id. |
| 403 | 214 | An earlier operation on this product hasn't finished. | Wait for its result; don't retry in a loop. |
| 403 | 704 | No Gateway currently sees the product. | Check the Gateway is online, on the same lot and in range. |
| 403 | 206 | The product isn't paired. | Pair it with the Connect app. |
| 422 | 23 | Invalid body, for example an unknown operation. | Fix the request. |
gateway_error types
{
"type": 5,
"event_id": "a1b2c3d4",
"data": { "id": 123, "name": "Spot 12" },
"message": "Device jammed"
}
When no Gateway answered in time, type is the string "Timeout" and there's no
message. Otherwise it's a number. These are not the HTTP error codes.
type | message | Tries the next Gateway? |
|---|---|---|
0 | Auth error | No |
1 | Invalid guest key | No |
2 | Device disconnected | Yes |
3 | Bluetooth error | Yes |
4 | Device not found | Yes |
5 | Device jammed | No |
6 | Car on top | No |
7 | Sensor dirty | No |
8 | Device not initialized | No |
9 | Rtcc not set | Yes |
10 | Device busy | Yes |
11 | Unknown error | Yes |
"Timeout" | (none) | Yes |
wait_for_completion
POST /v2/products/{id}/state accepts an optional wait_for_completion (default
true). It's passed on to the Gateway, which by default waits until the product reports
it has reached the new state. Set it to false for a brain without input sensors,
which can't report an open or closed state. It doesn't change the HTTP response: you
always get the event_id straight away.
Writing a client
- Connect to realtime before sending commands, with the same API token, so you can't miss the result.
- Keep the pending
event_ids in a map, and resolve each on thelot_changeorgateway_errorthat carries it. The result can arrive before your HTTP call returns: buffer results you haven't matched yet. - Ignore
event_ids you didn't send. Your connection also receives operations started by others (the Connect app, other users) and automatic ones, such as Parklio setting a device's clock. - Give up client-side after a timeout of your choice, and re-read the product with
GET /v2/products/{id}to see where it ended up. - One operation per product at a time. A second command while the first is
pending gets
214.
There's no REST endpoint to look up a result by event_id, and no replay: if you were
disconnected when the event was sent, re-read the product's state over REST.
A working example is in the Quickstart.