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.
{
"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
"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
.