Skip to main content

Send chat

Post chat to a broadcast under the bot account's name. It shows up in the Spoon app chat with the bot account's nickname.

Method and pathPOST /v1/live/chat
Permissionchat.send
Success204 (no body)
When not on air404 (OAPI_MNGR_0301)
export SPOON_BASE_URL="https://kr-openapi.spooncast.net"
export SPOON_ACCESS_TOKEN="{your access token}"

For the Japan region use https://jp-openapi.spooncast.net — see regions.

Request​

POST /v1/live/chat

Headers​

NameRequiredDescription
Authorization✔Bearer {access_token}.
Content-Type✔application/json.

Body​

{
"message": "Hello! Your bot says hi"
}
FieldTypeNullableDescription
messagestringNoThe chat message to send. Up to 200 characters. Empty or whitespace-only gives a 400.

The token decides which broadcast this goes to. Nothing in the path or the body points at a broadcast.

Request example​

curl -i -X POST "$SPOON_BASE_URL/v1/live/chat" \
-H "Authorization: Bearer $SPOON_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"message":"Hello! Your bot says hi"}'

Response​

On success you get a 204 with no body. There is nothing to parse — read the status code only.

HTTP/2 204

Code​

// send.ts — node --experimental-strip-types send.ts "your message"

interface ApiError {
detailCode: string;
message: string;
}

const res = await fetch(`${process.env.SPOON_BASE_URL}/v1/live/chat`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SPOON_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: process.argv[2] }),
});

if (res.status === 204) {
console.log('Sent.');
} else if (res.status === 404) {
console.log('Not on air.');
} else if (res.status === 429) {
console.log('Chat is busy right now. Try again shortly.');
} else if (res.status === 401) {
console.error('The integration is disconnected. Ask the DJ to authorize again.');
} else {
// A 403 for a missing permission is blocked in front, so it has no detailCode
const { detailCode, message } = (await res.json()) as Partial<ApiError>;
console.error(`Send failed ${res.status} ${detailCode ?? 'insufficient scope'}: ${message ?? ''}`);
}
node --experimental-strip-types send.ts "Hello"

Watch out​

Your own messages do not come back on your stream. The event stream does not return a bot's own posts to that bot, so it cannot re-run its own greeting as a command. Posts from other bots in the same broadcast are visible.

Over 200 characters is a 400. The count is in UTF-16 code units, so a single emoji can count as two.

A 429 is not only about your own rate. The send limit is shared across the whole broadcast, so busy listener chat can trip your bot when it has done nothing. Slowing your own sending will not clear it immediately — wait a moment and try again.

If the DJ blocks you, the bot is blocked. Freezing chat (isChatFrozen) or chat-banning the bot gives a 403, and a kick stops it joining at all. Checking isChatFrozen through Read the current broadcast before posting avoids the failure up front.

You can post without connecting to the stream. If the bot is not in the room, the server joins it first.

Errors​

HTTPdetailCodeWhat happenedWhat to do
400OAPI_MNGR_0103The request is malformed — a broken body, a missing required field, a missing Content-Type, or the wrong method.Fix the request format. Sending the same request again gives the same result.
400OAPI_MNGR_0108message is empty or only whitespace.Check what you are sending.
400OAPI_MNGR_0109Longer than 200 characters.Shorten it.
401noneThe token is missing or invalid.Ask the DJ to authorize again.
403noneThe chat.send permission is missing.Add the permission and get consent again.
403OAPI_MNGR_0208Chat is frozen, or the bot is chat-banned.The two are not distinguished. Retry shortly, and tell the DJ if it keeps failing.
403OAPI_MNGR_0209The bot cannot join the broadcast. The DJ may have blocked it.Retrying does not help. The DJ has to lift it.
404OAPI_MNGR_0301The DJ is not on air.Wait until the broadcast starts.
429OAPI_MNGR_0302You are sending chat too frequently.Wait a moment and send again.
502OAPI_MNGR_2302Broadcast server error. It happens at the broadcast check and join step before sending.Retry after a backoff.
502OAPI_MNGR_2303Chat server error.Retry after a backoff.

There are two kinds of 429. The OAPI_MNGR_0302 above (the chat send limit) has a body, but hitting the API call quota gives you no body and no detailCode. Branch on the status code before you parse the body, or it throws — the example above is written that way.

States outside this table can arrive too. A wrong method or Content-Type (400, 405, 415 — OAPI_MNGR_0103), the call quota (429, no body), and server errors (500 — OAPI_MNGR_1002) are common to every endpoint — see Error codes.