Back to How To
How To

How to run a Discord bot in a restricted network

Receive Discord Interactions and send responses through Adal when direct access to Discord endpoints is restricted by your network policy.

30 min

If your system administrator has restricted direct access to Discord endpoints from a corporate or server network, you do not necessarily need to run the integration on an unmanaged computer or request unrestricted access to the entire platform.

For bots based on HTTP Interactions, Adal can provide an approved intermediate delivery route. Discord sends events to a public HTTPS server in Adal, Adal CLI delivers them to the local application, and the bot sends its responses through a separate Adal server configured with a Direct HTTP Destination.

The local application does not need to connect directly to Discord endpoints. It establishes outbound connections only to the Adal infrastructure approved by the network administrator.

This setup should be used with the knowledge and approval of the network owner. Adal does not conceal or disguise traffic. It provides an explicit, manageable integration route with request history and delivery diagnostics.

What you will have at the end

Incoming events will follow this route:

Discord user ↓ Discord Interactions ↓ HTTPS POST Inbound Adal server ↓ Adal CLI ↓ Local bot application

Responses from the bot will travel through a separate route:

Local application ↓ HTTPS POST Outbound Adal server ↓ Direct HTTP Discord Interaction Callback ↓ Discord user

With this setup, the application can:

  • receive slash commands and other Discord Interactions;

  • verify the signature of every incoming request;

  • send initial Interaction responses;

  • send follow-up messages;

  • inspect request and delivery history in Adal;

  • operate without connecting the local process directly to Discord endpoints.

Which Discord bots this setup supports

Discord applications can receive Interactions in two ways:

  • through the Gateway;

  • through a public HTTP Interaction Endpoint.

This guide uses the second option. It is suitable for:

  • slash commands;

  • button clicks;

  • select menu actions;

  • modal submissions;

  • other components that create a Discord Interaction.

Adal does not replace the Discord Gateway in this setup. If your bot needs a persistent Gateway WebSocket connection to receive events such as MESSAGE_CREATE, member updates, or presence events, you will need a separate network architecture approved by your administrator.

What you need

Before you begin, make sure you have:

  • an application in the Discord Developer Portal;

  • the Discord Application ID;

  • the application Public Key;

  • an Adal account;

  • Adal CLI installed;

  • a local application with an HTTP handler;

  • permission to establish outbound HTTPS and WSS connections to Adal.

For the examples below, assume that the local application is running at:

http://127.0.0.1:3000

Discord Interactions will be handled at:

http://127.0.0.1:3000/discord/interactions

Step 1. Prepare the Discord application

Open the Discord Developer Portal and select an existing application or create a new one.

Save the following values:

  • Application ID — the application identifier;

  • Public Key — the public key used to verify request signatures;

  • if necessary, the Bot Token used for Discord API operations.

Do not commit secrets or tokens to Git, include them in frontend code, or send them in unprotected messages.

Save the application Public Key in an environment variable:

export DISCORD_PUBLIC_KEY='public-key-from-developer-portal'

You will also need the Application ID when constructing follow-up response URLs:

export DISCORD_APPLICATION_ID='your-application-id'

Step 2. Run a local Interaction handler

The local application must accept POST requests at:

/discord/interactions

Each request from Discord contains these headers:

X-Signature-Ed25519 X-Signature-Timestamp

The application must verify the signature against the original, unmodified request body. Do not parse the JSON, serialize it again, and verify the resulting string: the signature is calculated from the original request bytes.

After verifying the signature, the application can inspect the type field.

When validating the Interaction Endpoint, Discord sends a PING request. The application must produce a PONG response:

{ "type": 1 }

An initial response to a command can look like this:

{ "type": 4, "data": { "content": "Command received" } }

The local handler must reject requests with an invalid signature, even if they were delivered through a known Adal server.

Step 3. Create an inbound server in Adal

Open the Adal Dashboard and create a server for receiving Discord Interactions.

Recommended settings:

  • Name: discord-interactions-in;

  • Allowed methods: POST;

  • Deliver pending requests on connect: enabled for development;

  • Notes: Discord HTTP Interactions.

After creation, the server receives a permanent public URL:

https://your-inbound-server.se1.adal.cloud

Save the issued CLI token. It is shown only once. If the token is lost or exposed, generate a new one.

Step 4. Add a local Destination

Create a Destination for the inbound server with these settings:

  • Delivery method: Adal CLI;

  • URL: http://127.0.0.1:3000;

  • Name: discord-local-bot.

Adal preserves the path of the original request. A request received at:

https://your-inbound-server.se1.adal.cloud/discord/interactions

will therefore be delivered to:

http://127.0.0.1:3000/discord/interactions

Start Adal CLI with the token of the inbound server:

adalcli --token <your-cli-token>

After a successful connection, the Destination should appear as online.

The CLI establishes an outbound encrypted connection to Adal. You do not need to open an inbound port on the workstation or make the local application publicly accessible.

Step 5. Configure the Interaction Endpoint in Discord

Set the following Interaction Endpoint URL in the Discord application settings:

https://your-inbound-server.se1.adal.cloud/discord/interactions

Discord sends a validation PING and expects a correctly signed and timely response.

The initial response time is especially important for Discord. An Interaction must be acknowledged within Discord’s short response window, typically about three seconds. Before using this architecture in production, test the total latency of the complete route:

Discord → Adal → Adal CLI → application → Adal → Discord

If processing a command takes longer, send a deferred response first and perform the main work after acknowledging the Interaction.

Step 6. Create a server for sending responses

The local computer can now receive events through Adal CLI. In a restricted network, however, it may still be unable to connect directly to the Discord Interaction Callback endpoint.

Create a second Adal server for outgoing requests:

  • Name: discord-interactions-out;

  • Allowed methods: POST, and optionally PATCH and DELETE;

  • Notes: Discord interaction responses.

Add a Destination to this server:

  • Delivery method: Direct HTTP;

  • URL: https://discord.com;

  • Name: discord-api.

Keep this server separate from the inbound server. Otherwise, requests received from Discord could be sent both to the local application and back toward Discord.

Step 7. Send the initial response

Every Discord Interaction contains:

  • id — the Interaction identifier;

  • token — the temporary response token.

The local application constructs the callback path:

/api/v10/interactions/{interaction.id}/{interaction.token}/callback

Instead of sending the request directly to discord.com, it sends it to the outbound Adal server:

https://your-outbound-server.se1.adal.cloud/api/v10/interactions/{interaction.id}/{interaction.token}/callback

Example request body:

{ "type": 4, "data": { "content": "The bot is running and received your command" } }

Adal accepts the request and delivers it through the Direct HTTP Destination. Because the Destination URL is:

https://discord.com

the final delivery URL becomes:

https://discord.com/api/v10/interactions/{interaction.id}/{interaction.token}/callback

The original request path and query parameters are preserved.

Step 8. Send a follow-up message

After the initial response, the bot can send follow-up messages using the Interaction webhook.

The Discord path has this format:

/api/v10/webhooks/{application.id}/{interaction.token}

Through the outbound Adal server, the application sends the request to:

https://your-outbound-server.se1.adal.cloud/api/v10/webhooks/{application.id}/{interaction.token}

Example request body:

{ "content": "The task has been completed" }

Adal delivers the request to the corresponding Discord URL through Direct HTTP.

The Interaction token is sensitive and temporarily authorizes responses to a specific Interaction. Do not include complete callback URLs in public logs or messages.

Why two Adal servers are used

The inbound and outbound routes serve different purposes.

The inbound server handles:

Discord → Adal → Adal CLI → localhost

The outbound server handles:

localhost → Adal → Direct HTTP → Discord

Keeping them separate allows you to:

  • restrict HTTP methods independently;

  • inspect incoming and outgoing histories separately;

  • avoid mixing Discord Interactions with callback requests;

  • use different tokens and access rules;

  • identify the failing delivery stage more quickly.

For production, consider using separate Adal servers for different Discord applications and environments.

Working in a restricted network

If Discord endpoints are restricted by organizational policy, coordinate the use of Adal with the network administrator first.

In a typical configuration, the administrator does not need to provide the developer’s computer with unrestricted access to Discord. Instead, they can permit the local application and Adal CLI to establish the required outbound connections to approved Adal domains.

The connection to Discord is then made from the selected Adal region through the Direct HTTP Destination.

This creates an explicit and observable route:

Corporate network → Adal → Discord

The Adal Dashboard provides:

  • accepted request history;

  • delivery results;

  • HTTP status codes;

  • attempt counts;

  • processing times;

  • connection errors.

This architecture is useful when an administrator needs to preserve the organization’s network restrictions while approving a specific server-side integration.

Security

Always verify Discord signatures

The public URL of the inbound server may receive requests from sources other than Discord. The reliable way to authenticate an Interaction is to verify its Ed25519 signature using the application Public Key.

A request with a missing or invalid signature must receive 401 Unauthorized.

Do not expose Interaction tokens

The token appears in the callback URL and authorizes responses to a particular Interaction. Do not write the complete URL to:

  • public logs;

  • user-facing error messages;

  • analytics systems;

  • screenshots or documentation.

Restrict allowed HTTP methods

The inbound server normally requires only POST.

Allow only the methods actually used by the application on the outbound server. A narrower configuration is easier to review and control.

Separate environments

Create separate Adal servers for development and production:

discord-dev-in discord-dev-out discord-prod-in discord-prod-out

This prevents a development bot from accidentally using production delivery routes.

Account for repeated delivery

A webhook can be delivered more than once. The application should store the Interaction ID and avoid performing the same irreversible action repeatedly.

This is particularly important for commands that:

  • create records;

  • send notifications;

  • start builds;

  • change permissions;

  • perform payment or administrative operations.

Common issues

Discord does not accept the Interaction Endpoint

Check that:

  1. The URL uses HTTPS.

  2. Adal CLI is connected.

  3. The local Destination is online.

  4. The application handles PING.

  5. The signature is verified against the original request body.

  6. The response reaches Discord before its deadline.

The Adal server history will show whether the validation request was received and delivered to the local application.

The Interaction arrives, but the user sees an error

The initial response probably did not reach Discord in time.

Do not perform long-running business logic before acknowledging the Interaction. Send an immediate or deferred response first, then continue processing.

Also inspect the outbound Adal server history and the HTTP status returned by Discord.

The incoming request receives 401 Unauthorized

Check:

  • the value of DISCORD_PUBLIC_KEY;

  • the X-Signature-Ed25519 and X-Signature-Timestamp headers;

  • whether verification uses the original request body;

  • whether middleware modifies the body before signature verification.

The outbound server accepts the request, but no message appears in Discord

Open the Direct HTTP delivery history and inspect the Discord response.

Possible causes include:

  • an incorrect interaction.id;

  • an expired or invalid interaction.token;

  • an incorrect Application ID;

  • an initial callback sent too late;

  • an unsupported Interaction Response type;

  • a path that does not match the Discord API.

The bot stops receiving commands after a computer restart

The public Adal server URLs remain unchanged, but the local application and Adal CLI must be started again.

Check the complete local chain:

local application → Adal CLI → Destination status

If delivery of pending requests is enabled, queued events may be delivered after the connection is restored.

Limitations

This approach is intended for Discord HTTP Interactions and webhook requests.

It does not provide the local application with general-purpose Discord access and does not replace:

  • the Discord Gateway;

  • voice connections;

  • arbitrary Discord resource downloads;

  • a general HTTP proxy;

  • the Discord desktop or web application.

If the bot requires Gateway events, coordinate a separate network architecture with the administrator.

You should also test the latency of the initial response carefully. Discord imposes a strict deadline for acknowledging an Interaction, while routing through two Adal servers adds extra network hops.

Summary

If direct access to Discord endpoints is restricted by network policy, an HTTP-based bot does not necessarily need to be moved to an unmanaged computer, and the network does not need to be opened to all Discord traffic.

Instead, you can establish an approved architecture through Adal:

  1. Discord sends Interactions to a public inbound Adal server.

  2. Adal CLI delivers them to the local application.

  3. The application verifies the signature and prepares a response.

  4. A separate outbound Adal server delivers the callback to Discord through Direct HTTP.

  5. The complete incoming and outgoing delivery history remains available for diagnostics and oversight.

The local application stays inside the restricted network, accepts no direct connections from the internet, and does not connect directly to Discord endpoints. The organization’s network policy remains in place while the integration receives a clear, limited, and observable delivery route.