Skip to main content
This page explains the base runtime model and common return bodies for middleware.js. Before you write business logic, first confirm whether your plugin runs in a message, HTTP route, or cron / fake context.

Runtime context

Each plugin run has a senderID. For message triggers, senderID contains the IM, account, chat, and user. Cron triggers use a fake runtime context. Route triggers use a route context.
ScenariogetImtype()Reply targetProactive push account selection
Message triggerCurrent message IM, such as qq or weixinCurrent chat and current receiving accountIf the target IM equals the current IM, the current chat account is used by default.
cron / fakefakereply* does not send real IM messages by defaultUses the target IM default account.
HTTP routeRoute contextreply(...) / replyMarkdown(...) writes route response dataUses the target IM default account unless you pass account_id explicitly.
Cross-IM pushCurrent context IM differs from target IMNot applicableUses the target IM default account.
push(...) can explicitly choose a sending account with options.account_id. An explicit account_id has priority over the current chat account and the target IM default account. For multi-account plugins, explicitly set it for text proactive pushes.
const { Sender, getSenderID, push } = require('./middleware.js')
const sender = new Sender(getSenderID())

// The message comes from qq:bot_qq_2 and the target IM is also qq: bot_qq_2 is used by default.
await push('qq', '123456', '', 'Notice', 'Done')

// Explicit account_id wins: this uses bot_qq_ops.
await push('qq', '123456', '', 'Notice', 'Done', {
  account_id: 'bot_qq_ops',
})

// Cross-IM push: the message comes from qq but is pushed to weixin; the weixin default account is used.
await push('weixin', '', 'wxid_user', 'Notice', 'Done')

Runtime response wrapper

Plugin calls receive the data field. You do not need to handle the HTTP wrapper. The underlying runtime response shape is:
{
  "code": 0,
  "message": "success",
  "data": {
    "ok": true
  },
  "request_id": "req_abc123"
}
If a request fails, middleware.js converts the runtime error to an Error and throws it.

SendReceipt

Text, media, mixed-message, and recall methods usually return a send receipt.
{
  "ok": true,
  "message_id": "msg_123456",
  "messageid": "msg_123456",
  "message_ids": ["msg_123456"],
  "raw": {
    "adapter": "qq"
  },
  "action": "sendText"
}
ok
boolean
Whether sending succeeded. If the adapter does not return it explicitly, the runtime fills it with true.
message_id
string
Standard message ID. Pass it to recallMessage(...) when recalling a message.
messageid
string
Message ID field kept for legacy plugins. It is usually the same as message_id.
message_ids
string[]
Used for multi-part messages or when an adapter returns multiple IDs. A single message usually contains one ID.
raw
unknown
Adapter raw receipt. Read it only when troubleshooting platform issues.
error
string
Failure reason that an adapter may return. On failure, check receipt.ok === false and receipt.error first.

FileDownloadResult

fileDownload(...) returns FileDownloadResult. downloadAdapterFile(...) returns the same fields and also includes ok, url, and action.
{
  "ok": true,
  "action": "resolveFileURL",
  "url": "https://adapter.example/files/card_keys.csv",
  "path": "/opt/autclaw/file/imports/card_keys.csv",
  "file_path": "/opt/autclaw/file/imports/card_keys.csv",
  "file_name": "card_keys.csv",
  "mime_type": "text/csv",
  "file_size": 12345
}
ok
boolean
Always returned by downloadAdapterFile(...). It means the file was saved.
action
string
When the SDK needs an adapter to resolve a platform file ID into a download URL first, this is usually resolveFileURL. It may be empty for direct URL downloads.
url
string
Actual download URL. It may be the original URL or a temporary URL resolved by the adapter.
path
string
Saved local path. Prefer passing this field to media methods such as replyImage(...) and replyFile(...).
file_path
string
Same as path. Kept as a more explicit field name.
file_name
string
Saved filename.
mime_type
string
Detected or provided MIME type, such as image/png or text/csv.
file_size
number
File size in bytes.

MediaItem

sender.getMediaItems() returns normalized media items. It first returns media captured by the latest listen(...) / input(...); otherwise it returns media_items / mediaItems from the current event.
[
  {
    "type": "file",
    "source": "file_123",
    "name": "card_keys.csv",
    "mime_type": "text/csv",
    "platform_payload": {
      "file_id": "file_123"
    }
  },
  {
    "type": "image",
    "source": "https://example.com/demo.png",
    "name": "demo.png",
    "mime_type": "image/png"
  }
]
type
'image' | 'voice' | 'video' | 'file' | string
required
Media type. The current normalization flow mainly keeps media items that have both type and source.
source
string
required
Media source. It may be a URL, local path, temporary resource ID, platform file ID, or adapter raw file identifier.
name
string
Filename or display name. The runtime normalizes name, filename, and file_name to name.
mime_type
string
MIME type. The runtime normalizes it from mime_type or mimeType.
platform_payload
object
Platform raw file information, such as file_id, path, or url. When downloading user-uploaded files, pass the whole MediaItem to downloadAdapterFile(...).
Do not assemble platform file download URLs yourself. Pass the MediaItem through to downloadAdapterFile(...) and let the adapter handle platform differences and temporary URLs.

Next steps

Read and reply to messages

See Sender message context, reply, and recall methods.

Proactive push account rules

See push(...) account_id priority and cross-IM behavior.
Last modified on June 3, 2026