API FAQ
Technical questions about integrating with the Voodoo API — endpoints, authentication, and troubleshooting.
Looking for implementation questions?
It depends on your server:
- Hosted (Multi-Tenant) Big Block —
https://www.voodoodevices.com/api/ - Self-Hosted Big Block —
http://<your-bigblock-ip>/api/
The API endpoints are identical regardless of which server you use. Only the base URL changes.
Voodoo supports multiple authentication methods:
- API-KEY header — Pass your key in the
X-API-KEYheader. Simplest for server-to-server integration. - Basic Auth — Username and password in the Authorization header. Good for quick testing.
- OAuth2 — Token-based auth for enterprise integrations with automatic refresh.
- Session-based Authentication — Uses a session cookie together with a CSRF token and Referer header. Useful when you are working through a logged-in browser session.
See the Authentication guide for detailed examples.
Send a POST request to the single-device endpoint with explicit command fields:
import requests
response = requests.post(
"https://www.voodoodevices.com/api/device/D4F660:AFA0CB/",
headers={
"API-KEY": "YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"command": "flash",
"line1": "SKU-9876",
"quantity": 3,
"color": "green",
"seconds": 60,
},
)
print(response.status_code) # 200 = successcurl -X POST "https://www.voodoodevices.com/api/device/D4F660:AFA0CB/" \
-H "API-KEY: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"command": "flash",
"line1": "SKU-9876",
"quantity": 3,
"color": "green",
"seconds": 60
}'Send a POST request to the same single-device endpoint, but use static fields instead of command fields:
import requests
response = requests.post(
"https://www.voodoodevices.com/api/device/D4F660:AFA0CB/",
headers={
"API-KEY": "YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"statica": "SKU-9876",
"staticb": "Shelf 12",
"staticc": "Reorder at 4",
},
)
print(response.status_code) # 200 = successcurl -X POST "https://www.voodoodevices.com/api/device/D4F660:AFA0CB/" \
-H "API-KEY: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"statica": "SKU-9876",
"staticb": "Shelf 12",
"staticc": "Reorder at 4"
}'Statics persist until you change them. They do not timeout and do not generate ACK or NACK events.
Use the plural devices endpoint and send a JSON array. Each element in that array must include its own deviceid. Note the URL carefully: /api/devices/ (plural) is a different endpoint from /api/device/ (singular) — the plural form is the one that accepts a batch array.
import requests
response = requests.post(
"https://www.voodoodevices.com/api/devices/",
headers={
"API-KEY": "YOUR_API_KEY",
"Content-Type": "application/json",
},
json=[
{
"deviceid": "D4F660:AFA0CB",
"command": "flash",
"line1": "SKU-100",
"quantity": 2,
"color": "green",
"seconds": 60,
},
{
"deviceid": "B8C3E1:2F94D7",
"command": "flash",
"line1": "SKU-200",
"quantity": 1,
"color": "blue",
"seconds": 60,
},
],
)
print(response.status_code) # 200 = successcurl -X POST "https://www.voodoodevices.com/api/devices/" \
-H "API-KEY: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '[
{
"deviceid": "D4F660:AFA0CB",
"command": "flash",
"line1": "SKU-100",
"quantity": 2,
"color": "green",
"seconds": 60
},
{
"deviceid": "B8C3E1:2F94D7",
"command": "flash",
"line1": "SKU-200",
"quantity": 1,
"color": "blue",
"seconds": 60
}
]'Use this same endpoint for bulk static updates too. The difference is the fields inside each array element, not the endpoint itself.
Every time your code opens a brand-new HTTP connection to the Voodoo server it pays a setup cost you might not notice in testing but that adds up fast in production. Here is what a typical cold request actually costs:
dns: ~50 ms tcp: ~120 ms tls: ~195 ms ───────────────────── overhead: ~365 ms (before your command even reaches the server)
The Voodoo server itself responds in under 20 ms. All that extra time is pure connection setup — and it happens again for every request that opens a fresh connection.
The fix is to keep the same requests.Session() object alive and reuse it. A single session maintains a connection pool, so subsequent calls to the same host travel over the already-established TLS connection instead of paying DNS + TCP + TLS all over again.
import requests
BASE_URL = "https://www.voodoodevices.com/api/"
# Create the session once and reuse it for every command.
# This avoids paying the DNS + TCP + TLS setup cost
# on each individual request.
session = requests.Session()
session.headers.update({
"API-KEY": "YOUR_API_KEY",
"Content-Type": "application/json",
})
# Command to first device — uses /api/device/ (singular)
session.post(
BASE_URL + "device/E8D008:619874/",
json={
"command": "flash",
"line1": "Pick",
"line2": "SKU 12345",
"quantity": 5,
"seconds": 30,
"color": "r",
},
)
# Command to second device — same session, same connection
session.post(
BASE_URL + "device/E476EA:EA3329/",
json={
"command": "flash",
"line1": "Put",
"line2": "Order 98765",
"quantity": 2,
"seconds": 30,
"color": "g",
},
)
session.close()This pattern uses /api/device/ (singular) for each individual command — not the plural batch endpoint. It is the right choice when your app issues commands one at a time but still wants the efficiency of a persistent connection.
Both control the same devices, but they differ in approach:
- REST API — Standard RESTful endpoints with JSON payloads and proper HTTP methods (GET, POST, PUT, DELETE). Supports authentication headers, closed-loop confirmation, and bulk operations.
- GET API (QueryString) — All parameters in the URL. No request body. Fire-and-forget. Best for simple integrations, legacy systems, or barcode scanners that can trigger URLs.
The GET API is a subset of the REST API. If you can use REST, prefer it for better error handling and scalability.
| Code | Meaning |
|---|---|
| 200 | Success — command accepted by the server |
| 400 | Bad request — missing or invalid parameters |
| 401 | Unauthorized — invalid or missing API key |
| 404 | Not found — device or resource doesn't exist |
| 500 | Server error — contact support if persistent |
To clear statics, set the static fields you are using to the empty string:
import requests
response = requests.post(
"https://www.voodoodevices.com/api/device/D4F660:AFA0CB/",
headers={
"API-KEY": "YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"statica": "",
"staticb": "",
"staticc": "",
"staticd": "",
"statice": "",
},
)
print(response.status_code) # 200 = successcurl -X POST "https://www.voodoodevices.com/api/device/D4F660:AFA0CB/" \
-H "API-KEY: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"statica": "",
"staticb": "",
"staticc": "",
"staticd": "",
"statice": ""
}'An active command normally clears when the operator pushes the button. If you need to clear it from software, send a kill command with the same nonce value that you used when you issued the original command:
import requests
response = requests.post(
"https://www.voodoodevices.com/api/device/D4F660:AFA0CB/",
headers={
"API-KEY": "YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"command": "kill",
"nonce": "pick-12345",
},
)
print(response.status_code) # 200 = successcurl -X POST "https://www.voodoodevices.com/api/device/D4F660:AFA0CB/" \
-H "API-KEY: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"command": "kill",
"nonce": "pick-12345"
}'The kill request must use the same nonce that your application chose for the command you want to clear. A different nonce will not target that command.
Sequences are pre-defined, multi-step workflows that run on the server. Instead of sending individual commands for each pick in an order, you create a Sequence with all steps and launch it. The server handles progression, timing, and device coordination.
Use Sequences when:
- An order involves multiple pick locations in sequence
- You want the server to manage step-by-step progression
- You need consistent timing and color coding across steps
See the Sequence API reference for endpoint details.
