Skip to content

About

No description, website, or topics provided.

Resources

Stars

311 stars

Watchers

10 watching

Forks

Repository files navigation

飞书开放接口SDK/Feishu OpenPlatform Server SDK

旨在让开发者便捷的调用飞书开放API、处理订阅的事件、处理服务端推送的卡片行为等。

Feishu Open Platform offers a series of server-side atomic APIs to achieve diverse functionalities. However, actual coding requires additional work, such as obtaining and maintaining access tokens, encrypting and decrypting data, and verifying request signatures. Furthermore, the lack of semantic descriptions for function calls and type system support can increase coding burdens.

To address these issues, Feishu Open Platform has developed the Open Interface SDK, which incorporates all lengthy logic processes, provides a comprehensive type system, and offers a semantic programming interface to enhance the coding experience.

介绍文档 Introduction Documents

Channel and Agent Integration

LarkChannel is a high-level conversation facade for Agent and bot scenarios. It brings event intake, message normalization, safety policy gates, replies, streaming output, media upload/download, card actions and reactions into one Java entry point.

Use Channel when you are building a conversational bot, AI Agent, support assistant, knowledge-base assistant or any service that needs to receive Feishu/Lark messages and reply in the same chat context. For small one-off API calls, use the regular Client; for long-running chat workflows, Channel handles the surrounding plumbing.

Key entry points:

  • Create a channel with LarkChannelFactory.createLarkChannel(...).
  • Call connect() before processing inbound events; it returns CompletableFuture<BotIdentity>.
  • Listen with channel.on("message", handler), channel.on("cardAction", handler), channel.on("reject", handler) and related event names.
  • Reply with send(...), stream incremental output with stream(...), and use downloadResource(...) for image/file content.
  • Configure policy(...) for group allowlists, direct-message behavior, mention requirements and mention-all handling.
  • Use includeRawEvent(true) only when handlers need the original event body.

See CHANNEL.en.md for the English guide and CHANNEL.md for the Chinese guide.

App Registration

The SDK provides RegisterApp.register(...) for one-click app creation based on OAuth 2.0 Device Authorization Grant (RFC 8628). It returns a verification URL that users can open in Feishu/Lark to authorize and automatically register an app, then obtain the app credentials without manually creating one in the developer console.

import com.lark.oapi.scene.registration.AppPreset;
import com.lark.oapi.scene.registration.QRCodeInfo;
import com.lark.oapi.scene.registration.RegisterApp;
import com.lark.oapi.scene.registration.RegisterAppException;
import com.lark.oapi.scene.registration.RegisterAppOptions;
import com.lark.oapi.scene.registration.RegisterAppResult;

public class Sample {

    public static void main(String[] args) {
        try {
            RegisterAppResult result = RegisterApp.register(
                    RegisterAppOptions.newBuilder()
                            .source("test")
                            .appPreset(AppPreset.newBuilder()
                                    .avatars(
                                            "https://s1-imfile.feishucdn.com/static-resource/v1/v3_00cj_d6bebede-c56b-40a2-b767-8e9da07f3b3g",
                                            "https://s1-imfile.feishucdn.com/static-resource/v1/v2_bc5d2075-fcbd-41f8-bfe3-5a5ecbf0f7dg"
                                    )
                                    .name("{user}'s app")
                                    .desc("Created by the business platform")
                                    .build())
                            .onQRCode(info -> {
                                System.out.println("Please scan the QR code:");
                                System.out.println(info.getUrl());
                                System.out.println(String.format("Expires in %s seconds", info.getExpireIn()));
                            })
                            .onStatusChange(info -> System.out.println(
                                    "Status: " + info.getStatus()
                                            + (info.getInterval() > 0
                                            ? String.format(" (interval: %ss)", info.getInterval())
                                            : "")
                            ))
                            .build()
            );

            System.out.println("App ID: " + result.getClientId());
            System.out.println("App Secret: " + result.getClientSecret());
            System.out.println("User Info: " + result.getUserInfo());
        } catch (RegisterAppException e) {
            System.err.println("Failed: " + e.getCode() + " " + e.getDescription());
        }
    }
}

Real runnable demo:

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 opens the verification URL, and take effect after the user confirms:

import com.lark.oapi.scene.registration.AppAddons;

// Create: incrementally request scopes/events/callbacks, and only allow creating a new app.
RegisterApp.register(RegisterAppOptions.newBuilder()
        .addons(AppAddons.newBuilder()
                .tenantScopes("im:message:send_as_bot")
                .userScopes("calendar:calendar:read")
                .tenantEvents("im.message.receive_v1")
                .callbacks("card.action.trigger")
                .build())
        .createOnly(true)
        .onQRCode(info -> System.out.println(info.getUrl()))
        .build());

// Update: pass the appId of an existing app to let the user confirm incremental config changes.
RegisterApp.register(RegisterAppOptions.newBuilder()
        .appId("cli_xxx")
        .addons(AppAddons.newBuilder()
                .tenantScopes("drive:drive.metadata:readonly")
                .build())
        .onQRCode(info -> System.out.println(info.getUrl()))
        .build());

// Minimal base: preset(false) switches the base to the minimal template, so the
// confirm page only shows the config declared here. Incremental items may be empty.
RegisterApp.register(RegisterAppOptions.newBuilder()
        .addons(AppAddons.newBuilder()
                .preset(false)
                .tenantScopes("im:message:send_as_bot")
                .build())
        .onQRCode(info -> System.out.println(info.getUrl()))
        .build());

Notes:

  • addons is additive only. Items are merged on top of the base template; base permissions cannot be removed.
  • addons.preset selects the base template: unset or true keeps the platform default template (with its default scopes/events/callbacks); false switches to the minimal base template (bot capability only, no business scopes), so the final config is fully declared by addons. With preset(false), addons may contain no incremental items at all.
  • Only the 5 public config types are supported: tenant/user scopes, tenant/user events, and callbacks. Sensitive config such as event request URLs, security.*, and encrypt keys cannot travel through addons; use the update application config OpenAPI instead.
  • The SDK validates the shape and non-empty values, not whether scope/event/callback names exist in the platform catalog.

RegisterAppOptions parameters

Parameter Description Type Required Default
source Source identifier, appended to the QR code URL source parameter as java-sdk/{source} String No -
domain Custom Feishu accounts base URL String No https://accounts.feishu.cn
larkDomain Custom Lark accounts base URL, used when tenant brand is detected as Lark String No https://accounts.larksuite.com
appPreset Pre-fill values for the app creation page. All fields are optional; users can still edit them on the page. The SDK URL-encodes raw values automatically. AppPreset No -
appPreset.avatar App avatar URL candidates. Supports 1-6 URLs; the first one is selected by default. Page/server handles image rendering rules such as png/jpg/jpeg/webp/gif and GIF frame sampling. String / String[] / List<String> No -
appPreset.name App name. Supports the {user} placeholder, replaced by the app creation page with the scanning user's name. String No -
appPreset.desc App description. Supports the {user} placeholder. String No -
addons Incremental scopes/events/callbacks pre-filled into the confirm page. AppAddons No -
addons.preset Base template switch. Unset or true keeps the platform default template; false switches to the minimal base template so only the config declared in addons is shown. boolean No Platform default template
addons.scopes.tenant App-identity scopes, for example im:message:send_as_bot. String[] / List<String> No -
addons.scopes.user User-identity scopes, for example calendar:calendar:read. String[] / List<String> No -
addons.events.items.tenant App-identity events, for example im.message.receive_v1. String[] / List<String> No -
addons.events.items.user User-identity events, for example calendar.calendar.event.changed_v4. String[] / List<String> No -
addons.callbacks.items Callbacks, for example card.action.trigger. String[] / List<String> No -
createOnly When true, the landing page only allows creating a new app and hides the select-existing-app entry. boolean No false
appId App ID (cli_ prefix) of an existing app. When set, the flow updates that app's config by asking the user to confirm the diff brought by addons. The page ignores it when createOnly is true. String No -
onQRCode Callback when the verification URL is ready. Receives QRCodeInfo with url and expireIn Consumer<QRCodeInfo> Yes -
onStatusChange Callback on polling status changes. Receives StatusChangeInfo Consumer<StatusChangeInfo> No -

Return value

Field Type Description
clientId String App ID
clientSecret String App Secret
userInfo UserInfo Scanning user info
userInfo.openId String User's open_id
userInfo.tenantBrand String "feishu" or "lark"

Status callback

Status Description
polling Authorization is still pending
slow_down The server asks the client to slow down polling; interval contains the new interval in seconds
domain_switched The SDK detected a Lark tenant and switched polling to larkDomain

Error handling

RegisterAppException contains code and description fields:

code Description
access_denied User denied the authorization
expired_token QR code expired or polling timed out
abort Registration was canceled via thread interruption
invalid_response The service returned an unexpected response

扩展示例

我们还基于 SDK 封装了常用的 API 组合调用及业务场景示例,如:

更多示例可参考:https://github.com/larksuite/oapi-sdk-java-demo

加入交流互助群

单击加入交流互助群

License

使用 MIT

Webhook authentication

When no EncryptKey is configured and a nonempty VerificationToken is explicitly supplied, the SDK validates the token before invoking event or callback handlers: top-level token for v1 and header.token for v2. Missing, null, empty, non-string, or mismatched tokens are rejected. Challenge responses also validate explicitly configured verification tokens.

Leaving both credentials empty retains the previous behavior. Encrypted events retain existing decryption and signature checks, and WebSocket clients continue using trusted internal dispatch.

Rollback: reverting this change restores the validation gap for explicitly configured VerificationTokens. Keep independent webhook authentication in place before rolling back.

Runtime webhook credentials

Register an optional provider on the event dispatcher builder. Update an immutable snapshot in your application's local cache to rotate credentials between requests:

AtomicReference<WebhookCredentials> current = new AtomicReference<>(
    new WebhookCredentials("initial-encrypt-key", "initial-verification-token"));
EventDispatcher dispatcher = EventDispatcher.newBuilder("", "")
    .webhookCredentialsProvider(current::get)
    // Register event handlers here.
    .build();
// After a successful configuration refresh:
current.set(new WebhookCredentials("new-encrypt-key", "new-verification-token"));

Import java.util.concurrent.atomic.AtomicReference, com.lark.oapi.event.WebhookCredentials, and com.lark.oapi.event.EventDispatcher. The constructor order is encryption key, then verification token. The provider is registered at initialization and called once per HTTP webhook (including challenge and card callbacks), before parsing/decryption. That request uses the same immutable snapshot throughout; trusted WebSocket dispatch never calls the provider. Public parseReq/decryptEvent helpers and credential getters retain their static behavior; use handle for provider-based HTTP processing.

Providers must be thread safe and prompt, preferably reading a local cache. Any external I/O must have its own timeout; the SDK starts no background worker and does not retry. Exceptions or null results/fields reject the request without falling back to static credentials or exposing provider error details. Successful empty strings preserve static empty-string behavior, including legacy challenge validation. Without a provider, existing static configuration is unchanged. To roll back, remove provider registration and configure static credentials; retain the token validation fix.

About

No description, website, or topics provided.

Resources

Stars

311 stars

Watchers

10 watching

Forks

Releases

Packages

Used by

Contributors

Languages