業務システム

LINE WORKS APIの使い方:JWT認証・Bot送信・Rate Limitを実装コードで解説

LINE WORKS APIの使い方:JWT認証・Bot送信・Rate Limitを実装コードで解説

LINE WORKS APIは、Botからのメッセージ送信やメンバー・組織情報の取得を、外部のシステムからHTTPで行うための公開APIです。この記事では、サーバー間連携で使うService Account認証(JWT)でアクセストークンを取るPythonコードから始め、Botからユーザーへの送信、Callbackの署名検証、メンバー一覧の全件取得までを順に実装します。後半で扱うのは、フリープランと有料プランで4倍違うRate Limit、ドメイン全体で5本までの同時接続といった本番で詰まる上限と、API連携を自社で作るか任せるかの判断です。Webhook URLへ通知を投げるだけなら、LINE WORKS Incoming Webhookの使い方を解説した記事の方法で足ります。

まとめ:LINE WORKS APIはJWT認証とBot送信の2つから始める

LINE WORKS API 2.0で最初に作るべき部品は2つだけです。Private KeyでJWTを署名してアクセストークンを取る関数と、そのトークンでBotからメッセージを送る関数。業務システムからの承認通知も、問い合わせ対応のBotも、この2つの上に組み立てます。

本番で効いてくるのは字面のAPI仕様より上限値です。1 APIあたり毎分60回(フリープラン)または240回(有料プラン)、ドメイン内の全APIを合わせて同時5本という枠を超えると429が返ります。全社員への一斉送信や人事マスタの夜間同期を組むなら、待ち合わせと再送を最初から設計に入れてください。

LINE WORKS API 2.0でできることとIncoming Webhookとの違い

LINE WORKSから外部のシステムとつなぐ入口は、Incoming WebhookとAPI 2.0の2系統あります。前者は投稿専用、後者は読み書きの両方を扱う汎用APIです。

2022年4月提供開始のAPI 2.0とOAuth 2.0・Cursor型ページング

現行のAPIは、ワークスモバイルジャパンが2022年4月4日に提供開始を発表したAPI 2.0です。発表時の特徴として、OAuth 2.0に準拠した認可、Scopeによる権限の付与、Cursor型のページング、全APIへのRate Limit設定が挙げられていました。旧API 1.0を前提に書かれた記事やサンプルは認証の作法から違うため、2022年より前の情報はそのまま写さないほうが安全です。

呼び出し先のホストは2つに分かれます。トークンの発行は auth.worksmobile.com、Bot送信やユーザー取得などの業務APIは www.worksapis.com/v1.0 です。

Bot・ユーザー・組織・カレンダーなど業務連携で触る主要API

OAuth Scopesの一覧を見ると、業務システム連携でよく使うのは次の範囲です。

  • bot.message:Botのメッセージ送受信、リッチメニュー・固定メニュー、トークルームの操作
  • user.read:メンバー一覧と、名前・メールアドレス・組織などのメンバー情報の取得
  • directory.read:メンバーに加えて組織・役職・グループなどの組織情報の取得
  • calendar:カレンダーの予定の取得・作成・編集・削除

実務ではまず bot.message と user.read の2つで足ります。承認結果をBotで本人に届け、送り先のユーザーIDを人事システムの社員番号と突き合わせる、という構成がもっとも多いからです。

Webhookで足りる一方向通知とAPIが要る双方向連携の境目

Incoming Webhookは、発行したURLへJSONをPOSTすればトークルームに投稿される仕組みで、認証の実装が要りません。監視アラートやバッチの完了通知のように、送りっぱなしで済む用途はこちらが手軽です。

APIが必要になるのは、個人宛てに送り分けたいとき、メンバーの返信やボタン操作を受け取って業務システム側で処理したいとき、メンバー情報を読み出したいときです。どれか1つでも当てはまるなら、Webhookで粘らずにAPI 2.0で作り始めてください。

Developer ConsoleでアプリとService Accountを準備する手順

APIを呼ぶ前に、LINE WORKSのDeveloper Consoleでアプリを登録し、認証に使う値をそろえます。

サーバー間連携の認証に必要な識別情報と秘密鍵の4点を準備する手順

サーバー間連携で控える値は4つです。アプリのClient IDとClient Secret、そのアプリで発行するService Account ID、Service Accountと一緒にダウンロードするPrivate Key。Service Account認証の仕様によると、Service Accountは1アプリにつき1つだけ発行でき、発行時に管理者権限が付き、課金対象のユーザーには数えられません。

管理者権限を持つ点は軽く見ないでください。Private Keyが漏れると、付与したScopeの範囲でドメイン全体を読み書きされます。鍵はリポジトリに置かず、シークレット管理サービスか、権限を絞ったファイルから読み込む形にします。

必要なScopeだけを付けるbot.messageとuser.readの選び方

Scopeはアプリに必要なものだけを付けるよう公式が推奨しています。Botの送信だけなら、全Bot APIに効く bot ではなく bot.message が選択対象です。Bot APIの概要も、Botの作成自体をAPIで行う場合を除き bot.message の利用を勧めています。

Service Accountで使えないScopeがある点も先に確かめておきます。仕様には、mail・file・task・form・group.note・ainote などに対応する一部のAPIはService Accountからは呼べないとあり、パスの {userId} に me も指定できません。メールやタスクを扱う連携を考えているなら、ユーザーの認可を得るUser Account認証の設計に切り替える必要があります。

JWTを作ってアクセストークンを取得するPythonコードの実例

Service Account認証では、Private KeyでJWTに署名し、それをトークンエンドポイントへ渡してアクセストークンに換えます。

RS256で署名するJWTのクレームとiat・expの60分ルール

JWTのヘッダは alg がRS256、typ がJWTです。クレームには、iss にClient ID、sub にService Account ID、iat に発行時刻、exp に満了時刻を入れます。exp は iat から最長60分で、どちらもUNIX時間の秒(10桁)で書く決まりです。ミリ秒の13桁を入れると弾かれるので、JavaScriptで Date.now() をそのまま使うと失敗します。

次のコードは、PyJWTとrequestsでトークンを取る最小の関数です。秘密の値はすべて環境変数から読みます。

# pip install "pyjwt[crypto]" requests
import os
import time

import jwt
import requests

TOKEN_URL = "https://auth.worksmobile.com/oauth2/v2.0/token"


def get_access_token(scope: str = "bot.message user.read") -> str:
    now = int(time.time())  # 秒単位(10桁)。固定値は使わない
    claims = {
        "iss": os.environ["LW_CLIENT_ID"],
        "sub": os.environ["LW_SERVICE_ACCOUNT_ID"],
        "iat": now,
        "exp": now + 3600,  # iat から最長60分
    }
    with open(os.environ["LW_PRIVATE_KEY_PATH"], "rb") as f:
        assertion = jwt.encode(claims, f.read(), algorithm="RS256")

    res = requests.post(
        TOKEN_URL,
        data={
            "assertion": assertion,
            "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
            "client_id": os.environ["LW_CLIENT_ID"],
            "client_secret": os.environ["LW_CLIENT_SECRET"],
            "scope": scope,  # 複数は半角スペース区切り
        },
        timeout=10,
    )
    res.raise_for_status()
    return res.json()["access_token"]

PyJWTはヘッダに typ: JWT を自動で入れるため、ヘッダを手で組む必要はありません。requests の data= に辞書を渡すと、仕様が指定する application/x-www-form-urlencoded で送られます。

トークンエンドポイントへのPOSTと有効期限1時間・24時間の設定

返ってくるJSONには access_token と refresh_token、expires_in が入ります。アクセストークンの有効期限は、Developer Consoleの設定に応じて1時間(3600秒)か24時間(86400秒)、リフレッシュトークンは90日です。

Service Accountの場合、リフレッシュトークンを保管して回すより、期限切れが近づいたらJWTを作り直して新しいトークンを取るほうが単純です。リクエストのたびにトークンを取り直す実装は、トークン発行のAPI呼び出しを無駄に増やします。expires_in から5分ほど手前を期限としてメモリにキャッシュし、401が返ったときだけ取り直す形にしてください。Authorizationヘッダの書き方そのものはBearer Tokenの書き方とJWTとの違いを扱った記事で詳しく説明しています。

Botからユーザーとトークルームへメッセージを送る実装と確認手順

トークンが取れたら、Botから実際にメッセージを送ります。送信先が個人か複数人のトークルームかで、呼ぶAPIが変わります。

usersとchannelsの2つの送信APIと201応答・2,000字の上限

個人宛ては POST /bots/{botId}/users/{userId}/messages、トークルーム宛ては POST /bots/{botId}/channels/{channelId}/messages です。ユーザーへのメッセージ送信の仕様では、userId にユーザーIDかログインIDを指定でき、ログインIDを使う場合はURLエンコードが要ります。成功時の応答は200ではなく201なので、ステータスを200で判定しているとすべて失敗扱いになります。

curl -X POST "https://www.worksapis.com/v1.0/bots/${LW_BOT_ID}/users/${LW_USER_ID}/messages" \
  -H "Authorization: Bearer ${LW_ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"content": {"type": "text", "text": "経費申請(No.1024)が承認されました"}}'

テキストメッセージの仕様では、text の上限は2,000字です。日報の全文や長いエラーログを流し込む連携では、要約か分割の処理を送信前に入れておきます。i18nTexts を使えば、利用者の言語設定ごとに文面を出し分けることもできます。

Callbackを受けるサーバーでX-WORKS-Signatureを検証するコード

メンバーがBotに送ったメッセージは、Botに設定したCallback URLへPOSTで届きます。Callbackの仕様では、ヘッダの X-WORKS-Signature に、Bot Secretを鍵としてリクエストボディをHMAC-SHA256で計算しBase64にした値が入ります。改ざんやなりすましのリクエストを捨てるには、イベントを処理する前に必ず照合する必要があるためです。

import base64
import hashlib
import hmac


def is_valid_signature(raw_body: bytes, signature: str, bot_secret: str) -> bool:
    digest = hmac.new(bot_secret.encode("utf-8"), raw_body, hashlib.sha256).digest()
    expected = base64.b64encode(digest).decode("utf-8")
    return hmac.compare_digest(expected, signature)

照合には、JSONとして読み直す前の生のバイト列を使ってください。フレームワークがボディをパースして整形し直すと、1バイトでも違えば一致しません。仕様は応答として200を返すことを求め、イベント処理の非同期化を勧めています。受信したらキューに積んで即座に200を返し、業務処理は別のワーカーで行う構成が安全です。署名検証と冪等性の一般論はWebhook受信側の署名検証と冪等性の実装記事にまとめています。

業務システムからメンバー一覧を同期するユーザー取得と全件処理の実装

Botで個人宛てに送るには、送り先のLINE WORKSユーザーIDが要ります。人事システムの社員とLINE WORKSのメンバーを突き合わせるため、ユーザー一覧を定期的に取り込む処理を作ります。

GET /usersのcount上限100とnextCursorで全件を取るループ

ユーザーリストの取得は GET /users で、1回の count は既定・最大とも100件です。続きは応答の responseMetaData.nextCursor を次の cursor に渡して取ります。

import time

import requests

USERS_URL = "https://www.worksapis.com/v1.0/users"


def iter_all_users(token: str):
    params = {"count": 100}
    while True:
        res = requests.get(
            USERS_URL,
            headers={"Authorization": f"Bearer {token}"},
            params=params,
            timeout=10,
        )
        if res.status_code == 429:
            # 上限超過。リセットまでの秒数が無ければ1分待つ
            time.sleep(int(res.headers.get("RateLimit-Reset", "60")))
            continue
        res.raise_for_status()
        body = res.json()
        yield from body.get("users", [])
        cursor = (body.get("responseMetaData") or {}).get("nextCursor")
        if not cursor:
            break
        params["cursor"] = cursor  # requests が URL エンコードする

社員1,000人のドメインなら10回の呼び出しで終わり、毎分の上限には届きません。それでも429の分岐を最初から書いておくのは、同じドメインの別のアプリやバッチと上限を取り合う場面があるからです。

customFieldsの2027年4月廃止予定とcustomPropertiesへ移行

社員番号のような独自項目をLINE WORKS側に持たせて突き合わせる場合は注意が要ります。ユーザーリストの仕様には、customFields が2027年4月上旬を目処に廃止予定で、今後は customProperties を使うよう書かれています(2026年11月時点)。

これから作る連携は最初から customProperties を読み、既存の連携で customFields を参照しているなら、廃止の前に読み出し箇所を差し替えておきます。社員番号での突き合わせが壊れると、承認通知が別人に届くか、誰にも届かなくなります。

Rate Limitと同時接続5本の上限を踏まえた本番運用の設計

開発中は気づかず、全社展開の初日に表面化しやすいのが上限値です。

フリープラン60回・有料プラン240回の上限と429応答の扱い

API使用の上限によると、LINE WORKSサービスのRate Limitは1 APIあたり、フリープランで毎分60回、スタンダード・アドバンストで毎分240回です。超過すると429と TOO_MANY_REQUESTS が返り、最長1分でリセットされます。トークルームの登録は毎分10回、監査ログ系は毎分10回と、API固有のより厳しい枠もあります。

もう1つの枠が同時接続で、ドメイン内の全APIを合わせて最大5スレッドです。超えると同じ429で Concurrent access fail が返ります。全社員1,000人に個別通知を送るバッチを20並列で回す、といった作りはここで止まります。

RateLimit-Remainingヘッダで送信間隔を決める実装の要点

応答ヘッダには RateLimit-Limit・RateLimit-Remaining・RateLimit-Reset などが入ります。大量送信では、残り回数が少なくなったらリセットまで待つ制御を送信側に入れ、並列数は5未満に抑えます。仕様はヘッダ名の大文字小文字を区別せずに扱うよう求めているので、辞書の完全一致で読まないでください。

実務での優先順位ははっきりしています。まず並列数を3〜4に固定し、次に429を受けたらRateLimit-Resetだけ待って同じリクエストを1回やり直す。この2つで大半の業務連携は回ります。キューと送信の監視まで作り込むのは、毎日数千通を送る規模になってからで十分です。

LINE WORKS API連携を自社で作る場面と外部に任せる場面

ここまでのコードは数十行です。それでも、作るべき連携と作らなくてよい連携ははっきり分かれます。

通知だけ・1部署だけならWebhookかiPaaSで済ませる条件

送る内容が定型の通知だけで、利用者が1部署にとどまるなら、APIで連携を作るのは過剰です。Incoming Webhookで投稿するか、iPaaSのLINE WORKS連携を使えば、トークンの管理もサーバーの運用も要りません。iPaaSと受託開発の使い分けはAPI連携ツールの選び方を比較した記事で整理しています。

見送るべき失敗パターンもあります。返信を受けたいという理由だけでCallbackサーバーを立て、結局は誰も返信しないまま、Private Keyと公開サーバーの管理だけが残る。この形になりそうなら、最初は一方向の通知で始めてください。

人事マスタ同期や承認フローまで組むなら連携設計から相談する理由

反対に、人事システムとメンバー情報を突き合わせて個人宛てに送る、承認ボタンの操作を受けて業務システムの状態を変える、といった連携は自社で設計する価値があります。ここで問われるのはAPIの呼び方より、社員番号の紐付けの持ち方、Private Keyの保管、429と同時接続5本を前提にした送信の設計、Callbackの冪等性です。

Service Accountは管理者権限を持つため、設計の甘さはそのまま情報漏えいの経路になります。既存の基幹システムや勤怠・ワークフローとLINE WORKSをつなぐ構成を検討しているなら、一創のAPI開発・システム連携で、認証方式の選定から送信基盤の設計までご相談いただけます。

LINE WORKS APIの料金・認証・旧版移行でよくある質問

LINE WORKS APIの料金や、LINEのMessaging APIとの違いなど、実装の前によく出る質問に答えます。

LINE WORKS APIの利用に料金はかかりますか?

APIの呼び出しごとに課金される料金項目は、LINE WORKSの利用料金ページにはありません(2026年11月時点)。費用はプランのユーザー単価で決まり、税抜でスタンダードが年額契約450円・月額契約540円、アドバンストが800円・960円(いずれも1ユーザー・月)です。Service Accountは課金対象に数えられません。プランで変わるのはRate Limitで、フリープランは毎分60回、有料プランは毎分240回です。

フリープランでもBotやAPIを試せますか?

試せます。Rate Limitの一覧にはフリープランの値として1 APIあたり毎分60回が定められており、Botの送信やユーザー取得の動作確認には十分な枠です。ただし料金ページの開発者機能の欄には制限ありの表記もあるため、本番で使うAPIがフリープランで呼べるかは、Developer Consoleで実際にアプリを作って確かめてから設計を固めてください。

LINEのMessaging APIとLINE WORKS APIは同じものですか?

別の製品のAPIです。LINEのMessaging APIは、企業のLINE公式アカウントから個人のLINEユーザーへ送るためのもので、送信通数に応じた料金体系があります。LINE WORKS APIは、社内のLINE WORKSドメインのメンバーへBotから送るためのもので、認証もエンドポイントも異なります。顧客向けの配信ならLINE Messaging APIの料金と実装手順の記事を参照してください。

アクセストークンが401で弾かれるときは何を確認しますか?

まずJWTの iat と exp を確認します。ミリ秒の13桁になっていないか、exp が iat から60分を超えていないか、サーバーの時計がずれていないか。次に、トークン取得時に指定した scope に、呼んでいるAPIの必要Scopeが含まれているかを見ます。bot.message だけで取ったトークンでユーザー一覧を呼ぶと、401ではなく権限不足のエラーになることもあるので、応答ボディのエラー内容まで読んで切り分けてください。

API 1.0で作った古い連携はそのまま動きますか?

API 2.0は認証がOAuth 2.0準拠に変わり、リソースIDやページングの方式も改められました。1.0の時代に書かれたサンプルコードは、トークンの取り方から書き直しが要ると考えてください。社内に古い連携が残っているなら、使っているAPIの一覧を出し、2.0の対応APIとScopeに1本ずつ置き換える計画を立てます。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.03 テックブログ クラブツーリズム不正アクセス:約432万件の対象確認と偽メールの見分け方・流出検知の設計
  2. 2026.11.04 テックブログ CVE-2026-119977:WordPress更新中のRCEと安全な更新手順
  3. 2026.10.29 テックブログ ソニー生命の情報漏洩38.5万件|ログインできない理由、再開状況と窓口の電話番号
  4. 2026.11.03 テックブログ ノジマ不正アクセスで約700万件漏えい:対象確認・窓口と乗っ取り注文を止める実装
  5. 2026.11.02 テックブログ アララの不正アクセス:メール約7,700万件の持ち出しと契約企業が行うAPIキー再設定・代替送信・連名報告

RELATED POSTS 関連記事

目次