Responses
Commands respond to interactions in several ways. The framework handles all the low-level Discord protocol details — you just return what you want to say.
Plain string
The simplest response is a plain string:
The framework automatically wraps this in a MessageResponse and sends it back to Discord.
MessageResponse
For more control, return a MessageResponse explicitly:
from fastapi_interactions.responses import MessageResponse
@router.command(name="hello", description="Say hello")
async def hello(ctx):
return MessageResponse(f"Hello, {ctx.user.username}!")
Ephemeral messages
Ephemeral messages are only visible to the user who invoked the command:
@router.command(name="secret", description="A secret message")
async def secret(ctx):
return MessageResponse("Only you can see this.", ephemeral=True)
DeferResponse
Use DeferResponse when your command needs to do work that takes longer than Discord's 3-second response window. The framework acknowledges the interaction immediately with a "thinking..." indicator, then runs your work in the background.
How it works
- Your command returns
DeferResponse - The framework sends the defer acknowledgement to Discord within 3 seconds
- Discord shows "thinking..." to the user
- Your
finishfunction runs in the background - Once the work is done, call
ctx.send()to deliver the actual response
Example
import asyncio
from fastapi_interactions.responses import DeferResponse
@router.command(name="slow", description="A slow command")
async def slow(ctx):
async def finish():
# Do slow work here — fetching data, calling APIs, etc.
await asyncio.sleep(10) # simulate slow operation
await ctx.send("Done! Here's your result.")
return DeferResponse(finish=finish)
When a user invokes /slow:
1. They immediately see "thinking..." (the defer)
2. Your command's finish function runs in the background
3. After 10 seconds, they see "Done! Here's your result."
Warning
Long-running operations don't work with deferred responses on serverless because the container terminates after the HTTP response is sent. If a command needs more than 3 seconds, either complete the work within the response window, or use a traditional hosting setup. Most Discord bots don't need this pattern.
Ephemeral defers
Defer ephemeral messages the same way:
Ephemeral state is locked
The ephemeral flag is set at defer time. A later ctx.send(..., ephemeral=False) will still produce an ephemeral message if you deferred with ephemeral=True.
Multiple followups
You can call ctx.send() multiple times in a finish function:
async def finish():
await asyncio.sleep(5)
await ctx.send("First message.")
await asyncio.sleep(5)
await ctx.send("Second message.")
await ctx.send("Third message.")
Each call sends a separate message.
Accessing context in finish
The finish function is a closure that has access to ctx:
@router.command(name="personalized", description="Personalized message")
async def personalized(ctx):
async def finish():
user = ctx.user.username
await ctx.send(f"This is personalized for {user}!")
return DeferResponse(finish=finish)
This works because ctx is captured from the enclosing scope.
Other response types
| Response type | When to use |
|---|---|
MessageResponse |
Reply immediately with a message |
DeferResponse |
Acknowledge while doing async work |
PongResponse |
Respond to a PING (automatic, you don't need to use this) |
Other response types will be implemented along the way.
Full API documentation is available in the API reference.