Skip to content

Motherboard X Web API

The External Display API displays text on a specific motherboard in Home mode or supplies an input to its current routine step. It is a public, board-scoped API and does not require an end-user login. The motherboard owner supplies the board’s API ID.

POST https://api.motherboard.conjuringlab.com/api/display
Header Required Value
X-Motherboard-Api-Id Yes The board’s API ID, such as mb_...
Content-Type Yes application/json
Idempotency-Key For later status checks A new UUID for each logical display request

Keep the API ID, idempotency key, and exact body together. To check the request later, resend all three unchanged. There is no public status endpoint.

The owner can rotate the API ID at any time. Old IDs stop working immediately.

{
"kind": "text",
"text": "Hello world",
"orientation": "portrait",
"zone": "bottom-half"
}
Field Type Required Description
kind string Yes Must be "text".
text string Yes Text or routine input. Empty and whitespace-only strings are rejected. Send numbers as strings, such as "42".
orientation string or null No "landscape" or "portrait", lowercase. Omit or use null for landscape. Other values, including uppercase names, are rejected.
zone string or null No "top-half" or "bottom-half". Omit or use null for the full display.

In Home mode, the selected half follows the text orientation. Text fits inside that half and content outside it is preserved. Orientation applies to this request only and does not change board settings. During routines, orientation and zone are ignored; the routine controls rendering.

Literal \n, \r\n, and \r sequences in text are converted to line breaks before delivery. For example, the JSON value "Hello\\nworld" contains a literal backslash followed by n; the API converts it into a line break.

The complete notification payload, including metadata and JSON encoding, must fit within 4,096 UTF-8 bytes. This is not a 4,096-character allowance for text: the available space depends on the payload and characters used. Oversized text returns 400 invalid_request with the message The display text is too large for reliable notification delivery. before an attempt is stored.

Replace mb_... with your board’s API ID and use a fresh UUID for your first request. Run the exact same command again later to check that request’s state.

Terminal window
curl -i -X POST 'https://api.motherboard.conjuringlab.com/api/display' \
-H 'X-Motherboard-Api-Id: mb_...' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 3f36dc0d-3576-4f4e-aae0-86235913cf81' \
-d '{
"kind": "text",
"text": "Hello\\nworld",
"orientation": "portrait",
"zone": "bottom-half"
}'

Create the key and body once per logical display request, outside the function used to submit or recheck it.

const apiId = 'mb_...';
const idempotencyKey = crypto.randomUUID();
const body = JSON.stringify({
kind: 'text',
text: 'Hello world',
orientation: 'portrait',
zone: 'bottom-half',
});
async function submitOrCheck() {
const response = await fetch(
'https://api.motherboard.conjuringlab.com/api/display',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Motherboard-Api-Id': apiId,
'Idempotency-Key': idempotencyKey,
},
body,
},
);
return {
httpStatus: response.status,
body: await response.json(),
};
}
const initialResult = await submitOrCheck();
// For 202, call submitOrCheck() later with bounded backoff and a deadline.

Handle network failures in your calling code. If you check the same request after a failure, retain the original key and body; generating a new key starts a new attempt. See delivery and retries.

{
"requestId": "uuid",
"status": "delivered"
}

The firmware or app bridge reported the request handled.

{
"requestId": "uuid",
"status": "accepted",
"message": "The request was accepted and is pending delivery."
}

This is not a terminal result. Recheck the same request using its original idempotency key and body. The requestId cannot be queried directly.

202 is the normal initial response while delivery is pending, including when the board is offline. The request remains stored if a wake notification or app bridge broadcast fails.

Errors normally use this shape:

{
"requestId": "uuid",
"status": "error",
"code": "invalid_request",
"message": "The request body is invalid."
}

405 Method Not Allowed is an exception: it returns {"error":"METHOD_NOT_ALLOWED"} with an Allow: GET, POST header. GET is reserved for firmware polling with a separate board credential; it is not a public status lookup using your API ID. Use POST for this integration.

See delivery, rate limits, and errors for retry guidance. This HTTP interface accepts text only.