LINE Channel Setup and Configuration for OpenClaw
This page explains how to install and configure the LINE Messaging API plugin for OpenClaw, covering webhook setup, authentication, and supported message types. It is intended for developers integrating LINE with OpenClaw.
Read this when
- You want to connect OpenClaw to LINE
- You need LINE webhook + credential setup
- You want LINE-specific message options
LINE integrates with OpenClaw through the LINE Messaging API. Acting as a webhook receiver on the Gateway, the plugin authenticates using your channel access token together with the channel secret.
This is an official plugin that requires separate installation. It handles direct messages, group chats, media, locations, Flex messages, template messages, and quick replies. Neither reactions nor threads are supported.
Install
Before configuring the channel, install LINE:
openclaw plugins install @openclaw/line
For a local checkout (when operating from a git repository):
openclaw plugins install ./path/to/local/line-plugin
Setup
- Sign up for a LINE Developers account, then access the Console at https://developers.line.biz/console/.
- Choose or create a Provider, then add a Messaging API channel.
- From the channel settings, copy both the Channel access token and Channel secret.
- Within Messaging API settings, turn on Use webhook.
- Point the webhook URL at your gateway endpoint, which must use HTTPS:
https://gateway-host/line/webhook
The Gateway responds to LINE's signed webhook verification request by returning a POST that contains an empty events list. For signed inbound events, each one gets written to the durable ingress queue before a 200 response is sent; agent processing happens asynchronously after that. Undelivered events are retried from the queue, even across a Gateway restart, and poison events become failed queue records once bounded retries are exhausted. When durable persistence cannot be completed, the request returns 500 rather than acknowledging an event that might be lost.
Delivery across the queue-to-agent boundary is at least once: a Gateway shutdown or crash during an active delivery can cause the turn to be replayed. Message events are deduplicated by LINE message ID; other event types rely on webhookEventId. Retained completion records keep ordinary duplicate webhooks from being processed, but handlers with external side effects should still be written idempotently.
For a custom path, configure channels.line.webhookPath or channels.line.accounts.<id>.webhookPath and adjust the URL to match.
Security considerations:
- LINE's signature verification depends on the body (HMAC computed over the raw body), so OpenClaw enforces a strict 64 KB pre-auth body limit and a read timeout before verification runs.
- OpenClaw handles webhook events from the verified raw request bytes. Any upstream middleware-transformed
req.bodyvalues are disregarded to preserve signature integrity.
Inbound durability
The Setup webhook contract only acknowledges an event once it has been durably queued. The durable 200 carries x-openclaw-delivery-accepted: durable; signed verification pings (which have empty event lists) and error responses leave out the marker, letting reverse proxies require it to tell durable acceptance apart from a generic 200. From that point, delivery proceeds through the standard channel-ingress drain with LINE-specific behavior:
- Per-conversation ordering. Events are serialized by source lane, either
group:<groupId>,room:<roomId>, oruser:<userId>; events lacking a conversation source get their own event-scoped lane. Within a single lane, events dispatch in the order they arrived, so a retrying event holds up later events in that same chat. One chat's backlog cannot block another chat's lane, yet all lanes share a limit of 8 concurrent deliveries: other chats move forward independently whenever a slot is free, and a 9th lane waits until one opens up. - Retries. A failed delivery is retried with exponential backoff that begins at 1 second and doubles with each attempt, totaling roughly two minutes of cumulative backoff across the window. Once the 8th attempt fails, the event dead-letters (
retry-limit-exceeded) right away: LINE opts out of the generic 24-hour dead-letter age floor so a poison event cannot tie up its conversation lane for a full day. - Non-retryable failures. These dead-letter immediately, with no retries regardless of how many attempts were made: stored payloads that no longer parse (
invalid-event), deliveries that already committed side effects (delivery-side-effects-committed), and LINE API authentication failures (authentication-failed, HTTP 401/403). - Stall watchdog. A claimed delivery that neither reaches agent-turn adoption nor reports continued deferred progress for 5 minutes is aborted and returned to its lane under the same retry policy as any other failure: the event goes back to pending with its attempt count incremented and
handler-timeoutrecorded as its last error, keeping its position at the head of its lane. A stall is not itself a dead letter; only the retry limit above terminates the event, asretry-limit-exceeded. The watchdog covers only the window between claim and adoption: deferred progress re-arms it and adoption clears it, so a long agent turn is never interrupted. Adoption arriving after the watchdog fires is fenced off, meaning a late turn cannot claim an event that has already been handed back. - Crash recovery. Each drain pass starts with a recovery sweep that reclaims any claim whose owning Gateway process is no longer running, so a delivery lost to a hard crash gets retried on the next sweep rather than waiting for a timeout. The 30-minute claim lease is the fallback bound for the opposite case: without a successful lease refresh, it limits how long a claim stays protected solely because its owner PID still appears alive, including a reused PID whose process identity cannot be verified. Events accepted while the Gateway is stopping are still persisted and drain after the next start.
- Duplicate suppression window. On every admission, LINE removes completed and failed queue records older than 30 days, then keeps the 4096 most recently updated records of each kind per account. Since pruning runs before the new event is queued rather than on a timer, records can persist past 30 days on an idle account, and a newly completed record can push the count above 4096 until the next admission. While a record exists, a redelivered webhook for the same event is acknowledged without a second dispatch; once it disappears, whether by age or by cap, a redelivery is admitted and dispatched again, so handlers with external side effects should not treat this window as a replacement for their own idempotency.
The 500-on-persistence-failure contract only works if LINE re-sends the event. LINE redelivers a webhook when Webhook redelivery is enabled for the channel in the LINE Developers Console (Messaging API settings, alongside Use webhook) and the bot server did not answer 2xx. Without that setting, an event refused with 500 is not re-sent. Even when enabled, redelivery is best effort rather than guaranteed: LINE documents that it is not reliable, that the retry count and interval are undisclosed, and that redelivered events may arrive out of order or more than once (the duplicate suppression window above absorbs the repeats). See Redeliver a webhook that failed to be received.
Dead-lettered events remain inspectable and, depending on the failure reason, recoverable; see Inbound dead letters and Troubleshooting below.
Configure
Minimal configuration:
{
channels: {
line: {
enabled: true,
channelAccessToken: "LINE_CHANNEL_ACCESS_TOKEN",
channelSecret: "LINE_CHANNEL_SECRET",
dmPolicy: "pairing",
},
},
}
Public DM configuration:
{
channels: {
line: {
enabled: true,
channelAccessToken: "LINE_CHANNEL_ACCESS_TOKEN",
channelSecret: "LINE_CHANNEL_SECRET",
dmPolicy: "open",
allowFrom: ["*"],
},
},
}
Environment variables (default account only):
LINE_CHANNEL_ACCESS_TOKENLINE_CHANNEL_SECRET
Token and secret files:
{
channels: {
line: {
tokenFile: "/path/to/line-token.txt",
secretFile: "/path/to/line-secret.txt",
},
},
}
Both tokenFile and secretFile must reference regular files. Symlinks are rejected.
Inline config values take precedence over files; environment variables serve as the final fallback for the default account.
Multiple accounts:
{
channels: {
line: {
accounts: {
marketing: {
channelAccessToken: "...",
channelSecret: "...",
webhookPath: "/line/marketing",
},
},
},
},
}
Access control
Direct messages default to pairing. Unknown senders receive a pairing code and their messages are ignored until approved:
openclaw pairing list line
openclaw pairing approve line <CODE>
Allowlists and policies:
channels.line.dmPolicy:pairing | allowlist | open | disabled(defaultpairing)channels.line.allowFrom: allowlisted LINE user IDs for DMs;dmPolicy: "open"requires["*"]channels.line.groupPolicy:allowlist | open | disabled(defaultallowlist)channels.line.groupAllowFrom: allowlisted LINE user IDs for groups; DMallowFromentries do not admit group senders- Per-group overrides:
channels.line.groups.<groupId>.allowFrom(plusenabled,requireMention,systemPrompt,skills). WithgroupPolicy: "allowlist", setgroupAllowFromor the per-groupallowFrom; an empty group allowlist blocks group messages even when DMs are open. - Quoting one of the bot's own messages counts as addressing it, so a group reply made with LINE's quote gesture reaches the agent without an explicit mention. LINE does not read the
implicitMentionsflags, so this always counts; see Groups. The bot recognizes a quote of its own message from the most recent ones it remembers sending (a few hundred per account), so quoting an older message, or one sent before the last Gateway restart, still needs a mention. - Static sender access groups can be referenced from
allowFrom,groupAllowFrom, and per-groupallowFromwithaccessGroup:<name>; see Access groups. - Runtime note: if
channels.lineis completely missing, runtime falls back togroupPolicy="allowlist"for group checks (even ifchannels.defaults.groupPolicyis set).
LINE IDs are case-sensitive. Valid IDs look like:
- User:
U+ 32 hex chars - Group:
C+ 32 hex chars - Room:
R+ 32 hex chars
Group join introductions
When the bot joins an allowed group or multi-person room, it posts one introduction there. LINE exposes a group name through its group summary API, but no room name or topic for multi-person rooms. The Messaging API cannot read prior messages, so introductions use only available metadata and ask what the room wants the bot to take on rather than inventing activity.
Introductions are enabled by default. Set channels.line.joinIntro: false to
disable them, or use channels.line.accounts.<accountId>.joinIntro to override
one account. They never run in one-to-one user chats or when another member joins.
See group join introductions for room
admission, once-per-room behavior, and the no-tools turn that treats room content
as untrusted.
Message behavior
- Text is chunked at 5000 characters.
- Markdown formatting is stripped; code blocks and tables are converted into Flex cards when possible.
- Streaming responses are buffered; LINE receives full chunks with a loading animation while the agent works.
- Media downloads are capped by
channels.line.mediaMaxMb(default 10). - Inbound media is saved under
~/.openclaw/media/inbound/before it is passed to the agent, matching the shared media store used by other channel plugins. - LINE webhooks carry ids but no names, so the sender's display name and the group's name are fetched once and cached for five minutes. Group and room members are read through their conversation, which is the only way to see a member who has not added the bot as a friend. If either lookup fails the raw id is used and the message is still delivered. Multi-person rooms have no name API, so they keep their room id.
Structured rich messages
Use the shared message presentation fields for portable choices. LINE renders
buttons blocks as Flex controls and select blocks as quick replies. A two-button
block is the portable confirm-style form.
{
action: "send",
message: "Choose an action",
presentation: {
title: "Menu",
blocks: [
{
type: "buttons",
buttons: [
{ label: "Status", action: { type: "command", command: "/status" } },
{ label: "Website", action: { type: "url", url: "https://example.com" } },
],
},
{
type: "select",
placeholder: "Pick one",
options: [
{ label: "Alpha", action: { type: "callback", value: "alpha" } },
{ label: "Help", action: { type: "command", command: "/help" } },
],
},
],
},
}
LINE-only output uses the schema-validated channelData.line fields on
message(action="send"). Send one location and/or one card. The supported card
types are media_player, event, agenda, device, and appletv_remote.
{
action: "send",
message: "Here you go",
channelData: {
line: {
location: {
title: "Office",
address: "123 Main St",
latitude: 35.681236,
longitude: 139.767125,
},
card: {
type: "event",
title: "Team meeting",
date: "2026-08-18",
time: "10:00",
location: "Conference room",
description: "Weekly planning",
},
},
},
}
Other card shapes:
{ type: "media_player", title: "Song", artist: "Artist", source: "Living Room", status: "playing", imageUrl: "https://example.com/cover.jpg" }
{ type: "agenda", title: "Today", events: [{ title: "Standup", time: "09:00", location: "Online" }] }
{ type: "device", name: "TV", deviceType: "Streaming box", status: "Playing", controls: [{ label: "Pause", action: "pause" }] }
{ type: "appletv_remote", name: "Living Room", status: "Playing" }
Double-bracket strings such as [[buttons: ...]] are plain text and are not
interpreted as rich-message instructions.
The LINE plugin also ships a /card command for Flex message presets:
/card info "Welcome" "Thanks for joining!"
ACP support
LINE supports ACP (Agent Communication Protocol) conversation bindings:
/acp spawn <agent> --bind herebinds the current LINE chat to an ACP session without creating a child thread.- Configured ACP bindings and active conversation-bound ACP sessions work on LINE like other conversation channels.
See ACP agents for details.
Outbound media
The LINE plugin transmits images, videos, and audio via the agent message tool:
- Images: delivered as LINE image messages; the preview image falls back to the media URL by default.
- Videos: a preview image is mandatory; assign an image URL to
channelData.line.previewImageUrl. - Audio: sent as LINE audio messages; the duration is 60 seconds unless
channelData.line.durationMsoverrides it.
If mediaKind is not provided, LINE derives it from LINE-specific options or the URL
extension. Native extension detection covers JPEG/PNG, MP4, and MP3/M4A; URLs without an extension
keep the image fallback. Other extensions and inferred MP4 lacking a preview
are converted to text links. An explicit video still needs previewImageUrl.
Outbound media URLs must be public HTTPS endpoints capped at 2000 characters. OpenClaw checks the target hostname before passing the URL to LINE and blocks loopback, link-local, and private-network destinations.
Troubleshooting
- Webhook verification fails: the webhook URL must be HTTPS and
channelSecrethas to match the LINE console. - No inbound events: verify the webhook path aligns with
channels.line.webhookPathand that the gateway is accessible from LINE. - Media download errors: raise
channels.line.mediaMaxMbwhen media exceeds the default limit. - Bot silently skips messages (events dead-lettered):
openclaw logsdisplaysline: spooled update <id> ... dead-letteredentries with the failure reason. Examine withopenclaw channels dead-letters list --channel line --account defaultand review the failure reason before recovery:resubmitre-queues by event id without validating why the event failed. Once the cause of a failure with no committed side effects is fixed (for instanceretry-limit-exceededafter a provider outage), re-queue a single event usingopenclaw channels dead-letters resubmit <event-id> --channel line --account default. Do not resubmit adelivery-side-effects-committedevent: that reason indicates the delivery already claimed an agent turn or used up its reply token, so re-queuing duplicates the committed work, such as a second visible reply.openclaw healthreports dead-letter totals andopenclaw doctorlists affected accounts. handler-timeoutretries: the delivery was claimed but neither reached agent-turn adoption nor logged deferred progress within 5 minutes. This is a stall before the turn starts; adoption clears the watchdog, so an active turn is never the cause and is never interrupted by it. Inspect the dispatch path instead: the delivery preparation between claim and adoption, like inbound media download or a Gateway that refuses new work. This does not dead-letter the event;openclaw logsshowsapplying retry policy (handler-timeout)and the event waits through its backoff withhandler-timeoutas its final error. A recurring stall is what eventually depletes the retry limit, so an event that stalls into a dead letter falls underretry-limit-exceeded, not a timeout reason. Inspectopenclaw logs --follownear the affected event id.
Related
- Channels Overview, every supported channel
- Pairing, DM authentication and pairing flow
- Groups, group chat behavior and mention gating
- Channel Routing, session routing for messages
- Security, access model and hardening