Voznest Forge

Workspace

WorkspaceProjects

Development

DocsAPI ReferenceSDKsEmbed StudioChangelog
  1. Forge
  2. /Docs

Start here

Getting StartedQuickstartPlatform ConceptsEnvironments

Build

ProjectsIdentityAgentsGatewayCapabilitiesPermissions ReferenceInstallationsLive activity

Commands & interactions

CommandsContext-menu CommandsInteractionsComponentsModals (Forms)Activities (Embedded Apps)

Acting in hubs

MessagesEmbedsEmbeds v2 (Blocks)Embeds v3Incoming WebhooksHub Management APIEvent StreamDelivery

Sign in with Voznest

AccessOpenID Connect

API

APIAuthenticationErrorsRate LimitsSecurity

Publish & operate

App Market & ListingTemplates & SandboxObservability & Team Tools

Tools

SDKsExamplesFAQ & TroubleshootingReference

Commands & interactions

Interactions

The lifecycle of a command, button, menu or form — and every way to answer it.

An interaction is created when a member uses one of your commands (slash or context menu), clicks a button, picks from a select menu or submits a form. Voznest validates it first — command options, the member's permissions, the component's existence — so your agent only receives well-formed requests. They are still untrusted user input: validate values before acting on them.

The payload

json
{  "id": "66fb…",                    // interaction id  "token": "…",                     // answer with it (valid 15 min) — never log it  "type": "command",                // "command" | "component" | "modal_submit"  "applicationId": "…",  "serverId": "…",                  // the hub  "channelId": "…",                 // where it happened (a channel or a thread)  "user": { "id": "…", "username": "ana", "displayName": "Ana", "avatarUrl": "…" },  "member": { "roleIds": ["…"], "nickname": null, "permissions": ["viewChannels", "sendMessages", …] },  "data": {    // command:      { commandId, name, options: [{ name, type, value }], resolved: { users, channels, roles } }    // context menu: { commandId, name, kind: "user" | "message", targetId, target: { user } | { message } }    // component:    { customId, componentType: "button" | "select", values?: ["…"] }    // modal_submit: { customId, fields: [{ customId, value }] }  },  "message": { … }                  // component interactions: the message the control is on}

Answering

http
POST https://api.voznest.com/api/v1/interactions/{id}/{token}/callbackContent-Type: application/json{ "type": "reply", "data": { "content": "Done!", "ephemeral": true } }
typeWhenEffect
replyFirst answer or follow-upPosts a message (content, embeds, components). ephemeral: true shows it only to the member and isn't stored.
deferFirst answer onlyAcknowledges; the member sees that your app is working. Reply within 15 minutes.
updateComponent interactionsEdits the message the button/menu belongs to instead of posting a new one.
modalFirst answer onlyOpens a form; its submission arrives as a new modal_submit interaction. See Modals.

Timing

  • 5 seconds to send the first answer (reply, defer, update or modal). Otherwise the member is told your app didn't respond and the token stops accepting a first answer.
  • 15 minutes after the interaction, the token expires (INTERACTION_EXPIRED). Follow-up replies are accepted until then.
  • If no agent of the right environment is connected, the member immediately gets APP_OFFLINE — your code never sees the request.
ts
// Slow work: acknowledge now, answer later (same endpoint, within 15 minutes).await answer(d, { type: "defer" });                        // member sees "… is working on it"const report = await buildReport();                       // takes 30 secondsawait answer(d, { type: "reply", data: { content: report } });await answer(d, { type: "reply", data: { content: "Also: …" } }); // follow-ups are allowed
ts
// A button that updates its own message (no new message).if (d.type === "component" && d.data.customId === "count:+1") {  const n = Number(d.message.content.match(/\d+/)?.[0] ?? 0) + 1;  await answer(d, { type: "update", data: { content: `Count: ${n}` } });}

Which agent answers

The environment installed in the hub decides: Production hubs → the primary (production) agent; test hubs → that environment's agent. The callback endpoint itself needs no Authorization header — the token is the credential, so keep it out of logs.

The member's effective permissions in that channel are in member.permissions. Commands with requiredPermission are already filtered, but buttons are clickable by anyone who can see the message — check permissions for privileged buttons.
PreviousContext-menu CommandsNext Components