Skip to content

Ephemeral Messages

Ephemeral messages are visible only to one user and the bot. In Bot API 10.3, every send method groups the delivery fields inside ephemeral_message_parameters.

Bot API 10.3 migration

Top-level receiver_user_id and callback_query_id are no longer accepted by send methods. GramIO intentionally provides no compatibility alias: move both fields into ephemeral_message_parameters.

Send to one user

ts
import { 
Bot
} from "gramio";
const
bot
= new
Bot
(
process
.
env
.
BOT_TOKEN
as string);
bot
.
command
("secret", (
ctx
) =>
ctx
.
send
("🤫 Only you can see this.", {
ephemeral_message_parameters
: {
receiver_user_id
:
ctx
.
from
!.
id
,
}, }), );

The same nested object works with supported media methods and sendRichMessage.

Respond to or replace a callback message

Pass the callback query identifier when the send follows a button press. Set replace_callback_query_message to replace the original callback-query message with the ephemeral result.

ts
import { 
Bot
,
InlineKeyboard
} from "gramio";
const
bot
= new
Bot
(
process
.
env
.
BOT_TOKEN
as string);
bot
.
command
("menu", (
ctx
) =>
ctx
.
send
("Pick one:", {
reply_markup
: new
InlineKeyboard
().
text
("Reveal", "reveal"),
}), );
bot
.
callbackQuery
("reveal", async (
ctx
) => {
await
ctx
.
answer
();
return
ctx
.
send
("Your personal result", {
ephemeral_message_parameters
: {
receiver_user_id
:
ctx
.
from
.
id
,
callback_query_id
:
ctx
.
id
,
replace_callback_query_message
: true,
}, }); });

Do not set replace_callback_query_message for callbacks originating from an ephemeral message. Edit those with the dedicated editEphemeralMessage… methods.

Read the response metadata

The returned Message and incoming message context expose the receiver and the ephemeral identifier:

ts
import { 
Bot
} from "gramio";
const
bot
= new
Bot
(
process
.
env
.
BOT_TOKEN
as string);
bot
.
command
("private", async (
ctx
) => {
const
sent
= await
ctx
.
send
("Private", {
ephemeral_message_parameters
: {
receiver_user_id
:
ctx
.
from
!.
id
,
}, });
console
.
log
(
sent
.
receiverUser
?.
id
,
sent
.
ephemeralMessageId
);
});
  • receiverUser is the user who can see the message.
  • ephemeralMessageId addresses the message for later edits or deletion.

Edit and delete

Edit/delete methods still use receiver_user_id and ephemeral_message_id as their own top-level address fields. The nesting change applies to send methods.

ts
import { 
Bot
,
bold
,
format
} from "gramio";
const
bot
= new
Bot
(
process
.
env
.
BOT_TOKEN
as string);
bot
.
command
("countdown", async (
ctx
) => {
const
receiverId
=
ctx
.
from
!.
id
;
const
sent
= await
ctx
.
send
("3…", {
ephemeral_message_parameters
: {
receiver_user_id
:
receiverId
},
}); await
bot
.
api
.
editEphemeralMessageText
({
chat_id
:
ctx
.
chatId
,
receiver_user_id
:
receiverId
,
ephemeral_message_id
:
sent
.
ephemeralMessageId
!,
text
:
format
`Now ${
bold
`2…`}`,
}); await
bot
.
api
.
deleteEphemeralMessage
({
chat_id
:
ctx
.
chatId
,
receiver_user_id
:
receiverId
,
ephemeral_message_id
:
sent
.
ephemeralMessageId
!,
}); });

Keep the receiver ID together with the ephemeral message ID. A normal message_id cannot address an ephemeral message.

Reply to an ephemeral message

Use reply_parameters.ephemeral_message_id and make the reply ephemeral for the same receiver:

ts
import { 
Bot
} from "gramio";
const
bot
= new
Bot
(
process
.
env
.
BOT_TOKEN
as string);
bot
.
on
("message", (
ctx
) => {
if (!
ctx
.
ephemeralMessageId
|| !
ctx
.
receiverUser
) return;
return
ctx
.
send
("A private reply", {
ephemeral_message_parameters
: {
receiver_user_id
:
ctx
.
receiverUser
.
id
,
},
reply_parameters
: {
ephemeral_message_id
:
ctx
.
ephemeralMessageId
,
}, }); });

See also