# Send Line Client to your agent
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
## Fast path
- Download the package from Yavira.
- Extract it into a folder your agent can access.
- Paste one of the prompts below and point your agent at the extracted folder.
## Suggested prompts
### New install

```text
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete.
```
### Upgrade existing

```text
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run.
```
## Machine-readable fields
```json
{
  "schemaVersion": "1.0",
  "item": {
    "slug": "line-api",
    "name": "Line Client",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/2manslkh/line-api",
    "canonicalUrl": "https://clawhub.ai/2manslkh/line-api",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadUrl": "/downloads/line-api",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=line-api",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "packageFormat": "ZIP package",
    "primaryDoc": "SKILL.md",
    "includedAssets": [
      "SKILL.md"
    ],
    "downloadMode": "redirect",
    "sourceHealth": {
      "source": "tencent",
      "slug": "line-api",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-05-04T23:25:17.529Z",
      "expiresAt": "2026-05-11T23:25:17.529Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=line-api",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=line-api",
        "contentDisposition": "attachment; filename=\"line-api-0.2.1.zip\"",
        "redirectLocation": null,
        "bodySnippet": null,
        "slug": "line-api"
      },
      "scope": "item",
      "summary": "Item download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this item.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/line-api"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    }
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/line-api",
    "downloadUrl": "https://openagent3.xyz/downloads/line-api",
    "agentUrl": "https://openagent3.xyz/skills/line-api/agent",
    "manifestUrl": "https://openagent3.xyz/skills/line-api/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/line-api/agent.md"
  }
}
```
## Documentation

### LINE Client Skill

Full LINE messaging client via the Chrome extension gateway JSON API.

### Repo & Files

Repo: /data/workspace/line-client (github.com/2manslkh/line-api)
Main client: src/chrome_client.py → LineChromeClient
QR login: src/auth/qr_login.py → QRLogin
HMAC signer: src/hmac/signer.js (Node.js, auto-starts on port 18944)
Token storage: ~/.line-client/tokens.json
Certificate cache: ~/.line-client/sqr_cert
WASM files: lstm.wasm + lstmSandbox.js (required, in repo root)

### Quick Start

import json
from pathlib import Path
from src.chrome_client import LineChromeClient

tokens = json.loads((Path.home() / ".line-client" / "tokens.json").read_text())
client = LineChromeClient(auth_token=tokens["auth_token"])

# Send a message
client.send_message("U...", "Hello!")

# Get profile
profile = client.get_profile()

Tokens expire in ~7 days. If expired (APIError(10051)), re-run QR login.

### QR Login (Authentication)

QR login requires user interaction: scan QR on phone + enter PIN.

from src.hmac import HmacSigner
from src.auth.qr_login import QRLogin
import qrcode

signer = HmacSigner(mode="server")
login = QRLogin(signer)
result = login.run(
    on_qr=lambda url: send_qr_image_to_user(qrcode.make(url)),
    on_pin=lambda pin: send_pin_to_user_IMMEDIATELY(pin),  # TIME SENSITIVE!
    on_status=lambda msg: print(msg),
)
# result.auth_token, result.mid, result.refresh_token

Critical: The PIN must reach the user within ~60 seconds. Send it the instant on_pin fires.

### QR Login State Machine

createSession → session ID
createQrCode → callback URL (append ?secret={curve25519_pubkey}&e2eeVersion=1)
checkQrCodeVerified — poll until scan (uses X-Line-Session-ID, no origin header)
verifyCertificate — MUST be called even if it fails (required state transition!)
createPinCode → 6-digit PIN (skip if cert verified in step 4)
checkPinCodeVerified — poll until user enters PIN
qrCodeLoginV2 → JWT token + certificate + refresh token

### Server-Side Login Script

python scripts/qr_login_server.py /tmp/qr.png

Emits JSON events on stdout: {"event": "qr", "path": "...", "url": "..."}, {"event": "pin", "pin": "123456"}, {"event": "done", "mid": "U..."}.

### Contacts & Friends

MethodArgsDescriptionget_profile()—Get your own profile (displayName, mid, statusMessage, etc.)get_contact(mid)mid: strGet a single contact's profileget_contacts(mids)mids: list[str]Get multiple contactsget_all_contact_ids()—List all friend MIDsfind_contact_by_userid(userid)userid: strSearch by LINE IDfind_and_add_contact_by_mid(mid)mid: strAdd friend by MIDfind_contacts_by_phone(phones)phones: list[str]Search by phone numbersadd_friend_by_mid(mid)mid: strAdd friend (RelationService)get_blocked_contact_ids()—List blocked MIDsget_blocked_recommendation_ids()—List blocked recommendationsblock_contact(mid)mid: strBlock a contactunblock_contact(mid)mid: strUnblock a contactblock_recommendation(mid)mid: strBlock a friend suggestionupdate_contact_setting(mid, flag, value)mid, flag: int, value: strUpdate contact setting (e.g. mute)get_favorite_mids()—List favorited contact MIDsget_recommendation_ids()—List friend suggestions

### Messages

MethodArgsDescriptionsend_message(to, text, ...)to: str, text: str, reply_to: str (opt)Send a text message. Supports replies via reply_to=message_idunsend_message(message_id)message_id: strUnsend/delete a sent messageget_recent_messages(chat_id, count=50)chat_id: strGet latest messages in a chatget_previous_messages(chat_id, end_seq, count=50)chat_id, end_seq: intPaginated history (older messages)get_messages_by_ids(message_ids)message_ids: list[str]Fetch specific messagesget_message_boxes(count=50)—Get chat list with last message (inbox view)get_message_boxes_by_ids(chat_ids)chat_ids: list[str]Get specific chats with last messageget_message_read_range(chat_ids)chat_ids: list[str]Get read receipt infosend_chat_checked(chat_id, last_message_id)chat_id, last_message_id: strMark messages as readsend_chat_removed(chat_id, last_message_id)chat_id, last_message_id: strRemove chat from inboxsend_postback(to, postback_data)to, postback_data: strSend postback (bot interactions)

### Chats & Groups

MethodArgsDescriptionget_chats(chat_ids, with_members=True, with_invitees=True)chat_ids: list[str]Get chat/group detailsget_all_chat_mids()—List all chat MIDs (groups + invites)create_chat(name, target_mids)name: str, target_mids: list[str]Create a new group chataccept_chat_invitation(chat_id)chat_id: strAccept group invitereject_chat_invitation(chat_id)chat_id: strReject group inviteinvite_into_chat(chat_id, mids)chat_id: str, mids: list[str]Invite users to groupcancel_chat_invitation(chat_id, mids)chat_id: str, mids: list[str]Cancel pending invitesdelete_other_from_chat(chat_id, mids)chat_id: str, mids: list[str]Kick members from groupleave_chat(chat_id)chat_id: strLeave a group chatupdate_chat(chat_id, updates)chat_id: str, updates: dictUpdate group name/settingsset_chat_hidden_status(chat_id, hidden)chat_id: str, hidden: boolArchive/unarchive a chatget_rooms(room_ids)room_ids: list[str]Get legacy room infoinvite_into_room(room_id, mids)room_id: str, mids: list[str]Invite to legacy roomleave_room(room_id)room_id: strLeave legacy room

### Reactions

MethodArgsDescriptionreact(message_id, reaction_type)message_id: str, type: intReact to a message. Types: 2=like, 3=love, 4=laugh, 5=surprised, 6=sad, 7=angrycancel_reaction(message_id)message_id: strRemove your reaction

### Profile & Settings

MethodArgsDescriptionupdate_profile_attributes(attr, value, meta={})attr: int, value: strUpdate profile. Attrs: 2=DISPLAY_NAME, 16=STATUS_MESSAGE, 4=PICTURE_STATUSupdate_status_message(message)message: strShortcut: update status messageupdate_display_name(name)name: strShortcut: update display nameget_settings()—Get all account settingsget_settings_attributes(attr_bitset)attr_bitset: intGet specific settingsupdate_settings_attributes(attr_bitset, settings)attr_bitset: int, settings: dictUpdate settings

### Polling & Events

MethodArgsDescriptionget_last_op_revision()—Get latest operation revision numberfetch_ops(count=50)—Fetch pending operations (may long-poll)poll()—Generator yielding operations as they arriveon_message(handler)handler: Callable(msg, client)Start polling thread, calls handler on new messages. Op types: 26=SEND_MESSAGE, 27=RECEIVE_MESSAGEstop()—Stop the polling thread

### Other Services

MethodArgsDescriptionget_server_time()—Get LINE server timestampget_configurations()—Get server configurationsget_rsa_key_info()—Get RSA key for authissue_channel_token(channel_id)channel_id: strIssue channel token (LINE Login/LIFF)get_buddy_detail(mid)mid: strGet official account inforeport_abuse(mid, category=0, reason="")mid: strReport a useradd_friend_by_mid(mid)mid: strAdd friend (RelationService)logout()—Logout and invalidate token

### MID Format

LINE identifies entities by MID:

U... or u... → User (toType=0)
C... or c... → Group chat (toType=2)
R... or r... → Room (toType=1)

The client auto-detects toType from the MID prefix when sending messages.

### HMAC Signing

All API calls require X-Hmac header. The WASM signer handles this automatically:

Derives key from version "3.7.1" + access token via proprietary KDF (in lstm.wasm)
Signs path + body → base64 → X-Hmac
Server mode: ~13ms/sign (Node.js HTTP server on port 18944, auto-started)
Subprocess mode: ~2s/sign (fallback)

### Error Handling

from src.chrome_client import APIError

try:
    client.send_message(mid, "test")
except APIError as e:
    print(e.code, e.api_message)
    # 10051 = session expired / invalid
    # 10052 = HTTP error from backend
    # 10102 = invalid arguments

### Architecture

User's Phone (LINE app)
    ↕ (scan QR / enter PIN)
LINE Servers (line-chrome-gw.line-apps.com)
    ↕ (JSON REST + X-Hmac signing)
LineChromeClient (this repo)
    ↕ (WASM HMAC via Node.js signer)
lstm.wasm + lstmSandbox.js

The Chrome Gateway translates JSON ↔ Thrift internally. We never deal with Thrift binary — everything is clean JSON.
## Trust
- Source: tencent
- Verification: Indexed source record
- Publisher: 2manslkh
- Version: 0.2.1
## Source health
- Status: healthy
- Item download looks usable.
- Yavira can redirect you to the upstream package for this item.
- Health scope: item
- Reason: direct_download_ok
- Checked at: 2026-05-04T23:25:17.529Z
- Expires at: 2026-05-11T23:25:17.529Z
- Recommended action: Download for OpenClaw
## Links
- [Detail page](https://openagent3.xyz/skills/line-api)
- [Send to Agent page](https://openagent3.xyz/skills/line-api/agent)
- [JSON manifest](https://openagent3.xyz/skills/line-api/agent.json)
- [Markdown brief](https://openagent3.xyz/skills/line-api/agent.md)
- [Download page](https://openagent3.xyz/downloads/line-api)