Holds the request open for up to wait seconds (capped by the server, 25 by default) and returns commands queued for this booth after since. Kinds: sync_now (pull the library), update_available and revoke (acknowledging it revokes the key). Store next_since and send it on the next poll. Acknowledge each command.
Auth. A booth device key. Needs the device:read scope. Send X-Folio-Device-Contract; an unsupported version gets 426.
Limits. Responses carry RateLimit-Limit and RateLimit-Remaining; a 429 carries Retry-After.
Auth
Booth device key
Scope
device:read
Operation id
deviceCommands
Parameters
Parameters
Name
In
Description
since
query
Return commands after this sequence number. (integer, e.g. 0)
wait
query
Seconds to hold the request open when nothing is queued. Capped by the server. (integer, e.g. 25)
{
"status": false,
"message": "the page cursor is not valid",
"error": {
"code": "invalid_argument",
"status": 400,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
401 The key or session is missing, invalid or expired.401 example
{
"status": false,
"message": "That API key is not valid or has expired.",
"error": {
"code": "unauthenticated",
"status": 401,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
403 The credential lacks the scope, the church is suspended, the feature is not in the plan, or the address is not allowed.403 example
{
"status": false,
"message": "That API key does not have the required scope.",
"error": {
"code": "permission_denied",
"status": 403,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
404 The resource is not in this church.404 example
{
"status": false,
"message": "That item is not in this church.",
"error": {
"code": "not_found",
"status": 404,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
409 Conflict, including a duplicate Idempotency-Key still being processed.409 example
{
"status": false,
"message": "A request with this Idempotency-Key is still running.",
"error": {
"code": "conflict",
"status": 409,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
422 A field failed validation, or an Idempotency-Key was reused with a different body.422 example
{
"status": false,
"message": "A field is not valid.",
"error": {
"code": "validation",
"status": 422,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
426 The booth's contract version is no longer supported. Call GET /v1/device/update.426 example
Marks a command as handled. Acknowledging a revoke command revokes the device key, so the booth's next call answers 401.
Auth. A booth device key. Needs the device:write scope. Send X-Folio-Device-Contract; an unsupported version gets 426.
Limits. Responses carry RateLimit-Limit and RateLimit-Remaining; a 429 carries Retry-After.
Auth
Booth device key
Scope
device:write
Operation id
deviceAckCommand
Parameters
Parameters
Name
In
Description
command required
path
The command id from GET /v1/device/commands. (string, e.g. cmd_0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012)
Idempotency-Key
header
Makes a retry safe. The same key with the same body replays the first response for 24 hours; the same key with a different body returns 422; a duplicate that is still running returns 409 with Retry-After. Responses that carry a one-time secret are not replayed. (string, e.g. sunday-2026-11-01-upload)
Responses
Response data fields
Field
Type
Description
created_at
string
When it was created, in RFC 3339 UTC.
id
string
The id, with a type prefix such as tch_ or note_.
kind
enum: sync_now | update_available | revoke
The kind.
object
enum: device_command
The object type.
payload
object
Details for the command.
seq
integer (int64)
The command's sequence number. Send the highest one as since.
{
"status": false,
"message": "the page cursor is not valid",
"error": {
"code": "invalid_argument",
"status": 400,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
401 The key or session is missing, invalid or expired.401 example
{
"status": false,
"message": "That API key is not valid or has expired.",
"error": {
"code": "unauthenticated",
"status": 401,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
403 The credential lacks the scope, the church is suspended, the feature is not in the plan, or the address is not allowed.403 example
{
"status": false,
"message": "That API key does not have the required scope.",
"error": {
"code": "permission_denied",
"status": 403,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
404 The resource is not in this church.404 example
{
"status": false,
"message": "That item is not in this church.",
"error": {
"code": "not_found",
"status": 404,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
409 Conflict, including a duplicate Idempotency-Key still being processed.409 example
{
"status": false,
"message": "A request with this Idempotency-Key is still running.",
"error": {
"code": "conflict",
"status": 409,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
422 A field failed validation, or an Idempotency-Key was reused with a different body.422 example
{
"status": false,
"message": "A field is not valid.",
"error": {
"code": "validation",
"status": 422,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
426 The booth's contract version is no longer supported. Call GET /v1/device/update.426 example
Sends events recorded during a service, such as songs shown. Events are deduplicated by their id, so resending a batch after a dropped connection is safe. Send up to 500 at a time.
Auth. A booth device key. Needs the device:write scope. Send X-Folio-Device-Contract; an unsupported version gets 426.
Limits. Responses carry RateLimit-Limit and RateLimit-Remaining; a 429 carries Retry-After.
Auth
Booth device key
Scope
device:write
Operation id
deviceRecordEvents
Parameters
Parameters
Name
In
Description
Idempotency-Key
header
Makes a retry safe. The same key with the same body replays the first response for 24 hours; the same key with a different body returns 422; a duplicate that is still running returns 409 with Retry-After. Responses that carry a one-time secret are not replayed. (string, e.g. sunday-2026-11-01-upload)
{
"status": false,
"message": "the page cursor is not valid",
"error": {
"code": "invalid_argument",
"status": 400,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
401 The key or session is missing, invalid or expired.401 example
{
"status": false,
"message": "That API key is not valid or has expired.",
"error": {
"code": "unauthenticated",
"status": 401,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
403 The credential lacks the scope, the church is suspended, the feature is not in the plan, or the address is not allowed.403 example
{
"status": false,
"message": "That API key does not have the required scope.",
"error": {
"code": "permission_denied",
"status": 403,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
404 The resource is not in this church.404 example
{
"status": false,
"message": "That item is not in this church.",
"error": {
"code": "not_found",
"status": 404,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
409 Conflict, including a duplicate Idempotency-Key still being processed.409 example
{
"status": false,
"message": "A request with this Idempotency-Key is still running.",
"error": {
"code": "conflict",
"status": 409,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
422 A field failed validation, or an Idempotency-Key was reused with a different body.422 example
{
"status": false,
"message": "A field is not valid.",
"error": {
"code": "validation",
"status": 422,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
426 The booth's contract version is no longer supported. Call GET /v1/device/update.426 example
{
"status": false,
"message": "the page cursor is not valid",
"error": {
"code": "invalid_argument",
"status": 400,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
401 The key or session is missing, invalid or expired.401 example
{
"status": false,
"message": "That API key is not valid or has expired.",
"error": {
"code": "unauthenticated",
"status": 401,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
403 The credential lacks the scope, the church is suspended, the feature is not in the plan, or the address is not allowed.403 example
{
"status": false,
"message": "That API key does not have the required scope.",
"error": {
"code": "permission_denied",
"status": 403,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
404 The resource is not in this church.404 example
{
"status": false,
"message": "That item is not in this church.",
"error": {
"code": "not_found",
"status": 404,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
409 Conflict, including a duplicate Idempotency-Key still being processed.409 example
{
"status": false,
"message": "A request with this Idempotency-Key is still running.",
"error": {
"code": "conflict",
"status": 409,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
422 A field failed validation, or an Idempotency-Key was reused with a different body.422 example
{
"status": false,
"message": "A field is not valid.",
"error": {
"code": "validation",
"status": 422,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
426 The booth's contract version is no longer supported. Call GET /v1/device/update.426 example
{
"status": false,
"message": "the page cursor is not valid",
"error": {
"code": "invalid_argument",
"status": 400,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
401 The key or session is missing, invalid or expired.401 example
{
"status": false,
"message": "That API key is not valid or has expired.",
"error": {
"code": "unauthenticated",
"status": 401,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
403 The credential lacks the scope, the church is suspended, the feature is not in the plan, or the address is not allowed.403 example
{
"status": false,
"message": "That API key does not have the required scope.",
"error": {
"code": "permission_denied",
"status": 403,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
404 The resource is not in this church.404 example
{
"status": false,
"message": "That item is not in this church.",
"error": {
"code": "not_found",
"status": 404,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
409 Conflict, including a duplicate Idempotency-Key still being processed.409 example
{
"status": false,
"message": "A request with this Idempotency-Key is still running.",
"error": {
"code": "conflict",
"status": 409,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
422 A field failed validation, or an Idempotency-Key was reused with a different body.422 example
{
"status": false,
"message": "A field is not valid.",
"error": {
"code": "validation",
"status": 422,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
426 The booth's contract version is no longer supported. Call GET /v1/device/update.426 example
{
"status": false,
"message": "the page cursor is not valid",
"error": {
"code": "invalid_argument",
"status": 400,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
401 The key or session is missing, invalid or expired.401 example
{
"status": false,
"message": "That API key is not valid or has expired.",
"error": {
"code": "unauthenticated",
"status": 401,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
403 The credential lacks the scope, the church is suspended, the feature is not in the plan, or the address is not allowed.403 example
{
"status": false,
"message": "That API key does not have the required scope.",
"error": {
"code": "permission_denied",
"status": 403,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
404 The resource is not in this church.404 example
{
"status": false,
"message": "That item is not in this church.",
"error": {
"code": "not_found",
"status": 404,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
409 Conflict, including a duplicate Idempotency-Key still being processed.409 example
{
"status": false,
"message": "A request with this Idempotency-Key is still running.",
"error": {
"code": "conflict",
"status": 409,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
422 A field failed validation, or an Idempotency-Key was reused with a different body.422 example
{
"status": false,
"message": "A field is not valid.",
"error": {
"code": "validation",
"status": 422,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
426 The booth's contract version is no longer supported. Call GET /v1/device/update.426 example
The booth's version of creating a teaching: it returns a signed upload URL and a key. PUT the recording to url, then call POST /v1/device/teachings/{teaching}/upload with key. The same plan checks apply as for an API key, so the booth needs no second credential.
Auth. A booth device key. Needs the device:write scope. Send X-Folio-Device-Contract; an unsupported version gets 426.
Plan. Needs the booth feature and transcription hours left in the plan (402 quota_exceeded otherwise).
Limits. Responses carry RateLimit-Limit and RateLimit-Remaining; a 429 carries Retry-After.
Auth
Booth device key
Scope
device:write
Plan
Needs the booth feature and transcription hours left in the plan (402 quota_exceeded otherwise).
Operation id
deviceCreateTeaching
Parameters
Parameters
Name
In
Description
Idempotency-Key
header
Makes a retry safe. The same key with the same body replays the first response for 24 hours; the same key with a different body returns 422; a duplicate that is still running returns 409 with Retry-After. Responses that carry a one-time secret are not replayed. (string, e.g. sunday-2026-11-01-upload)
Request body
Request body fields
Field
Type
Description
content_type
string
The media type of the recording, such as audio/mpeg.
filename
string
The file name of the recording, used to keep the extension.
{
"status": false,
"message": "the page cursor is not valid",
"error": {
"code": "invalid_argument",
"status": 400,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
401 The key or session is missing, invalid or expired.401 example
{
"status": false,
"message": "That API key is not valid or has expired.",
"error": {
"code": "unauthenticated",
"status": 401,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
403 The credential lacks the scope, the church is suspended, the feature is not in the plan, or the address is not allowed.403 example
{
"status": false,
"message": "That API key does not have the required scope.",
"error": {
"code": "permission_denied",
"status": 403,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
404 The resource is not in this church.404 example
{
"status": false,
"message": "That item is not in this church.",
"error": {
"code": "not_found",
"status": 404,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
409 Conflict, including a duplicate Idempotency-Key still being processed.409 example
{
"status": false,
"message": "A request with this Idempotency-Key is still running.",
"error": {
"code": "conflict",
"status": 409,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
422 A field failed validation, or an Idempotency-Key was reused with a different body.422 example
{
"status": false,
"message": "A field is not valid.",
"error": {
"code": "validation",
"status": 422,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
426 The booth's contract version is no longer supported. Call GET /v1/device/update.426 example
Tells Folio the recording is in storage and starts processing.
Auth. A booth device key. Needs the device:write scope. Send X-Folio-Device-Contract; an unsupported version gets 426.
Limits. Responses carry RateLimit-Limit and RateLimit-Remaining; a 429 carries Retry-After.
Auth
Booth device key
Scope
device:write
Operation id
deviceCompleteUpload
Parameters
Parameters
Name
In
Description
teaching required
path
The teaching id, such as tch_0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012. (string, e.g. tch_0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012)
Idempotency-Key
header
Makes a retry safe. The same key with the same body replays the first response for 24 hours; the same key with a different body returns 422; a duplicate that is still running returns 409 with Retry-After. Responses that carry a one-time secret are not replayed. (string, e.g. sunday-2026-11-01-upload)
{
"status": false,
"message": "the page cursor is not valid",
"error": {
"code": "invalid_argument",
"status": 400,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
401 The key or session is missing, invalid or expired.401 example
{
"status": false,
"message": "That API key is not valid or has expired.",
"error": {
"code": "unauthenticated",
"status": 401,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
403 The credential lacks the scope, the church is suspended, the feature is not in the plan, or the address is not allowed.403 example
{
"status": false,
"message": "That API key does not have the required scope.",
"error": {
"code": "permission_denied",
"status": 403,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
404 The resource is not in this church.404 example
{
"status": false,
"message": "That item is not in this church.",
"error": {
"code": "not_found",
"status": 404,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
409 Conflict, including a duplicate Idempotency-Key still being processed.409 example
{
"status": false,
"message": "A request with this Idempotency-Key is still running.",
"error": {
"code": "conflict",
"status": 409,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
422 A field failed validation, or an Idempotency-Key was reused with a different body.422 example
{
"status": false,
"message": "A field is not valid.",
"error": {
"code": "validation",
"status": 422,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
426 The booth's contract version is no longer supported. Call GET /v1/device/update.426 example
Returns the latest and the minimum supported version for a platform, and where to download. It works even when X-Folio-Device-Contract is too old, so a stale booth can learn where to update. update_required means the booth must update before other calls succeed.
Auth. A booth device key. Needs the device:read scope. Send X-Folio-Device-Contract; an unsupported version gets 426.
Limits. Responses carry RateLimit-Limit and RateLimit-Remaining; a 429 carries Retry-After.
Auth
Booth device key
Scope
device:read
Operation id
deviceUpdate
Parameters
Parameters
Name
In
Description
platform
query
For example windows, macos or linux. (string, e.g. windows)
current
query
The version the booth runs now, such as 1.4.2. (string, e.g. 1.4.2)
Responses
Response data fields
Field
Type
Description
contract
object
The X-Folio-Device-Contract versions this API accepts. Other values get 426.
current
integer (int64)
The newest contract version the API speaks.
minimum
integer (int64)
The oldest contract version accepted.
current
string
The version the booth reported.
latest
string
The newest Folio Live version.
min_supported
string
The oldest version that still works.
notes
string
Release notes for the update.
object
enum: device_update
The object type.
platform
string
windows, mac or linux.
update_available
boolean
Whether a newer version exists.
update_required
boolean
True when current is below min_supported. The booth must update before it can sync.
url
string
Where to download the build for this platform, when one is published.
200 Success. Check for a Folio Live update.200 example
{
"status": false,
"message": "the page cursor is not valid",
"error": {
"code": "invalid_argument",
"status": 400,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
401 The key or session is missing, invalid or expired.401 example
{
"status": false,
"message": "That API key is not valid or has expired.",
"error": {
"code": "unauthenticated",
"status": 401,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
403 The credential lacks the scope, the church is suspended, the feature is not in the plan, or the address is not allowed.403 example
{
"status": false,
"message": "That API key does not have the required scope.",
"error": {
"code": "permission_denied",
"status": 403,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
404 The resource is not in this church.404 example
{
"status": false,
"message": "That item is not in this church.",
"error": {
"code": "not_found",
"status": 404,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
409 Conflict, including a duplicate Idempotency-Key still being processed.409 example
{
"status": false,
"message": "A request with this Idempotency-Key is still running.",
"error": {
"code": "conflict",
"status": 409,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
422 A field failed validation, or an Idempotency-Key was reused with a different body.422 example
{
"status": false,
"message": "A field is not valid.",
"error": {
"code": "validation",
"status": 422,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
426 This booth must update Folio Live. See GET /v1/device/update.429 Rate limited. Wait Retry-After seconds.429 example
A booth sends the six-digit code a team member created in the console. The code is single use and valid for 10 minutes. The response holds the device key (folio_booth_…) exactly once; store it securely on the booth.
This route is public because the booth has no key yet. It is rate limited per client address and per code prefix; past the limit it answers 429. Unknown, expired and used codes all answer the same 422, so a guess reveals nothing. Codes are stored hashed.
Auth. None. This route is public.
Plan. The church needs the booth feature.
Auth
No key
Plan
The church needs the booth feature.
Operation id
claimDevicePairing
Request body
Request body fields
Field
Type
Description
code
string
A machine-readable code.
Example request body
{
"code": "482913"
}
Responses
Response data fields
Field
Type
Description
device
string
Device id (dev_...).
id
string
The id, with a type prefix such as tch_ or note_.
key
string
The device key (folio_booth_...). Shown once.
object
enum: device_pairing
The object type.
paired
boolean
Always true when pairing worked.
200 Success. Trade a pairing code for a device key.200 example
{
"status": false,
"message": "the page cursor is not valid",
"error": {
"code": "invalid_argument",
"status": 400,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
422 The code is unknown, expired or already used.422 example
{
"status": false,
"message": "That code is not valid. Ask for a new one.",
"error": {
"code": "validation",
"status": 422,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}
500 Something went wrong. Quote error.request_id.500 example
{
"status": false,
"message": "Something went wrong. Quote the request id to support.",
"error": {
"code": "internal",
"status": 500,
"request_id": "req_b337770ecebe03b03a88a8998b59263d"
}
}