LINE Onboarding: Messaging API Channel to Working Two-Way Chat
This guide walks you from an empty LINE channel to a working, production-ready setup. It complements the LINE channel page, which covers message shapes and capability detail — this guide is the ordered path you follow the first time, and re-run whenever you attach, rotate, or retire a LINE Official Account. LINE has no template approval cycle for plain text, and no brand gate beyond having a LINE Official Account. The real work is connecting the right credentials and understanding which LINE recipient ids are usable. The Asia channels onboarding guide positions LINE beside KakaoTalk, Viber, and WeChat; this guide is the LINE-specific walkthrough.1. Prerequisites — the Messaging API channel
You need two things from a LINE Official Account before Orbit can carry traffic:- A LINE Official Account. If you do not have one, create it in the LINE Official Account Manager.
- A Messaging API channel on that account, created in the LINE Developers Console.
- Channel access token (long-lived) — from the channel’s Messaging API tab. This is the bearer token LINE accepts on outbound push calls.
- Channel secret — from the channel’s Basic settings tab. Orbit uses it to verify the
x-line-signatureheader on inbound webhooks before any event reaches your handler.
destination value every inbound webhook carries, and it is what identifies your tenant on the shared inbound URL.
Treat both values as bearer credentials: anyone holding them can message as your Official Account. See Credential handling — your responsibility on the channel page for the handling rules (never commit to git, scope dev/staging/production to separate channels, rotate on suspicion).
2. Connect the channel to Orbit
Paste the two values once in the dashboard:- Navigate to Channels → LINE → Connect.
- Paste the channel access token and channel secret. Orbit validates the token at connect time — an expired or invalid token returns
LINE_INVALID_TOKEN(401) instead of connecting. - Orbit stores both values encrypted at rest under your organization’s
settings.channels.lineand never echoes them back through any API response. - In the LINE Developers Console, set the channel’s Webhook URL to
https://api.orbit.devotel.io/api/v1/webhooks/inbound/lineand enable Use webhook. Every Orbit tenant shares this single URL — the LINE channel id (destination) on each inbound event is what resolves the receiving tenant (see §4).
owner, admin, or developer role.
Where the connect-time rejection actually lands: the connect call answers 200 with a body that carries the rejection — branch on data.error_code, not on the HTTP status. An expired or invalid channel access token looks like this:
error_status is the status LINE’s own API returned to Orbit’s verification probe. Re-paste a current token under Channels → LINE and connect again — a token rotated after a successful connect keeps working on sends until LINE rejects it, and that surfaces as MESSAGE_SEND_FAILED (§7).
3. Send — POST /api/v1/messages/line
Sending goes through the unified Messaging surface. The text body is the top-levelbody field, the LINE message type is the top-level type field (defaulting to text), and the per-type secondary options ride under metadata as string→string values. The sender is selected automatically from your connected channel credentials — you never pass a token or channel id on the request.
The request body is the shared free-form shape used by the other OTT channels (Viber, Telegram, KakaoTalk) — to + body are the only required fields, with optional from, media_url, metadata, and scheduled_at:
202 Accepted means queued — the terminal delivered / failed state arrives later on the delivery-status webhook, whose envelope shape is documented on the channel page. A soft failure (recipient blocked your Official Account) shows up there as message.failed with error_code: MESSAGE_DELIVERY_FAILED.
Outbound POST /messages/line is capped at 80 requests/minute per tenant; bursts past the cap receive 429 with a Retry-After header, so stage high-volume sends through the campaigns API.
The full send with metadata — six languages
metadata is a flat string→string map, and that is the part first integrations get wrong: every value must already be a string. Send numbers as strings, and send nested JSON (like a flex layout) as a JSON-encoded string, never as a nested object. The same text send with a campaign_tag attached:
text sends Orbit echoes metadata back on the delivery and status webhooks, so a tag set here is how you correlate a webhook to the send that caused it.
Sticker and flex messages
Two types cannot be deduced from the field table above, so here they are worked end to end. Sticker — a sticker message takes exactly two options: the sticker set (package_id) and the sticker inside it (sticker_id), both strings under metadata. body is required by the API but is not rendered by LINE; put a plain-text fallback in it for surfaces that cannot show the sticker.
metadata.flex_contents. Encode it twice: build the layout object, then stringify it so it sits inside metadata as a single string value. The top-level body becomes LINE’s altText — the text announced where the layout cannot render, so write a real sentence, not a placeholder. A layout that is not a valid JSON object (an array, or malformed JSON) is rejected with VALIDATION_ERROR (422).
image, video, audio, location) follow the same pattern — their option keys are listed on the channel page, and the API recipes cookbook works several of them through the shared OTT shape.
The response envelope
Every send above answers the same envelope;data.id is your handle for polling and reconciliation:
4. Inbound — tenant resolution from destination
LINE fans every tenant’s inbound traffic into the single platform URL you set in §2. Each inbound event carries a destination field — the numeric LINE channel id of the receiving Official Account — and Orbit resolves the owning tenant from that value before doing anything else:
- Read the untrusted payload’s
destinationand resolve the tenant that connected that channel id. - Verify the
x-line-signatureHMAC-SHA256 header against that tenant’s stored channel secret. Requests that fail verification are rejected before any webhook fires. - Normalize each
message-type event into the canonical inbound envelope and forward it to the endpoint configured under Settings → Webhooks, signed with the Standard-Webhooks scheme.
destination at the top level — the channel id you copied in §1. Here 1654385910 is what resolves the tenant; replyToken and source.userId are LINE’s own ids, and the U… user id is what you send replies to:
destination resolved into your tenant:
U…) on the envelope is the to value for every outbound reply.
Delivery receipts from LINE advance the outbound lifecycle on a separate POST /api/v1/webhooks/dlr/line route that Orbit registers as part of connect — nothing for you to wire. Full envelope samples are on the channel page.
5. Verify the loop end to end
- From a LINE device, message your Official Account — confirm a
message.receivedevent lands on your webhook within seconds. - Reply through
POST /messages/lineusing the sender’sU…id from that event — confirm the message arrives in the chat. - Confirm the
message.deliveredevent arrives and that the message record in the dashboard moved throughqueued → delivered.
https://api.orbit.devotel.io/api/v1/webhooks/inbound/line. LINE also enforces that webhook URL validation responds on the same domain — if you rotate credentials, re-paste them in Orbit rather than re-pointing the URL.
6. Rotation and retirement
Rotating the access token from the LINE Developers Console does not break history — Orbit keys conversation history by channel id, not by token, and re-pasting the new token value under Channels → LINE preserves everything. Disconnecting the channel in the dashboard removes the credential row; inbound webhook traffic for that channel id then resolves to no tenant and is rejected, so retire it only after the Official Account has stopped receiving traffic.7. Error reference
A failed send returns a JSON error envelope whoseerror.code is one of the values below.
LINE_INVALID_TOKEN (401) is returned only at connect time, for an expired or invalid channel access token; it is never raised on the send path. The connect response that carries it — and the branch to write for it — is shown in §2.
Compliance scope
LINE does not impose platform-side template or quiet-hours gates on plain text, and Orbit does not either — any consent, opt-out, or timing policy you promise your LINE audience is enforced from your own tenant configuration (quiet hours, frequency caps, suppression lists), not by this channel’s connect path. Keep those controls enabled in production and follow them in your journey logic; this guide only wires the transport.Where to go next
- LINE channel page — message types, capability detail, pricing notes.
- Asia channels onboarding — LINE beside KakaoTalk, Viber, and WeChat.
- API recipes cookbook — the four-channel complement pack: the send → poll → webhook loop task by task, with curl and per-language SDK snippets, including the LINE send under the Asia-channels task.
- Inbound webhook debugging — tools for the receiver side on any channel.