Discord Interactions
Base path: /discord
POST /discord/interactions
Receive and process Discord interaction webhooks (slash commands, ping challenges).
Auth: Ed25519 signature verification via the DISCORD_INTERACTIONS_PUBLIC_KEY environment variable. If no public key is configured, signature verification is skipped.
Tenant: Resolved automatically from the interaction's guild_id or the Discord user's registered tenant.
Headers
| Header | Type | Required | Description |
|---|---|---|---|
| x-signature-ed25519 | string | Yes* | Discord request signature (*required when public key is configured) |
| x-signature-timestamp | string | Yes* | Discord request timestamp (*required when public key is configured) |
Request Body
Discord interaction payload (JSON). The endpoint validates the body against the following schema:
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | No | Interaction ID |
| type | number | Yes | Interaction type (1 = Ping, 2 = Application Command, 3 = Message Component, 5 = Modal Submit) |
| guild_id | string | No | Discord guild/server ID |
| channel_id | string | No | Channel where the command was invoked (required for /notify channel target) |
| member | object | No | Guild member object containing user.id and roles |
| user | object | No | DM user object containing id |
| data | object | No | Command data: name and options (type 2), custom_id and values (type 3), custom_id and components (type 5) |
Behavior
Ping Challenge (type 1)
Returns a ping acknowledgement so Discord can verify the endpoint.
{ "type": 1 }
Application Command (type 2)
- Deduplication: Acquires a cache lock on
discord:interaction:lock:{id}(60 s TTL). If the lock is already held, responds with an ephemeral "already being processed" message. - Tenant resolution: Resolves the tenant from the guild or the Discord user's registration. If unresolved, responds with an ephemeral message prompting the user to run
/registerin a server. - User registration: Registers the Discord user for the resolved tenant if a
guild_idis present. - Webhook event logging: Persists the interaction to the
WebhookEventcollection. DuplicateeventIdvalues are silently ignored with an ephemeral acknowledgement. - Command routing: Dispatches to the unified command router. Type 2 (slash commands) uses
execute; type 3 (buttons/selects) usesexecuteComponent; type 5 (modal submit) usesexecuteModal. Results may be ephemeral messages (content, embeds, components) or a modal (type 9). The/notifycommand requiresDISCORD_BOT_TOKENfor DM/channel delivery.
Response (200)
Responses use Discord's interaction response format. Type 4 = channel message (content, embeds, components); type 9 = modal.
Message response (type 4):
{
"type": 4,
"data": {
"content": "Command result message",
"embeds": [],
"components": [],
"flags": 64
}
}
flags: 64 means ephemeral (visible only to the invoking user).
BTC_NOWPAYMENTS order submit response (embed):
When a customer submits an order with BTC_NOWPAYMENTS as the payment method and the NOWPAYMENTS_API_KEY is configured, the bot posts an ephemeral embed (type 4, flags 64) containing a NOWPayments invoice. The embed includes:
- BTC deposit address (code-block formatted)
- Expected BTC amount
- Invoice expiry time in minutes
- A
LINKbutton (Discord style 5) pointing to the hosted NOWPayments invoice page
Errors
| Status | Condition |
|---|---|
| 400 | Validation error or command execution failure |
| 401 | Invalid Discord signature |