OpenClaw plugin for connecting OMELINK to OpenClaw as a text-only channel.
- OMELINK to OpenClaw: OMELINK calls this plugin's inbound webhook.
- OpenClaw to OMELINK: this plugin calls OMELINK's
/api/external/openClaw/channel/messagesendpoint.
The first version supports single-user direct conversations only. It does not use sender_id and does not support group chat, media, reactions, message edits, or rich cards.
Install from GitHub:
openclaw plugins install git:avenger-dev-group/omelink-openclaw-pluginThe git: prefix is required by OpenClaw's plugin installer. Bare
https://github.com/... URLs are rejected as unsupported npm specs.
Or install from a local checkout:
Build the plugin:
npm install
npm run buildRegister this plugin directory in OpenClaw, then restart the gateway:
openclaw plugins install /path/to/omelink-openclaw-plugin --linkRestart the gateway:
openclaw gateway restartConfirm OpenClaw can load the plugin:
openclaw plugins inspect omelink --runtime --jsonThe plugin should report status: "loaded".
The plugin loads on OpenClaw gateway startup. It does not require
channels.omelink.baseUrl to be present before the inbound, agent, and config
routes are registered.
URL placeholders used below:
<OPENCLAW_GATEWAY_URL>: OpenClaw Gateway base URL, for examplehttps://openclaw.example.com.<OMELINK_API_URL>: OMELINK API base URL, for examplehttps://api.omelink.example.com.
Optionally configure the channel in OpenClaw config. The plugin reads only
channels.omelink; it does not read OMELINK_* environment variables.
OpenClaw may start the plugin with no channels.omelink entry at all; when
channels.omelink.baseUrl is omitted or blank, the plugin still starts and
uses http://127.0.0.1 at runtime.
{
"channels": {
"omelink": {
"baseUrl": "<OMELINK_API_URL>",
"apiKey": "<OMELINK_API_KEY>"
}
}
}channels.omelink.baseUrl is the OMELINK API base URL, for example
https://api.omelink.example.com. Leave it blank to use the runtime fallback
http://127.0.0.1. Do not include
/api/external/openClaw/channel/messages; the plugin appends that path.
channels.omelink.apiKey is optional. When set, OpenClaw sends it to OMELINK
as x-api-key on outbound /messages requests.
channels.omelink.baseUrl is optional. If it is omitted or blank, the plugin
falls back to http://127.0.0.1.
Inbound plugin routes use OpenClaw Gateway authentication. Send the Gateway
token as Authorization: Bearer <OPENCLAW_GATEWAY_TOKEN>.
OMELINK can verify that the plugin is installed and its gateway routes are available by calling the heartbeat endpoint:
curl --location --request GET '<OPENCLAW_GATEWAY_URL>/api/external/omelink/channel/heartbeat' \
--header 'Authorization: Bearer <OPENCLAW_GATEWAY_TOKEN>'Success response:
{
"ok": true,
"plugin": "omelink"
}Update channels.omelink.baseUrl and channels.omelink.apiKey through the
plugin config endpoint. The request field apiHost is written to
channels.omelink.baseUrl.
curl --location --request POST '<OPENCLAW_GATEWAY_URL>/api/external/omelink/channel/config' \
--header 'Authorization: Bearer <OPENCLAW_GATEWAY_TOKEN>' \
--header 'Content-Type: application/json' \
--data-raw '{
"apiHost": "<OMELINK_API_URL>",
"apiKey": "<OMELINK_API_KEY>"
}'You can send either apiHost, apiKey, or both. Fields omitted from the
request keep their current configured value.
The response includes restart_required: true. Restart the OpenClaw gateway so
the new outbound API config is loaded.
Send a user message to the plugin webhook:
curl --location --request POST '<OPENCLAW_GATEWAY_URL>/api/external/omelink/channel/inbound' \
--header 'Authorization: Bearer <OPENCLAW_GATEWAY_TOKEN>' \
--header 'Content-Type: application/json' \
--data-raw '{
"omelink_agent_id": "omelink-agent-xxx",
"omelink_message_id": "omelink-message-xxx",
"text": "你好"
}'Fields:
omelink_agent_id: OMELINK agent ID. This is also used as the OpenClaw direct conversation identity.omelink_message_id: unique OMELINK message ID. The plugin uses it for short-term duplicate protection.text: plain text message body.
Create OpenClaw agents and optionally bind OMELINK conversations to them:
curl --location --request POST '<OPENCLAW_GATEWAY_URL>/api/external/omelink/channel/agents' \
--header 'Authorization: Bearer <OPENCLAW_GATEWAY_TOKEN>' \
--header 'Content-Type: application/json' \
--data-raw '{
"agents": [
{
"agent_id": "support",
"name": "Support Agent",
"omelink_agent_id": "omelink-agent-support",
"model": "metis-coder/metis-coder"
},
{
"agent_id": "sales",
"name": "Sales Agent",
"omelink_agent_id": "omelink-agent-sales",
"model": "metis-coder/metis-coder"
}
]
}'Fields:
agents: required non-empty array of agent definitions.agents[].agent_id: required. Lowercase safe ID matching^[a-z][a-z0-9_-]{0,63}$;mainis reserved.agents[].name: optional display name.agents[].omelink_agent_id: optional OMELINK agent ID to route to this OpenClaw agent.agents[].model: optional OpenClaw model ID for this agent.agents[].workspace: optional absolute workspace path. Defaults to~/.openclaw/agents/<agent_id>/workspace.agents[].agent_dir: optional absolute agent state path. Defaults to~/.openclaw/agents/<agent_id>/agent.
The endpoint updates OpenClaw config in one batch by adding:
agents.list[]entries for the agents.bindings[]routes foromelink+omelink_agent_id, when provided.session.dmScope: "per-channel-peer"when unset or set to"main", so differentomelink_agent_idvalues keep separate context.
The response includes restart_required: true. Restart the OpenClaw gateway after creating or changing agents so the new routing config is loaded.
Success response:
{
"ok": true,
"agents": [
{
"agent_id": "support",
"created": true,
"bound": true,
"workspace": "/Users/you/.openclaw/agents/support/workspace",
"agent_dir": "/Users/you/.openclaw/agents/support/agent"
},
{
"agent_id": "sales",
"created": true,
"bound": true,
"workspace": "/Users/you/.openclaw/agents/sales/workspace",
"agent_dir": "/Users/you/.openclaw/agents/sales/agent"
}
],
"dm_scope": "per-channel-peer",
"restart_required": true
}The plugin sends OpenClaw replies to:
POST ${channels.omelink.baseUrl}/api/external/openClaw/channel/messages
with:
{
"omelink_conversation_id": "omelink-conversation-xxx",
"open_claw_message_id": "openclaw-message-xxx",
"text": "Hello, how can I help you?"
}When channels.omelink.apiKey is configured, the request includes:
x-api-key: <channels.omelink.apiKey>
If channels.omelink.apiKey is omitted, no plugin-specific API key header is
sent.
For example, if channels.omelink.baseUrl is <OMELINK_API_URL>, the full
URL is:
POST <OMELINK_API_URL>/api/external/openClaw/channel/messages
The current plugin and channel id is omelink. If an older local OpenClaw config
still uses the previous id, update it manually:
channels.omelink-im->channels.omelink- binding
match.channel: "omelink-im"->match.channel: "omelink"
npm install
npm test
npm run build