Discord triggers
Start an agent from signed Discord interactions and Webhook Events.
#Start your agent from Discord
A Discord connection receives signed HTTP requests from your Discord application and starts an agent running in Studio or in your own terminal. Choose the protocol that matches the setting you are configuring in Discord:
Ordinary server messages and Gateway events use Discord's WebSocket transport. They are separate from these signed HTTP connections.
#Try a synthetic connection first
You can test the entire local path without a Discord account or a paid service. In a platform source checkout, open a terminal in tooling/mock-gateway:
uv run --frozen maivn-mock-gateway replay-discord --print-setupThis prints a synthetic application ID and public key. That key is public test material: use it for a separate local fixture connection. A real Discord app must use the public key Discord assigned to that app.
In Studio's trigger setup, choose Add a connection. Or open Integrations → Connections in the Developer Portal and add a connection there. Choose Discord signed HTTP events, select Interactions, and paste the synthetic application ID and public key. Save the connection ID and callback address.
Without either UI, use the signed-in connection API described in Terminal connection setup. Send this JSON to POST /v1/projects/{project_id}/connections, replacing the application and key values with the command's output:
{
"display_name": "Local Discord replay",
"setup_kind": "discord_webhook",
"discord_protocol": "interactions",
"discord_application_id": "123456789012345678",
"discord_public_key": "03a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8"
}Ed25519 uses a public verification key. This connection does not ask you for a bot token or return a signing secret.
#Declare and start the agent
Add a declaration to the module containing your configured Agent. Replace the connection ID and target with values from your project:
discord_trigger = (
agent.on_connection("conn_your_discord", event_type="discord.interaction.command")
.key("discord-command")
.named("Discord command")
.target("your-agent-id", version=1)
.asking("Handle this Discord command: {{event.payload.data.name}}")
.display_in_app()
)In Studio, reload the app and follow the trigger's checklist to start serving and turn it on. Without Studio, start maivn serve with the same project, agent ID and version. Wait for Announced, then call discord_trigger.invoke() once from a registration script in another terminal. Keep the serving terminal open. Do not register on every import of the module.
#Send a local command
From tooling/mock-gateway, substitute the connection ID you just created:
uv run --frozen maivn-mock-gateway replay-discord --connection-id conn_your_discord --kind interaction-ping
uv run --frozen maivn-mock-gateway replay-discord --connection-id conn_your_discord --kind command --delivery-id 123456789012345682 --command-name triage --repeat 2The PING checks the connection's signed handshake. The command sends the same signed fixture twice. Both receipts should report protocol_acknowledged: true. Then open the trigger's run history and verify one completed run. An accepted callback alone does not prove the agent ran.
Keep the same delivery ID when retrying a command. Use a new ID to represent a new command. The replay tool contacts loopback only and does not change your connection, registration or Discord settings.
For Webhook Events, print the appropriate setup and create a separate connection with protocol webhook_events:
uv run --frozen maivn-mock-gateway replay-discord --print-setup --kind webhook-event
uv run --frozen maivn-mock-gateway replay-discord --connection-id conn_your_webhook_events --kind webhook-ping
uv run --frozen maivn-mock-gateway replay-discord --connection-id conn_your_webhook_events --kind webhook-event --event-type ENTITLEMENT_CREATE --delivery-id 123456789012345683 --repeat 2Declare its trigger with event_type="discord.webhook.entitlement_create". Webhook identity includes the event type and event time as well as its data. Keep these unchanged when replaying the same event.
#Connect a real Discord application
In the Discord Developer Portal, select your application and copy Application ID and Public Key from General Information. Create a new mAIvn connection with those values and the intended protocol. Local synthetic values cannot verify requests from this application.
For local testing, run the free receiver-only tunnel with that connection:
maivn dev-tunnel --connection-id conn_your_real_discordCopy its HTTPS callback into the matching Discord setting from the table above. Keep the tunnel and agent running. A new temporary tunnel has a new address, so update Discord when you restart it. See Local webhook tunnels for installation and operation.
For Interactions, register the commands your Discord application should offer and invoke one in a context where it is installed. mAIvn acknowledges supported commands, components and modal submissions with an ephemeral receipt after durable admission. The agent's result appears in mAIvn, or at the trigger's separately configured output destination. Autocomplete requests receive an empty choice list. This connection does not generate autocomplete suggestions.
For Webhook Events, enable events, select the types you need, and save. Use the matching lower-case discord.webhook. event name in your declaration. For example, Discord's ENTITLEMENT_UPDATE maps to discord.webhook.entitlement_update.
Discord verifies the callback with a signed PING and checks that invalid signatures are rejected. A correct public key, matching application ID and the correct protocol are all required. HTTP interactions and Webhook Events use different PING and acknowledgement shapes. Switching only the trigger name does not switch a connection's protocol.
#Send the result to a Discord channel
Create a webhook for the destination channel in Discord, then add a Discord output destination connection in Studio or the Developer Portal. Paste its webhook URL there. The saved connection keeps the URL private.
For terminal setup, use the same connection API with setup_kind: "discord_output", a display_name, and endpoint_url set to your Discord webhook URL. Keep that URL out of source control and terminal history.
Replace .display_in_app() in the declaration with:
discord_trigger = discord_trigger.to_connection("conn_your_discord_output")Reload the app and apply the updated trigger in Studio, or invoke the declaration from your registration script. The incoming connection verifies the event. The output connection selects the channel receiving the completed result. The initial Discord interaction receipt remains a brief acknowledgement.
#Troubleshooting
Discord signature timestamps may be up to 660 seconds old, covering its documented ten-minute webhook retry period plus clock skew. Future timestamps are limited to 60 seconds. Event identities remain deduplicated beyond that signature window. Other providers retain their existing signature rules.
Provider details: Discord Webhook Events and receiving interactions.