Docs / Developers / Reference / Reference — ctx attributes
ReferenceSDK 0.10.1

Reference — ctx attributes

These docs are public. To publish a plugin you need a YourBot account: sign in to open the Dev Portal.
AttributeType
ctx.server_idstr — Discord server (guild) ID
ctx.plugin_idstr — your plugin's id
ctx.versionstr — installed version string
ctx.capabilitiesset[str] — capabilities the user approved
ctx.discordDiscord REST surface
ctx.interactionInteraction response surface
ctx.kvPer-server KV store
ctx.sqlSandboxed SQL (capability)
ctx.ephemeralRedis-backed counters / cooldowns / flags
ctx.httpOutbound HTTP via proxy
ctx.wsPersistent WebSocket connections via the platform broker (capability)
ctx.secretsEncrypted secret storage (capability)
ctx.metricsTime-series metrics
ctx.request_idstr — correlation ID for the current interaction ("" outside one)
ctx.log(message, *, level, tags, **extra)Structured plugin log
ctx.has_capability(name)bool — capability check

ctx.discord

MethodCapability
send_message(*, channel_id, content="", embeds=None, components=None, files=None) → returns {"message_id", "channel_id"}files is a list of {"filename", "data_b64"}, up to 3 attachmentsdiscord:send_message
edit_message(*, channel_id, message_id, content=None, embeds=None, components=None) — None leaves components unchanged, [] clears themdiscord:edit_message
delete_message(*, channel_id, message_id)discord:delete_message
bulk_delete_messages(*, channel_id, message_ids)discord:delete_message
add_reaction(*, channel_id, message_id, emoji)discord:add_reaction
pin_message(*, channel_id, message_id)discord:send_message
unpin_message(*, channel_id, message_id)discord:send_message
get_messages(*, channel_id, limit=50, before=None, after=None)discord:read
iter_messages(*, channel_id, batch_size=50, before=None, after=None) v0.6.1 — generator that pages through full channel history (newest→oldest, or oldest→newest with after=)discord:read
get_guild()discord:read
get_channel(*, channel_id)discord:read
list_channels()discord:read
get_member(*, user_id)discord:read
list_members(*, role_id=None, limit=100, after=None)discord:read
search_members(query, *, limit=25)discord:read
list_roles()discord:read
create_channel(*, name, channel_type=0, category_id=None, topic=None, user_limit=None)discord:manage_channels
edit_channel(*, channel_id, name=None, topic=None, user_limit=None)discord:manage_channels
delete_channel(*, channel_id)discord:manage_channels
set_channel_permissions(*, channel_id, target_id, allow="0", deny="0", target_type=0)discord:manage_channels
delete_channel_permission(*, channel_id, target_id)discord:manage_channels
create_thread(*, channel_id, name, thread_type=11, auto_archive_duration=1440)discord:manage_channels
edit_thread(*, thread_id, archived=None, locked=None, name=None, auto_archive_duration=None)discord:manage_channels
timeout_member(*, user_id, duration_seconds, reason="")discord:moderate_members
set_nickname(*, user_id, nickname=None)discord:moderate_members
kick_member(*, user_id, reason="")discord:kick_members
ban_member(*, user_id, reason="", delete_message_seconds=0)discord:ban_members
unban_member(*, user_id)discord:ban_members
add_role(*, user_id, role_id, reason="")discord:manage_roles
remove_role(*, user_id, role_id, reason="")discord:manage_roles
add_role_bulk / remove_role_bulk / timeout_bulk / kick_bulk(matches single-action capability; each call takes at most 25 user_ids — extras are silently dropped, so chunk big lists yourself)
create_webhook(*, channel_id, name)discord:manage_webhooks
execute_webhook(*, webhook_id, webhook_token, content="", embeds=None, username=None, avatar_url=None)discord:manage_webhooks
delete_webhook(*, webhook_id)discord:manage_webhooks

edit_message, delete_message and bulk_delete_messages only operate on messages your plugin sent — attempts on other messages are rejected, and a bulk call fails closed on the whole batch if any ID isn't plugin-owned. Ownership records expire after 24 hours (each successful edit refreshes the clock), so design flows that edit long-lived messages — leaderboard boards, reaction-role posts — to re-send rather than edit once the message is a day old.

Silent clamps: arguments outside Discord's ranges are clamped or truncated rather than raising — bulk_delete_messages takes at most 100 IDs, timeout_member caps at 28 days, ban_member's delete_message_seconds at 7 days, set_nickname at 32 chars, and get_messages / list_members / search_members clamp their limit. If an exact value matters, validate before you call.

ctx.ws

Requires proxy:websocket. Connections are named; the platform's broker holds the socket. Full guide on the Storage & I/O tab.

MethodUse
ensure(name, url, *, secret_auth=None, auth=None, subscribe=None, binary=False)Idempotently open (or confirm) the named connection. Returns {"conn_id", "name", "state"}. subscribe frames are re-sent after every reconnect.
send(name, data)One frame: str sends text, bytes sends binary.
close(name)Hang up the named connection.
allow_host(host) / revoke_host(host)Authorize a WebSocket destination a server admin supplies at setup time; call it from a slash handler run by a member with Admin/Manage Server, or from a dashboard handler with a manager viewer.

ctx.interaction

All four methods require the interaction:respond capability (auto-added when your manifest declares slash_commands).

MethodUse
respond(*, content="", embeds=None, components=None, ephemeral=False, allowed_mentions=None, update_message=False)First reply (within 3s of receiving the interaction). allowed_mentions mirrors Discord's API field — e.g. {"parse": []} suppresses all pings. update_message=True (component handlers only, v0.7.0) edits the message the component sits on instead of sending a new reply.
defer(*, ephemeral=False)"I'm working on it" — buys you up to 15 minutes for a followup
followup(*, content="", embeds=None, components=None, ephemeral=False, allowed_mentions=None)Reply after a defer (or send additional messages). Returns {"message_id", "channel_id"} so you can edit the message later.
send_modal(*, title, custom_id, fields=None)Open a modal — use as the FIRST response, not after a defer

ctx.kv / ctx.sql / ctx.ephemeral / ctx.http / ctx.metrics

Full method signatures live on the Storage & I/O tab: KV, SQL, Ephemeral, HTTP (including secret-backed Authorization injection via secret_auth=), Metrics.

Plugin decorators

DecoratorHandler signature
@plugin.on_ready(ctx) — per (worker, server) before that server's first event in pool mode; once per boot locally
@plugin.on_event(event_type)(ctx, event)
@plugin.on_slash_command(name)(ctx, event)
@plugin.on_component(custom_id=None, *, prefix=None)(ctx, event) — exactly one of exact custom_id or prefix match
@plugin.on_modal_submit(custom_id)(ctx, event)
@plugin.on_ws_message(name)(ctx, frame) — {"name", "conn_id", "data", "binary"}, binary data base64. "prefix:*" wildcard matches by prefix; exact names win
@plugin.on_ws_open(name)(ctx)
@plugin.on_ws_close(name)(ctx, info) — {"reason", "will_reconnect"}
@plugin.on_dashboard(method_name)(ctx, params) → dict
@plugin.schedule(seconds)(ctx) — local yourbot dev only; does not run in pool mode (marketplace plugins), see Pool mode
@plugin.cron(spec)(ctx) — 5-field UTC cron string. Runs in production when declared in the manifest "cron" array (5-minute floor, 5 entries max); see Cron schedules
@plugin.on_install / on_enable / on_disable / on_uninstall(ctx) — advisory signals, delivered best-effort when a worker is live, with a tenant-scoped ctx that keeps your approved capabilities. Required init still belongs in on_ready

Event payload reference

Typed payload shapes for the 16 core events live in yourbot_sdk.events (MessageCreate, MemberJoin, …); the four gateway-only events (reaction_clear, thread_create, guild_join, guild_remove) are plain dicts. Defaults produced by yourbot_sdk.testing.make_event:

make_event("message_create", content="hi")
# {message_id, channel_id, guild_id, author_id, author_username, author_bot,
#  content, timestamp}

make_event("interaction_create", command_name="ban", custom_id="")
# {interaction_id, interaction_type, guild_id, channel_id, user_id,
#  command_name, custom_id, modal_values}

make_event("reaction_add", emoji="🎉")
# {message_id, channel_id, user_id, emoji, guild_id}

WebSocket handlers receive their own shapes (not events): on_ws_message gets {"name", "conn_id", "data", "binary"} (binary data base64, frames per connection arrive in order) and on_ws_close gets {"reason", "will_reconnect"}.

Exceptions reference

See the full table under Error handling on the Production tab.

YourBot docs Reference tables are generated from the code that is running. Ask in Discord Suggest a correction