---
title: "Discord"
description: "The verified plugin ai.oz0/discord - Discord's /oz0 starts your agents, and answers, questions and approvals come back with buttons."
canonical_url: "https://docs.oz0.ai/build/plugins/verified/discord"
---
# Discord

> The verified plugin ai.oz0/discord - Discord's /oz0 starts your agents, and answers, questions and approvals come back with buttons.

For: tenant administrators and plugin users.

`ai.oz0/discord` connects a [chat channel](https://docs.oz0.ai/build/apps/chat-channels) through Discord's interactions endpoint. Send `/oz0 prompt:<request> [agent:<name>]` to start an agent [bound to the channel](https://docs.oz0.ai/build/apps/chat-channels#bind-a-channel-to-agents).

The answer includes a Reply button for continuing the conversation. Questions and approvals arrive as messages with buttons.

<table>
<thead>
  <tr>
    <th>
      
    </th>
    
    <th>
      
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Manifest name
    </td>
    
    <td>
      <code>
        discord
      </code>
      
       (hub name <code>
        ai.oz0/discord
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      Tool
    </td>
    
    <td>
      <code>
        oz0_discord
      </code>
      
      : <code>
        normalize
      </code>
      
       (the event source), <code>
        post
      </code>
      
       (the channel), <code>
        send
      </code>
      
       (an agent posts a message), <code>
        register_commands
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Event source and channel
    </td>
    
    <td>
      <code>
        discord
      </code>
      
      , on <code>
        POST https://&lt;edge>:7443/events/&lt;tenant>/discord
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Tenant secrets
    </td>
    
    <td>
      <code>
        DISCORD_PUBLIC_KEY
      </code>
      
       (the application's public key, read by the edge only), <code>
        DISCORD_BOT_TOKEN
      </code>
      
       (which connection <code>
        discord
      </code>
      
       hands each call that posts or registers the command, for that call only; never in the plugin's environment); <code>
        DISCORD_CLIENT_SECRET
      </code>
      
       for Connect; each pasted on Connect's page or set by hand
    </td>
  </tr>
  
  <tr>
    <td>
      Connection
    </td>
    
    <td>
      <code>
        discord
      </code>
      
      : your own application in guided, checked steps, then the bot's OAuth2 install
    </td>
  </tr>
  
  <tr>
    <td>
      Egress
    </td>
    
    <td>
      <code>
        discord.com
      </code>
    </td>
  </tr>
</tbody>
</table>

Upgrading to 1.1.0 needs nothing: the same `DISCORD_BOT_TOKEN` now reaches each call through the connection instead of the plugin's environment.

Mentions of the bot (`@oz0 …`) and plain messages in threads need Discord's Gateway, a long-lived connection the platform does not keep: they come after v1.0. Everything else works without it.

## Create your own Discord application, about two minutes

Every cluster uses a Discord application of its own, since Discord sends an application's interactions to one URL and yours go to your edge. Discord has no app manifest, so Connect guides you through the developer portal and checks each step before the next ([Connections](https://docs.oz0.ai/admin/connections#your-own-app-about-two-minutes)):

```bash [Terminal]
oz0 plugin install hub:ai.oz0/discord
oz0 connection start discord --open --wait 15m
```

Or in the console: Credentials, Connections, `discord`, Connect. On the edge's pages:

1. Open Discord's developer portal and make a New Application. Paste its Application ID and Public Key (General Information), its Client Secret (OAuth2, Reset Secret) and the bot's Token (Bot, Reset Token). The edge checks the token with Discord's `GET /users/@me` (a bot's, not a person's) and `GET /applications/@me` (this application's), and the Client Secret with a client credentials grant (Discord answers a token for the application's owner, which the edge does not keep). Only then does it seal the secrets as `DISCORD_PUBLIC_KEY`, `DISCORD_CLIENT_SECRET` and `DISCORD_BOT_TOKEN`; a wrong token or secret saves nothing.
2. The page shows the Interactions Endpoint URL (`https://<edge>/events/<tenant>/discord`) and the Redirect (the connection's callback), each with a copy button. Paste the first under General Information and click Save Changes: Discord sends a signed PING and a request with a bad signature, the edge answers the PING only with the public key you pasted and refuses the other, and the page goes on. Add the second under OAuth2, Redirects.
3. Discord asks which server to add the bot to (scopes `bot` and `applications.commands`, with the permissions to send messages and read history filled in). The edge checks the code with the client secret, keeps the server's ID, and registers `/oz0` there.

Then bind the channel and map people:

```bash [Terminal]
oz0 channel bind community --channel discord --agent helper
oz0 channel member add discord 80351110224678912 alice
```

People can also link themselves: each makes a code under Chat channels in the console (or `oz0 channel link`) and sends it as `/oz0 prompt: link <code>`, which only they see ([linking](https://docs.oz0.ai/build/apps/chat-channels#link-your-own-chat-identity)). If a step does not pass, its page says why; [troubleshooting](https://docs.oz0.ai/admin/connections/troubleshooting#troubleshooting) has Discord's usual causes (the PING failing, a wrong public key, another application's token).

Once the bot is in the server, the connection registers `/oz0` there: its `after:` calls `register_commands` with that server's ID as a tool job of its own ([after connecting](https://docs.oz0.ai/admin/connections#after-connecting)), and `connection show discord` shows the run. Copy IDs in Discord with Developer Mode on, under Settings, Advanced.

### By hand

1. Create an application in Discord's developer portal. On General Information, copy the Public Key and set the Interactions Endpoint URL to `https://<edge>:7443/events/<tenant>/discord`. Discord checks it with a PING and a forged request; the edge answers the first and refuses the second (so set the public key first).
2. On Bot, copy the bot's token.
3. Invite the bot to your server with the scopes `bot` and `applications.commands`, and the permissions to send messages and read message history.

```bash [Terminal]
oz0 secret set DISCORD_PUBLIC_KEY --from-file ~/.secrets/discord-public-key --signing --host <edge>:7443
oz0 secret set DISCORD_BOT_TOKEN --from-file ~/.secrets/discord-bot-token
```

Then call the plugin's `register_commands` function once yourself, with your server's ID as `guild` (it appears at once there; without one it is global and takes up to an hour).

## In Discord

- `/oz0 prompt:what broke last night?` answers "thinking…" at once, to you alone. The bot posts "you asked helper: …" in the channel and replies to it with the answer; a refusal (you are not mapped, a quota) stays in your "thinking…" message, which only you see.
- Reply under an answer opens a form; what you send goes on with the conversation.
- Questions have a button per choice, or Answer, which opens a form. Approvals have Approve and Deny; only mapped people with the operator role decide, everyone else gets a message only they see. Open in OZ is a link button (Discord's link style) to the job's page in the console, where an edit or a [typed question](https://docs.oz0.ai/agents/asking-people#typed-questions) is answered: Discord's form is answered within three seconds from the interaction alone, so it cannot carry a call's fields.
- An answer that matches its agent's contract and is a table or key-values comes as an embed's fields, with masked links shown as typed ([results as cards](https://docs.oz0.ai/build/apps/chat-channels/conversations#results-as-cards)).

The edge checks each interaction's `X-Signature-Ed25519` over `X-Signature-Timestamp` and the body with the public key, and refuses a timestamp more than five minutes off. It answers Discord within its three seconds from the plugin's declared replies: a `PONG`, a deferred answer to a command (only its author sees it), a deferred update to a click, and the form for a form button. Posts set `allowed_mentions` so that only the person who asked is notified, whatever the text says (`@everyone` included). Interaction tokens last 15 minutes and stay on the edge; after that the bot posts with its token.

## For agents

An agent that lists `oz0_discord.send` may post to a channel by its ID: `send(channel, text, reply_to?)`. The text starts with the tenant's [AI label](https://docs.oz0.ai/govern/ai-labels), `[AI agent] ...`, as the platform's own posts do. The plugin puts only Discord IDs (numbers) into the API's paths, so a crafted ID cannot reach another endpoint. Give agents `send`, never `post`: buttons count only on the platform's own messages, but `post` lets an agent post buttons at all. An agent that calls `post` anyway gets the AI label on its message too.

A rate limit (429) or Discord's 5xx is reported as retryable, and the platform posts again after the wait Discord asked for.

## Settings

`DISCORD_API_URL` in the tool's environment points the plugin at another API, such as a test fake.

## A message an agent sends asks by default

The plugin's suggested policy asks a person before each `send` ("the plugin asks before it sends a message"), as a
function that does not say whether it destroys anything did already. The install writes it as a rule of the tenant's
tool policy, marked as the plugin's. An admin who lets an agent post on its own switches it, with a reason:
`oz0 plugin policy discord --set oz0_discord.send=allow --reason "..."`. The channel's own replies (`post`) are the platform's
and not asked about.


## Sitemap

See the full [sitemap](https://docs.oz0.ai/sitemap.md) for all pages.
