Table of Contents

Sending Messages

MessagingClient is the facade for the Messaging API. It unifies two Kiota clients — the control plane (api.line.me, for sending and most operations) and the data plane (api-data.line.me, for binary content) — so you do not have to think about which host a call targets.

using Line.OpenApi.Messaging;
using Line.OpenApi.Messaging.Generated.Api.Models;

var client = MessagingClient.CreateWithStaticToken("CHANNEL_ACCESS_TOKEN");
  • client.Api — control-plane builders (MessagingApiClient).
  • client.Blob — data-plane builders (MessagingBlobApiClient).

Push a message

await client.Api.V2.Bot.Message.Push.PostAsync(new PushMessageRequest
{
    To = "U0123456789abcdef...",
    Messages = new()
    {
        new TextMessage { Text = "Hello, world" },
    },
});

Reply to an event

Use the reply token you received from a webhook event (see Receiving Webhooks):

await client.Api.V2.Bot.Message.Reply.PostAsync(new ReplyMessageRequest
{
    ReplyToken = replyToken,
    Messages = new() { new TextMessage { Text = "Thanks!" } },
});

Fetch message content (data plane)

Binary content (for example an image a user sent) lives on the data-plane host. client.Blob routes there automatically — no host handling on your part:

Stream stream = await client.Blob.V2.Bot.Message["<messageId>"].Content.GetAsync();

Building messages and actions

Messages and actions are strongly typed. Note one Kiota naming quirk: the polymorphic action base type is generated as ActionObject (to avoid a clash with System.Action). Concrete actions keep their natural names:

var buttons = new TemplateMessage
{
    AltText = "menu",
    Template = new ButtonsTemplate
    {
        Text = "Pick one",
        Actions = new()
        {
            new MessageAction  { Label = "Say hi", Text = "hi" },
            new PostbackAction { Label = "Buy",     Data = "action=buy" },
            new URIAction      { Label = "Open",    Uri  = "https://example.com" },
        },
    },
};

Rich menus

Rich menu operations are part of the Messaging spec (control operations on api.line.me, image upload/download on api-data.line.me), so they are available through MessagingClient directly. For convenience, RichMenuClient wraps a MessagingClient and adds the common create → image → set-default flow, plus an image helper that infers the required image/png / image/jpeg content type from the file extension.

var rich = RichMenuClient.CreateWithStaticToken("CHANNEL_ACCESS_TOKEN");

var id = await rich.CreateAsync(new RichMenuRequest
{
    Size = new RichMenuSize { Width = 2500, Height = 843 },
    Selected = false,
    Name = "Main menu",
    ChatBarText = "Menu",
    Areas = new List<RichMenuArea> { /* ... */ },
});

await rich.SetImageFromFileAsync(id!, "menu.png");  // content type inferred from ".png"
await rich.SetDefaultAsync(id!);                    // show it to all users

Less common operations (alias CRUD, bulk link/unlink, batch) remain available via the low-level rich.Messaging.Api / rich.Messaging.Blob builders.

Why a facade instead of one generated client?

The Messaging spec mixes two base URLs: control operations on api.line.me and blob content on api-data.line.me. Kiota builds one client per leading server, so the library generates two clients and the facade sets the data-plane BaseUrl to api-data.line.me before construction. Exposing the generated builders directly (rather than wrapping every endpoint) is deliberate: the Messaging surface is large, so complete convenience-method coverage would be impractical. Contrast this with LIFF, whose small surface is fully wrapped.