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.town
back-end; or, - replacing
poem.town
with their own, compatible back-end.
If, instead, you want to post notes to an existing clock, please contact us to discuss 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 require an HTTP header with a valid API key of the form:
Authorization: Bearer poem.dummyKey
Request 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.)
Example
curl \
-H "Authorization: Bearer poem.dummyKey" \
--json '{"screenId": "80AB412341234"}' \
https://poem.town/api/v1/clock/status
Request
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
lastSeen: "2025-01-03T16:49:10Z", // string, ISO date
seen: 2, // number
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/compose
Alternatively 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/compose
Additionally 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/compose
Request
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/seen
Request
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/mark
Request
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/unmark
Request
See /likes/\{poemId}/mark
.
Response
See /likes/\{poemId}/mark
.