Example
Start the reaction request and your usual model request when the user sends a message. Deliver each result as it arrives. This example uses your existing functions for generating replies, sending them, and attaching reactions.
// Start both from your message handler. No model tool call.
const reactionTask = fetch(
"https://useemote.com/v1/react",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.EMOTE_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": eventId,
},
body: JSON.stringify({ message, agent, context, reactions, frequency: "balanced" }),
signal: AbortSignal.timeout(3000),
},
).then(async (response) => {
if (!response.ok) throw new Error(`Emote: ${response.status}`);
const reaction = await response.json();
if (reaction !== "none") {
await attachReaction(messageId, reaction);
}
}).catch(reportReactionError);
// Your existing model and delivery code keep running independently.
const replyTask = Promise.resolve()
.then(() => generateReply(message, context))
.then(sendReply);
const [, reply] = await Promise.allSettled([reactionTask, replyTask]);
if (reply.status === "rejected") throw reply.reason;These snippets belong inside your existing async message handler. TypeScript uses Node.js 20 or later; Python uses pip install httpx. Supply your existing reply and reaction-delivery functions, the original message ID, and the reaction options your platform supports. Use a stable eventId / event_id of 1–128 printable ASCII characters without spaces, unique per message and agent, and keep the input unchanged on retries.
Use the message ID captured when you start the request to attach the result to the correct message. Cancel or discard reactions when a message is deleted, the conversation resets, or the result arrives too late for your interface. Keep your Emote key on your server.