The Device API is the API expected by the clock (the screen) in the course of its normal operation.
This documentation is useful for developers who are:
- creating their own clocks, making use of the existing
poem.townback-end; or, - replacing
poem.townwith their own, compatible back-end.
If, instead, you want to post notes to an existing clock from a script or third-party app, see the Web API.
Usage#
Endpoints
The root for all API endpoints is https://poem.town/api/v1/clock.
Poem/1 clocks allow for changing the server URL via the Wi-Fi configuration interface. This will change the server URL but not the path. For example, changing the server URL to example.org will change the endpoint root to “https://example.org/api/v1/clock`.
Authorization
All API requests (except for /status)require an HTTP header with a valid API key of the form:
Authorization: Bearer poem.dummyKeyRequest and response format
All requests are made using HTTP POST with a JSON body. Responses are JSON.
Datetimes always use ISO8601 without milliseconds and with an explicit Z, e.g. 2025-01-03T16:49:10Z.
/status#
Use /status as an end-to-end test when a device is booting, before fetching a poem. Note: this method must be called at least once before the user visits the personalization website. (If it is not called then there is a risk that the screen ID will not be recognised.)
Authorization is not required for this endpoint.
Example
curl \
--json '{"screenId": "80AB412341234"}' \
https://poem.town/api/v1/clock/statusOptionally include buildId to identify the firmware build running on the device:
curl \
--json '{"screenId": "80AB412341234", "buildId": "abc123"}' \
https://poem.town/api/v1/clock/statusRequest
The request body is an object with screenId.
{
"screenId": "80AB412341234"
}Response
The request object is returned with a success property, and information about this device.
{
"success": true // boolean,
"device": {
screenId: "80AB412341234", // string
buildId: "abc123", // string | null
lastSeen: "2025-01-03T16:49:10Z", // string, ISO date — last device-initiated contact (any endpoint)
seen: 2, // number — count of /status calls
createdAt: "2025-01-03T12:00:00Z", // string, ISO date
isClaimed: false // boolean
}
}/compose#
Use /compose every minute to compose a new poem.
Examples
Use time24 to compose a poem for a specific time:
curl \
-H "Authorization: Bearer poem.dummyKey" \
--json '{"time24": "12:34"}' \
https://poem.town/api/v1/clock/composeAlternatively use geolocate to pass in the time in UTC, and allow the server to auto-detect the device’s local time:
curl \
-H "Authorization: Bearer poem.dummyKey" \
--json '{"geolocate": "2024-08-20T19:51:00Z"}' \
https://poem.town/api/v1/clock/composeAdditionally pass in screenId to allow the server to load personalization settings such as preferred font:
curl \
-H "Authorization: Bearer poem.dummyKey" \
--json '{"screenId": "80AB412341234", "geolocate": "2024-08-20T19:51:00Z"}' \
https://poem.town/api/v1/clock/composeRequest
The request body must include EITHER time24 or geolocate, but not both. The screenId is optional.
{
"screenId": "80AB412341234", // string, optional
"time24": "12:34", // string, either time24 or geolocate
"geolocate": "2024-09-01T12:34:00Z" // string, either time24 or geolocate
}Response
The response object includes the composed poem and a number of display hints.
The device may decide how to handle a note. For example, a Poem/1 device will display a note on its screen until it is 30 minutes old, and then remove it — even if the note is unseen. The latest note is included in the response object (seen or not) for up to 24 hours.
If the screensaver property is present and is true then a Poem/1 device will display a blank screen (unless the user interacts with the device).
{
"poemId": "123456", // string
"time24": "12:34", // string
"poem": "The poem text / Two lines with a slash", // string
"note": {
// optional
"noteId": "123456", // string
"body": "The note text", // string
"posted": "2024-09-01T12:34:00Z", // string, ISO date
"seen": false // boolean
},
"preferredFont": "INTER", // "INTER" | "PLAYFAIR", optional
"screensaver": true, // boolean, should default to false
"debug": {
// optional, should be ignored by the device
// ...
}
}/notes/{noteId}/seen#
When the user interacts with the device to mark a note as seen, use this endpoint to update the note’s status.
Example
curl \
-H "Authorization: Bearer poem.dummyKey" \
--json '{"screenId": "80AB412341234"}' \
https://poem.town/api/v1/clock/notes/30/seenRequest
The note ID is part of the URL. (Retain it from the /compose response.)
The request body is an object with screenId.
{
"screenId": "80AB412341234"
}Response
A 404 error is returned if screenId is not known.
The response object will indicate success whether or not the note ID is known, so long as it was valid.
{
"success": true // boolean
}/likes/{poemId}/mark#
When the user interacts with the device to like a poem, use this endpoint to record it.
Example
curl \
-H "Authorization: Bearer poem.dummyKey" \
--json '{"screenId": "80AB412341234"}' \
https://poem.town/api/v1/clock/likes/832253/markRequest
The poem ID is part of the URL. (Retain it from the /compose response.)
The request body is an object with screenId.
The screenId must be claimed.
{
"screenId": "80AB412341234"
}Response
The response will indicate success if the screenId is known, valid, and claimed, and the request was well formed.
A 404 will be returned if the poemId in the URL is not known.
{
"success": true // boolean
}/likes/{poemId}/unmark#
When the user interacts with the device to remove their like from a poem, use this endpoint to record the action.
The request and response are identical to the /likes/\{poemId}/mark endpoint.
Example
curl \
-H "Authorization: Bearer poem.dummyKey" \
--json '{"screenId": "80AB412341234"}' \
https://poem.town/api/v1/clock/likes/832253/unmarkRequest
See /likes/\{poemId}/mark.
Response
See /likes/\{poemId}/mark.