Library / SDK
line/line-bot-sdk-php avatar
line/line-bot-sdk-php

line-bot-sdk-php: the official PHP SDK for the LINE Messaging API

LINE Messaging API SDK for PHP

734 stars642 forksPHPApache-2.0

At a glance

What is it?
A Composer package that turns the LINE Messaging API into typed request objects, one client class per API family, and exceptions that carry the request id headers you need for support tickets. It is generated from the platform's OpenAPI description, which is why it tracks new endpoints so closely.
Who is it for?
Adopt line-bot-sdk-php if you are building a LINE bot in PHP, since the request objects, the exception classes and the webhook parser are the parts you would otherwise write against a moving API surface. Skip it if your language is not PHP, since this SDK exists for one runtime.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

An SDK that tracks a platform that ships new endpoints every month

The LINE Messaging API is the interface a bot uses to talk to LINE. It covers push and reply messages, rich menus, bot info, broadcast and narrowcast, quota consumption and a set of webhook event types, and it grows regularly. The release history of this SDK shows the tempo: v12.8.0 added support for templated LINE notification messages and custom aggregation units, v12.7.0 added support for the Message Edited webhook, and v12.6.0 added support for the rich menu summary API and the daily report API.

That cadence is the whole reason this package exists in the form it does. Writing raw HTTP calls against that surface means every new endpoint is a new hand written request class, every new response shape is a new manual array access, and every error is a generic HTTP exception with no structure.

The SDK states its purpose plainly: it makes it easy to develop bots using the LINE Messaging API, and you can create a sample bot within minutes. The homepage is the Packagist page for linecorp/line-bot-sdk, and the licence is Apache-2.0, with a copyright line dating it to 2016.

The requirement is PHP 8.2 or later, which is a fairly recent floor and tells you the package is not maintaining compatibility with older runtimes.

The client is a Configuration object plus one Api class

The wiring is short. You create a Guzzle HTTP client, create a Configuration object, set the access token on it, and pass both into the API class:

php
$client = new \GuzzleHttp\Client();
$config = new \LINE\Clients\MessagingApi\Configuration();
$config->setAccessToken('<channel access token>');
$messagingApi = new \LINE\Clients\MessagingApi\Api\MessagingApiApi(
  client: $client,
  config: $config,
);

Two things about that shape are worth pointing out.

The first is that the HTTP client is injected rather than created internally. The documentation states you must use the Client with a GuzzleHttp\ClientInterface implementation, and the constructor takes named arguments, client and config. That means you can supply a client with your own middleware, your own timeout, your own retry behaviour, or your own mock in a test. You are not stuck with whatever transport the SDK picked.

The second is that credentials live on a Configuration object rather than being passed to every call. The access token is set once, and the API class holds the pair of the two objects for the lifetime of your application.

The namespacing tells you the shape of the whole library. Everything lives under \LINE\Clients\MessagingApi, with Api\MessagingApiApi as the entry point, Configuration as the settings holder, and ApiException as the error type. The PHPDoc for the package is published at line.github.io/line-bot-sdk-php.

The docs also point at the official API documentation in English and Japanese, which is where the semantics of each endpoint live, as opposed to the SDK which encodes the request and response shapes.

Two ways to build a request, and both are typed

Every API call takes a request object, and the SDK offers two construction styles for it.

The array style passes everything to the constructor:

php
$message = new TextMessage(['text' => 'hello!']);
$request = new ReplyMessageRequest([
    'replyToken' => '<reply token>',
    'messages' => [$message],
]);
$response = $messagingApi->replyMessage($request);

The setter style is the alternative, and it is what you want when you build the object across several lines of application logic:

php
$message = (new TextMessage())
  ->setText('hello!');
$request = (new ReplyMessageRequest)
  ->setReplyToken('<reply token>')
  ->setMessages([$message]);
try {
  $messagingApi->replyMessage($request);
  // Success
} catch (\LINE\Clients\MessagingApi\ApiException $e) {
  // Failed
  echo $e->getCode() . ' ' . $e->getResponseBody();
}

Both styles produce the same typed object, and the class names map onto the API concepts: TextMessage for the payload, ReplyMessageRequest for the call that answers a user action using the reply token.

The setter style is not cosmetic. Because the setters return the object, the fluent chain above reads as one expression, and because the properties are typed, a wrong message type is caught by the language rather than by the API returning a validation error.

The exception handling in that snippet is also the model for the rest of the SDK. A failed call throws ApiException, and the exception carries the HTTP status code and the response body, so you can log the reason the API rejected the request rather than only knowing that it did.

The WithHttpInfo variants give you the headers, which is what support asks for

Every API method appears in two forms, and the second one exists for a specific operational reason.

The documentation says you may need to store the x-line-request-id header obtained as a response from several APIs. When you call the WithHttpInfo variant, you get the body, the status code and the headers:

php
$request = new ReplyMessageRequest([
    'replyToken' => $replyToken,
    'messages' => [$textMessage = (new TextMessage(['text' => 'reply with http info']))],
]);
$response = $messagingApi->replyMessageWithHttpInfo($request);
$this->logger->info('body:' . $response[0]);
$this->logger->info('http status code:' . $response[1]);
$this->logger->info('headers(x-line-request-id):' . $response[2]['x-line-request-id'][0]);

The return value is a three element array, body, status code, headers. The x-line-accepted-request-id and content-type headers can also be obtained in the same way.

Why this matters: when you open a support case with the platform, the request id is what lets them find your call in their logs. If you use the plain method, the id is discarded. The WithHttpInfo variants are therefore the difference between a debuggable integration and one that produces an anonymous error report.

The error path provides the same information. The exception exposes getResponseHeaders, getCode and getResponseBody:

php
try {
    $profile = $messagingApi->getProfile("invalid-userId");
} catch (\LINE\Clients\MessagingApi\ApiException $e) {
    $headers = $e->getResponseHeaders();
    $lineRequestId = isset($headers['x-line-request-id']) ? $headers['x-line-request-id'][0] : 'Not Available';
    $httpStatusCode = $e->getCode();
    $errorMessage = $e->getResponseBody();

The x-line-accepted-request-id header is available from the error response in the same way, as $headers['x-line-accepted-request-id'][0]. Each client defines its own exception class, so a catch block targeting the Messaging API exception does not accidentally swallow an error from another client.

Webhook handling is three steps and one parser

The inbound direction is where a bot actually lives, and the SDK keeps it to a small amount of code.

The mechanism is described in three steps. Receive the webhook from LINE's server, parse the request payload by EventRequestParser#parseEventRequest($body, $channelSecret, $signature), then iterate the parsed events and react as you like.

That middle step is the security boundary and it is worth being precise about. The parser takes the raw body, the channel secret and the signature, which means the SDK performs the signature verification itself using the channel secret you hold. You are not expected to validate the request by hand, and you should not skip the signature check because the SDK is doing it.

The event model follows from the platform's semantics. LINE's server sends user actions, such as a message, an image or a location, to your bot server. A request contains one or more events, and an event is an action of the user. So a single webhook delivery can carry several events, and your reaction logic has to be written per event type rather than per request.

Two worked examples are included in the repository. EchoBot is a simple sample implementation that reacts to text messages sent from users. KitchenSink is a full stack and slightly complex sample that demonstrates a practical use of the Messaging API.

For a first-time user, the project documentation recommends starting with the examples directory and the PHPDoc of the \LINE namespace, which is a reasonable order: the examples show the shape of a bot, and the PHPDoc tells you what each generated class contains.

The code is generated, and you can see the generator in the tree

The repository layout reveals how the SDK is produced, and it is the reason the request and response classes are so consistent.

Alongside the ordinary src, test and examples directories there is a line-openapi entry, a generator directory, a generate-code.py script, and a tools directory. In other words, the API description and the code generator are part of the repository rather than being an internal process.

That has two consequences for you as a user. The first is coverage: when the platform adds an endpoint, the SDK can add the corresponding class rather than only adding a method to an existing one, which is why new API families such as the rich menu summary API arrive as their own classes in a minor version. The second is consistency: every request class has the same constructor and setter shape, every method has the WithHttpInfo twin, and every client has its own exception class, because a generator produced them.

The quality tooling is visible too. The tree carries phpcs.xml for coding standards, phpstan.neon.dist for static analysis, phpmd.xml for mess detection, phpunit.xml for tests, a phpdoc.dist.xml for the documentation build, and a renovate.json5 for dependency updates. The build status badge points at a workflow named php-checks.yml, so those checks run in continuous integration rather than as a suggestion.

The project respects semantic versioning, with one documented exception: if a feature that was publicly released is discontinued for business reasons and becomes completely unusable, they will release the changes as a patch release. That is worth knowing before you write automation that assumes a major version bump always signals a breaking change.

Laravel support, and the generated docs you can run locally

Two conveniences round out the package, and both are optional.

The first is Laravel support. The documentation says it is easy to use from Laravel, and the setup is to add LINE_BOT_CHANNEL_ACCESS_TOKEN to your .env file:

code
LINE_BOT_CHANNEL_ACCESS_TOKEN=<Channel Access Token>

Then you can use facades like $profile = \LINEMessagingApi::pushMessage(....). The facade uses \GuzzleHttp\Client by default, and if you want to change the configuration you run php artisan vendor:publish with the LINEBotServiceProvider provider and the config tag, after which line-bot.php is published to the config directory. The published file shows the full configuration shape:

php
return [
    'channel_access_token' => env('LINE_BOT_CHANNEL_ACCESS_TOKEN'),
    'channel_id' => env('LINE_BOT_CHANNEL_ID'),
    'channel_secret' => env('LINE_BOT_CHANNEL_SECRET'),
    'client' => [
        'config' => [
          'headers' => ['X-Foo' => 'Bar'],
        ],
    ],
];

The second is documentation. The package ships PHPDoc describing how to use the methods, and you can generate the site locally with two commands:

bash
composer docs
php -S 127.0.0.1:8000 -t build/site

The first builds the site, the second serves it, which means you can read the documentation for the exact version you have installed rather than for whatever is current on the hosted site.

For a team that pins a specific SDK version, that is a small thing that removes a real source of confusion when a new release changes a class name.

Where this SDK is the wrong choice

There are limits that follow from what the package is, and one that follows from what it is not.

The first is the language. This is a PHP SDK. If your bot runs on Node, Python, Ruby, Go or anything else, you need the SDK for that runtime, or you write the HTTP calls yourself. Nothing here is reusable across languages, since the value of the package is the typed request classes and the generated mapping, and both are PHP.

The second is the PHP floor. PHP 8.2 or later is required, so an application pinned to PHP 7.x cannot adopt this version. Given that the package's own major versions track API features rather than PHP versions, the upgrade path for an older application is a decision about the platform side.

The third is that the SDK does not remove the platform's constraints. Reply tokens are short lived, so a reply based workflow has a narrow window, and the push path has quota limits and a per message cost that the platform accounts for separately. A generated client class does not change any of that, and it is worth remembering that a bot which replies correctly in a test can still fail in production because the token expired.

The fourth is the wrapper layer. The HTTP client is Guzzle, so the package brings that dependency in, and a Laravel application using the facade gets a second configuration path to keep in sync with the core one. If you already have a strict policy about which HTTP client a service uses, that is a real constraint.

The fifth is that a version of the SDK cannot call an endpoint the SDK does not know about. If you need a very new endpoint immediately, the generated classes may not exist yet, and you are back to raw HTTP for that one call.

Editorial conclusion

Adopt line-bot-sdk-php if you are building a LINE bot in PHP, since the request objects, the exception classes and the webhook parser are the parts you would otherwise write against a moving API surface. Skip it if your language is not PHP, since this SDK exists for one runtime. Verify first that you are on PHP 8.2 or later, that your channel access token is set through Configuration rather than hardcoded, and that the generated classes in the version you pin include the endpoints you need, since a major version is where new API families appear.

Frequently asked questions

How do I install the LINE Messaging API SDK for PHP?

Install it with Composer using composer require linecorp/line-bot-sdk. The package requires PHP 8.2 or later, and it is published on Packagist as linecorp/line-bot-sdk.

How do I create the bot client instance in line-bot-sdk-php?

Create a GuzzleHttp\Client, a \LINE\Clients\MessagingApi\Configuration with the access token set on it, and pass both into a new \LINE\Clients\MessagingApi\Api\MessagingApiApi. The client must implement GuzzleHttp\ClientInterface.

How do I send a reply message with the LINE PHP SDK?

Build a TextMessage, put it into a ReplyMessageRequest with the reply token, and pass the request to replyMessage on the messagingApi instance. Both the array constructor style and the fluent setter style are supported.

How do I get the x-line-request-id header from a LINE API call?

Use the WithHttpInfo variant of the method, for example replyMessageWithHttpInfo, which returns the body, the status code and the headers as a three element array. On an error, the ApiException exposes getResponseHeaders, and the accepted request id is at $headers['x-line-accepted-request-id'][0].

How is a LINE webhook parsed in PHP?

Receive the webhook, then parse it with EventRequestParser#parseEventRequest($body, $channelSecret, $signature), and iterate the parsed events. The signature check is performed by the parser using the channel secret.

Does the LINE PHP SDK work with Laravel?

Yes. Add LINE_BOT_CHANNEL_ACCESS_TOKEN to your .env file and use the facades. To change the configuration, publish it with the LINE\Laravel\LINEBotServiceProvider provider and the config tag, which writes line-bot.php into your config directory.

Official sources

  1. License: Apache-2.0
  2. line/line-bot-sdk-php on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/line-line-bot-sdk-php.svg)](https://hysenlabs.com/projects/line-line-bot-sdk-php)

Was this useful?