larksuite/oapi-sdk-python

Larksuite development interface SDK

557

stars

154

commits

Python

primary language

Aug 19, 2026

updated

README

Feishu OpenPlatform Server SDK for Python

中文

The Feishu Open Platform provides server-side APIs for messaging, contacts, approval, sheets, Base, and many other product capabilities. This SDK wraps the repeated platform work around API calls, including token management, request signing, encryption/decryption, event dispatching, and typed request/response models.

Documentation

Installation

pip install lark-oapi

Python 3.8 or later is required.

Basic Usage

import lark_oapi as lark
from lark_oapi.api.im.v1 import *

client = lark.Client.builder() \
    .app_id("cli_xxx") \
    .app_secret("your_app_secret") \
    .build()

request = CreateMessageRequest.builder() \
    .receive_id_type("chat_id") \
    .request_body(CreateMessageRequestBody.builder()
        .receive_id("oc_xxx")
        .msg_type("text")
        .content("{\"text\":\"hello world\"}")
        .build()) \
    .build()

response = client.im.v1.message.create(request)

ClientAssertion Keyless Mode

For self-built apps that use an external signing service, the SDK can fetch tenant tokens with client_assertion instead of app_secret. The SDK does not generate, parse, sign, or store JWT private keys; your provider supplies the final assertion string.

import os

import lark_oapi as lark
from lark_oapi.core.client_assertion import ClientAssertionToken


class EnvClientAssertionProvider:
    def retrieve_token(self, aud: str) -> ClientAssertionToken:
        return ClientAssertionToken(os.environ["LARK_CLIENT_ASSERTION"])


client = lark.Client.builder() \
    .app_id(os.environ["LARK_APP_ID"]) \
    .client_assertion_provider(EnvClientAssertionProvider()) \
    .build()

If you use a custom OpenAPI domain, also configure oauth_base_url(...) so the SDK can derive the OAuth audience correctly. Keyless mode is for self-built apps only and does not support AppAccessToken-only APIs.

One-Click App Registration

lark_oapi.register_app creates an app through the OAuth device flow. It returns a verification URL in on_qr_code; render the URL as a QR code or show it as a link for the user to open in Feishu/Lark.

import lark_oapi as lark

def on_qr_code(info):
    print(info["url"])


result = lark.register_app(
    on_qr_code=on_qr_code,
    app_preset={
        "avatar": [
            "https://example.com/a.png",
            "https://example.com/b.webp",
        ],
        "name": "{user}'s app",
        "desc": "Created by the business platform",
    },
)

print(result["client_id"])

Custom scopes/events/callbacks and updating an existing app

When creating an app, use addons to incrementally request scopes, event subscriptions, and callbacks on top of the platform base template. They are pre-filled into the confirm page shown after the user scans the QR code, and take effect once the user confirms:

result = lark.register_app(
    on_qr_code=on_qr_code,
    addons={
        "scopes": {
            "tenant": ["im:message:send_as_bot"],
            "user": ["calendar:calendar:read"],
        },
        "events": {"items": {"tenant": ["im.message.receive_v1"]}},
        "callbacks": {"items": ["card.action.trigger"]},
    },
    create_only=True,
)

lark.register_app(
    on_qr_code=on_qr_code,
    app_id="cli_xxx",
    addons={"scopes": {"tenant": ["drive:drive.metadata:readonly"]}},
)

Notes:

  • addons is additive only: items are merged on top of the base template; base permissions can never be removed.
  • addons.preset picks the base template: omitted or True keeps the default base template, while False switches to the minimal base template so the final config only contains what addons declares. With "preset": False, an addons without any incremental item is also valid.
  • Only the 5 public config types are supported: tenant/user scopes, tenant/user events, and callbacks. Sensitive config such as event request URLs, security.*, or encrypt keys cannot travel through addons.
  • The SDK validates the shape, not the item names; names unknown to the platform catalog are ignored by the confirm page.

For a real manual E2E run without mocked registration responses:

python3 samples/registration/app_preset_live_e2e.py --open

register_app parameters

ParameterDescriptionTypeRequiredDefault
on_qr_codeCallback when the verification URL is ready. Receives {"url": str, "expire_in": int}functionYes-
on_status_changeCallback on polling status changes. Status values include polling, slow_down, domain_switchedfunctionNo-
sourceSource identifier appended to the QR URL as python-sdk/{source}stringNopython-sdk
cancel_eventthreading.Event used to cancel sync pollingthreading.EventNo-
domainCustom Feishu accounts base URLstringNohttps://accounts.feishu.cn
lark_domainCustom Lark accounts base URL used when tenant brand is LarkstringNohttps://accounts.larksuite.com
app_presetPre-fill values for the app-creation page. All fields are optional; users can still edit them on the page. Pass raw values; the SDK URL-encodes them automaticallydictNo-
app_preset.avatarApp avatar URL(s). 1-6 URLs supported; the first one is selected by default. Allowed formats are handled by the Web page: png / jpg / jpeg / webp / gifstring or list[string]No-
app_preset.nameApp name. Supports the {user} placeholder, replaced by the Web page with the scanning user's namestringNo-
app_preset.descApp description. Supports the {user} placeholderstringNo-
addonsIncremental scopes/events/callbacks pre-filled into the confirm page, effective after user confirmationdictNo-
addons.presetBase template switch. Omitted or True keeps the default base template; False switches to the minimal base template so the app only carries the configs explicitly declared in addonsboolNoTrue
addons.scopes.tenantApp-identity scopes, e.g. im:message:send_as_botlist[string]No-
addons.scopes.userUser-identity scopes, e.g. calendar:calendar:readlist[string]No-
addons.events.items.tenantApp-identity events, e.g. im.message.receive_v1list[string]No-
addons.events.items.userUser-identity events, e.g. calendar.calendar.event.changed_v4list[string]No-
addons.callbacks.itemsCallbacks, e.g. card.action.triggerlist[string]No-
create_onlyWhen True, the landing page only allows creating a new app and hides the select-existing-app entry. Takes precedence over app_id when both are setboolNo-
app_idApp ID (cli_ prefix) of an existing app. When set, the flow updates that app's config; carried on the QR URL as clientIDstringNo-

Legacy Channel Module

lark_oapi.channel is the legacy Channel entry point kept for compatibility during the migration window. New Channel features ship in lark-channel-sdk with the lark_channel import path; critical fixes for existing lark_oapi.channel users are evaluated for backport until 2027-06-02.

lark-channel-sdk can be installed alongside lark-oapi. Its SecurityConfig defaults to compatibility mode so migrated bots can roll out with audit mode before strict enforcement. See the migration guide for the full checklist.

pip install lark-channel-sdk
from lark_channel import FeishuChannel

Existing legacy import example:

import asyncio
import os

from lark_oapi.channel import FeishuChannel

channel = FeishuChannel(
    app_id=os.environ["LARK_APP_ID"],
    app_secret=os.environ["LARK_APP_SECRET"],
)

async def on_message(msg):
    await channel.send(
        msg.chat_id,
        {"text": f"echo: {msg.content_text}"},
    )

channel.on("message", on_message)

asyncio.run(channel.connect())

Channel documentation:

Examples

More composite API examples and business scenario samples are available in oapi-sdk-python-demo.

License

MIT

Contact Us

Click Server SDK in the upper right corner of the documentation page and submit feedback.

Contributors

maowenbo666

55 commits

SadMemories

37 commits

maemual

37 commits

HanShaoshuai-k

10 commits

larksuite/oapi-sdk-python

Larksuite development interface SDK

557

stars

154

commits

Python

primary language

Aug 19, 2026

updated

README

Feishu OpenPlatform Server SDK for Python

中文

The Feishu Open Platform provides server-side APIs for messaging, contacts, approval, sheets, Base, and many other product capabilities. This SDK wraps the repeated platform work around API calls, including token management, request signing, encryption/decryption, event dispatching, and typed request/response models.

Documentation

Installation

pip install lark-oapi

Python 3.8 or later is required.

Basic Usage

import lark_oapi as lark
from lark_oapi.api.im.v1 import *

client = lark.Client.builder() \
    .app_id("cli_xxx") \
    .app_secret("your_app_secret") \
    .build()

request = CreateMessageRequest.builder() \
    .receive_id_type("chat_id") \
    .request_body(CreateMessageRequestBody.builder()
        .receive_id("oc_xxx")
        .msg_type("text")
        .content("{\"text\":\"hello world\"}")
        .build()) \
    .build()

response = client.im.v1.message.create(request)

ClientAssertion Keyless Mode

For self-built apps that use an external signing service, the SDK can fetch tenant tokens with client_assertion instead of app_secret. The SDK does not generate, parse, sign, or store JWT private keys; your provider supplies the final assertion string.

import os

import lark_oapi as lark
from lark_oapi.core.client_assertion import ClientAssertionToken


class EnvClientAssertionProvider:
    def retrieve_token(self, aud: str) -> ClientAssertionToken:
        return ClientAssertionToken(os.environ["LARK_CLIENT_ASSERTION"])


client = lark.Client.builder() \
    .app_id(os.environ["LARK_APP_ID"]) \
    .client_assertion_provider(EnvClientAssertionProvider()) \
    .build()

If you use a custom OpenAPI domain, also configure oauth_base_url(...) so the SDK can derive the OAuth audience correctly. Keyless mode is for self-built apps only and does not support AppAccessToken-only APIs.

One-Click App Registration

lark_oapi.register_app creates an app through the OAuth device flow. It returns a verification URL in on_qr_code; render the URL as a QR code or show it as a link for the user to open in Feishu/Lark.

import lark_oapi as lark

def on_qr_code(info):
    print(info["url"])


result = lark.register_app(
    on_qr_code=on_qr_code,
    app_preset={
        "avatar": [
            "https://example.com/a.png",
            "https://example.com/b.webp",
        ],
        "name": "{user}'s app",
        "desc": "Created by the business platform",
    },
)

print(result["client_id"])

Custom scopes/events/callbacks and updating an existing app

When creating an app, use addons to incrementally request scopes, event subscriptions, and callbacks on top of the platform base template. They are pre-filled into the confirm page shown after the user scans the QR code, and take effect once the user confirms:

result = lark.register_app(
    on_qr_code=on_qr_code,
    addons={
        "scopes": {
            "tenant": ["im:message:send_as_bot"],
            "user": ["calendar:calendar:read"],
        },
        "events": {"items": {"tenant": ["im.message.receive_v1"]}},
        "callbacks": {"items": ["card.action.trigger"]},
    },
    create_only=True,
)

lark.register_app(
    on_qr_code=on_qr_code,
    app_id="cli_xxx",
    addons={"scopes": {"tenant": ["drive:drive.metadata:readonly"]}},
)

Notes:

  • addons is additive only: items are merged on top of the base template; base permissions can never be removed.
  • addons.preset picks the base template: omitted or True keeps the default base template, while False switches to the minimal base template so the final config only contains what addons declares. With "preset": False, an addons without any incremental item is also valid.
  • Only the 5 public config types are supported: tenant/user scopes, tenant/user events, and callbacks. Sensitive config such as event request URLs, security.*, or encrypt keys cannot travel through addons.
  • The SDK validates the shape, not the item names; names unknown to the platform catalog are ignored by the confirm page.

For a real manual E2E run without mocked registration responses:

python3 samples/registration/app_preset_live_e2e.py --open

register_app parameters

ParameterDescriptionTypeRequiredDefault
on_qr_codeCallback when the verification URL is ready. Receives {"url": str, "expire_in": int}functionYes-
on_status_changeCallback on polling status changes. Status values include polling, slow_down, domain_switchedfunctionNo-
sourceSource identifier appended to the QR URL as python-sdk/{source}stringNopython-sdk
cancel_eventthreading.Event used to cancel sync pollingthreading.EventNo-
domainCustom Feishu accounts base URLstringNohttps://accounts.feishu.cn
lark_domainCustom Lark accounts base URL used when tenant brand is LarkstringNohttps://accounts.larksuite.com
app_presetPre-fill values for the app-creation page. All fields are optional; users can still edit them on the page. Pass raw values; the SDK URL-encodes them automaticallydictNo-
app_preset.avatarApp avatar URL(s). 1-6 URLs supported; the first one is selected by default. Allowed formats are handled by the Web page: png / jpg / jpeg / webp / gifstring or list[string]No-
app_preset.nameApp name. Supports the {user} placeholder, replaced by the Web page with the scanning user's namestringNo-
app_preset.descApp description. Supports the {user} placeholderstringNo-
addonsIncremental scopes/events/callbacks pre-filled into the confirm page, effective after user confirmationdictNo-
addons.presetBase template switch. Omitted or True keeps the default base template; False switches to the minimal base template so the app only carries the configs explicitly declared in addonsboolNoTrue
addons.scopes.tenantApp-identity scopes, e.g. im:message:send_as_botlist[string]No-
addons.scopes.userUser-identity scopes, e.g. calendar:calendar:readlist[string]No-
addons.events.items.tenantApp-identity events, e.g. im.message.receive_v1list[string]No-
addons.events.items.userUser-identity events, e.g. calendar.calendar.event.changed_v4list[string]No-
addons.callbacks.itemsCallbacks, e.g. card.action.triggerlist[string]No-
create_onlyWhen True, the landing page only allows creating a new app and hides the select-existing-app entry. Takes precedence over app_id when both are setboolNo-
app_idApp ID (cli_ prefix) of an existing app. When set, the flow updates that app's config; carried on the QR URL as clientIDstringNo-

Legacy Channel Module

lark_oapi.channel is the legacy Channel entry point kept for compatibility during the migration window. New Channel features ship in lark-channel-sdk with the lark_channel import path; critical fixes for existing lark_oapi.channel users are evaluated for backport until 2027-06-02.

lark-channel-sdk can be installed alongside lark-oapi. Its SecurityConfig defaults to compatibility mode so migrated bots can roll out with audit mode before strict enforcement. See the migration guide for the full checklist.

pip install lark-channel-sdk
from lark_channel import FeishuChannel

Existing legacy import example:

import asyncio
import os

from lark_oapi.channel import FeishuChannel

channel = FeishuChannel(
    app_id=os.environ["LARK_APP_ID"],
    app_secret=os.environ["LARK_APP_SECRET"],
)

async def on_message(msg):
    await channel.send(
        msg.chat_id,
        {"text": f"echo: {msg.content_text}"},
    )

channel.on("message", on_message)

asyncio.run(channel.connect())

Channel documentation:

Examples

More composite API examples and business scenario samples are available in oapi-sdk-python-demo.

License

MIT

Contact Us

Click Server SDK in the upper right corner of the documentation page and submit feedback.

Contributors

maowenbo666

55 commits

SadMemories

37 commits

maemual

37 commits

HanShaoshuai-k

10 commits

Languages

Python

97.6%

C++

2.4%