Skip to main content
Like AOF? Give us a star!
If you find AOF useful, please star us on GitHub. It helps us reach more developers and grow the community.

Discord Integration Reference

Complete reference for Discord Interactions API integration in AOF.

Configuration

Trigger Specification

apiVersion: aof.dev/v1
kind: Trigger
metadata:
name: discord-ops
labels:
platform: discord
spec:
type: Discord
config:
# Bot credentials (required)
bot_token: ${DISCORD_BOT_TOKEN}
application_id: ${DISCORD_APPLICATION_ID}
public_key: ${DISCORD_PUBLIC_KEY}

# Guild restrictions (optional)
guild_ids:
- "123456789012345678"

# Role restrictions (optional)
allowed_roles:
- "987654321098765432" # Admin role
- "876543210987654321" # DevOps role

# Command definitions
commands:
/status:
agent: devops
description: "Check system status"

/deploy:
agent: deployer
description: "Deploy application"

/diagnose:
fleet: rca-fleet
description: "Root cause analysis"

# Default for unmatched commands
default_agent: devops

Environment Variables

VariableRequiredDescription
DISCORD_BOT_TOKENYesBot token from Developer Portal
DISCORD_APPLICATION_IDYesApplication ID
DISCORD_PUBLIC_KEYYesEd25519 public key for verification

Daemon Configuration

# daemon.yaml
platforms:
discord:
enabled: true
webhook_path: /webhook/discord

# Bot credentials
bot_token: ${DISCORD_BOT_TOKEN}
application_id: ${DISCORD_APPLICATION_ID}
public_key: ${DISCORD_PUBLIC_KEY}

# Optional restrictions
guild_ids:
- ${DISCORD_GUILD_ID}

Interaction Types

Slash Commands (Type 2)

{
"id": "interaction-id",
"type": 2,
"application_id": "app-id",
"token": "interaction-token",
"data": {
"id": "command-id",
"name": "agent",
"options": [
{ "name": "action", "value": "run" },
{ "name": "agent_id", "value": "k8s-ops" }
]
},
"guild_id": "guild-id",
"channel_id": "channel-id",
"member": {
"user": {
"id": "user-id",
"username": "developer",
"discriminator": "0001"
},
"roles": ["role-id-1", "role-id-2"]
}
}

Message Components (Type 3)

{
"id": "interaction-id",
"type": 3,
"token": "interaction-token",
"data": {
"custom_id": "approve_deployment",
"component_type": 2
},
"message": {
"id": "original-message-id",
"content": "..."
},
"member": {
"user": { "id": "user-id", "username": "admin" }
}
}
{
"id": "interaction-id",
"type": 5,
"token": "interaction-token",
"data": {
"custom_id": "feedback_modal",
"components": [
{
"type": 1,
"components": [
{
"type": 4,
"custom_id": "feedback_text",
"value": "User's input text"
}
]
}
]
}
}

Slash Commands

Command Structure

{
"name": "agent",
"description": "Manage AOF agents",
"options": [
{
"type": 3,
"name": "action",
"description": "Action to perform",
"required": true,
"choices": [
{ "name": "run", "value": "run" },
{ "name": "status", "value": "status" },
{ "name": "stop", "value": "stop" }
]
},
{
"type": 3,
"name": "agent_id",
"description": "Agent ID",
"required": true
}
]
}

Option Types

TypeNameDescription
1SUB_COMMANDSubcommand
2SUB_COMMAND_GROUPSubcommand group
3STRINGString input
4INTEGERInteger input
5BOOLEANBoolean input
6USERUser mention
7CHANNELChannel selection
8ROLERole selection
10NUMBERDecimal number
11ATTACHMENTFile attachment

Built-in Commands

AOF registers these commands automatically:

CommandOptionsDescription
/agentaction, agent_idManage agents
/taskaction, descriptionManage tasks
/fleetactionManage fleets
/flowworkflowExecute workflows

Registering Commands

# Register commands globally
aofctl discord register-commands

# Register to specific guild (faster updates)
aofctl discord register-commands --guild-id 123456789012345678

Response Formatting

Basic Embed

{
"type": 4,
"data": {
"embeds": [
{
"title": "📊 System Status",
"description": "All systems operational",
"color": 5763719,
"fields": [
{ "name": "Nodes", "value": "✅ 3/3 Ready", "inline": true },
{ "name": "Pods", "value": "✅ 45/45 Running", "inline": true },
{ "name": "Services", "value": "✅ 12/12 Active", "inline": true }
],
"footer": { "text": "Last updated" },
"timestamp": "2024-01-15T10:30:00.000Z"
}
]
}
}

With Components

{
"type": 4,
"data": {
"embeds": [{ "title": "Status", "description": "..." }],
"components": [
{
"type": 1,
"components": [
{
"type": 2,
"style": 1,
"label": "Refresh",
"custom_id": "refresh_status"
},
{
"type": 2,
"style": 3,
"label": "View Logs",
"custom_id": "view_logs"
},
{
"type": 2,
"style": 4,
"label": "Restart",
"custom_id": "restart_service"
}
]
}
]
}
}

Component Types

TypeNameDescription
1Action RowContainer for components
2ButtonClickable button
3String SelectDropdown menu
4Text InputText input (modals only)
5User SelectUser selection menu
6Role SelectRole selection menu
7Mentionable SelectUser/role selection
8Channel SelectChannel selection

Button Styles

StyleNameColorUse Case
1PrimaryBlueMain actions
2SecondaryGraySecondary actions
3SuccessGreenConfirmations
4DangerRedDestructive actions
5LinkGrayExternal links
{
"type": 9,
"data": {
"custom_id": "feedback_modal",
"title": "Provide Feedback",
"components": [
{
"type": 1,
"components": [
{
"type": 4,
"custom_id": "feedback_text",
"label": "Your Feedback",
"style": 2,
"placeholder": "Enter feedback...",
"required": true,
"max_length": 1000
}
]
}
]
}
}

Signature Verification

Ed25519 Verification

Discord uses Ed25519 for signature verification:

// Headers required
X-Signature-Ed25519: <hex_signature>
X-Signature-Timestamp: <timestamp>

// Message to verify
message = timestamp + request_body

// Verification
ed25519::verify(public_key, message, signature)

Verification Implementation

use ed25519_dalek::{Signature, Verifier, VerifyingKey};

fn verify_signature(
public_key: &VerifyingKey,
timestamp: &str,
body: &[u8],
signature: &[u8; 64]
) -> bool {
let message = format!("{}{}", timestamp, String::from_utf8_lossy(body));
let sig = Signature::from_bytes(signature);
public_key.verify(message.as_bytes(), &sig).is_ok()
}

API Endpoints

Respond to Interaction

POST https://discord.com/api/v10/interactions/{interaction_id}/{interaction_token}/callback

{
"type": 4,
"data": {
"content": "Response text",
"embeds": [...],
"components": [...]
}
}

Response Types

TypeNameDescription
1PONGACK ping
4CHANNEL_MESSAGEReply with message
5DEFERRED_CHANNEL_MESSAGEACK, send later
6DEFERRED_UPDATE_MESSAGEACK component, update later
7UPDATE_MESSAGEUpdate original message
9MODALShow modal dialog

Edit Original Response

PATCH https://discord.com/api/v10/webhooks/{application_id}/{interaction_token}/messages/@original

{
"content": "Updated content",
"embeds": [...]
}

Send Follow-up Message

POST https://discord.com/api/v10/webhooks/{application_id}/{interaction_token}

{
"content": "Follow-up message"
}

Register Global Commands

PUT https://discord.com/api/v10/applications/{application_id}/commands

[
{ "name": "agent", "description": "...", "options": [...] },
{ "name": "task", "description": "...", "options": [...] }
]

Register Guild Commands

PUT https://discord.com/api/v10/applications/{application_id}/guilds/{guild_id}/commands

[...]

Rate Limits

EndpointLimitWindow
Interaction response3 secondsMust respond
Deferred response15 minutesFollow-up window
Global commands200/dayPer application
Guild commands200/dayPer guild
Message send5/5sPer channel

Error Handling

Common Errors

CodeDescriptionSolution
10003Unknown channelVerify channel ID
10008Unknown messageMessage deleted
10062Unknown interactionTimeout expired
40001UnauthorizedCheck bot token
40060Interaction acknowledgedAlready responded
50001Missing accessCheck permissions
50035Invalid form bodyCheck request format

Error Response Format

{
"code": 50035,
"message": "Invalid Form Body",
"errors": {
"data": {
"embeds": {
"0": {
"title": {
"_errors": [
{ "code": "BASE_TYPE_MAX_LENGTH", "message": "..." }
]
}
}
}
}
}
}

Limits

ResourceLimit
Embed title256 characters
Embed description4096 characters
Embed fields25 fields
Field name256 characters
Field value1024 characters
Total embed6000 characters
Components per row5
Action rows5
Select options25
Button label80 characters

Security Best Practices

  1. Always verify signatures - Never skip Ed25519 verification
  2. Validate user roles - Check allowed_roles before execution
  3. Use ephemeral messages - For sensitive information
  4. Rate limit commands - Prevent abuse
  5. Audit logging - Log all command invocations
  6. Secure tokens - Never expose in logs or responses

Testing

Discord Developer Portal

Test in the Developer Portal:

  1. Go to your application
  2. Navigate to "Bot" section
  3. Use "Interactions Endpoint URL" testing

Local Development

# Use ngrok for local testing
ngrok http 8080

# Update Interactions Endpoint URL
# https://xxx.ngrok.io/webhook/discord

Interaction Verification Test

# Test PING verification
curl -X POST http://localhost:8080/webhook/discord \
-H "Content-Type: application/json" \
-H "X-Signature-Ed25519: <signature>" \
-H "X-Signature-Timestamp: <timestamp>" \
-d '{"type": 1}'

Troubleshooting

Bot Not Responding

  1. Check webhook URL is correct
  2. Verify signature verification passes
  3. Confirm bot has required permissions
  4. Check AOF daemon logs

Commands Not Appearing

  1. Wait 1 hour for global commands
  2. Use guild commands for instant updates
  3. Check command registration succeeded
  4. Verify bot is in the guild

Signature Verification Failing

  1. Verify public key is correct
  2. Check timestamp header is present
  3. Ensure body hasn't been modified
  4. Verify hex decoding is correct

See Also