Feishu OpenPlatform Server SDK

July 7, 2026 ยท View on GitHub

English | Simplified Chinese

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 these lengthy logic processes, provides a comprehensive type system, and offers a semantic programming interface to improve the coding experience.

Introduction Documents

Channel Module

The SDK provides a Channel module built on top of WebSocket and the API Client. It encapsulates event listening, message normalization, streaming replies, and media uploads, allowing developers to focus purely on business logic.

One-Click App Registration

The SDK provides registration.RegisterApp 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 or render as a QR code. After authorization, the app is created automatically and the SDK returns the App ID and App Secret.

package main

import (
	"context"
	"errors"
	"fmt"
	"time"

	lark "github.com/larksuite/oapi-sdk-go/v3"
	"github.com/larksuite/oapi-sdk-go/v3/scene/registration"
)

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
	defer cancel()

	result, err := registration.RegisterApp(ctx, &registration.Options{
		OnQRCode: func(info *registration.QRCodeInfo) {
			fmt.Printf("open or scan this url: %s\n", info.URL)
			fmt.Printf("the link expires in %d seconds\n", info.ExpireIn)
		},
		OnStatusChange: func(info *registration.StatusChangeInfo) {
			// status: polling | slow_down | domain_switched
			fmt.Printf("registration status: %s", info.Status)
			if info.Interval > 0 {
				fmt.Printf(", next poll after %d seconds", info.Interval)
			}
			fmt.Println()
		},
	})
	if err != nil {
		var regErr *registration.RegisterAppError
		if errors.As(err, &regErr) {
			fmt.Printf("register app failed: code=%s, description=%s\n", regErr.Code, regErr.Description)
			return
		}
		panic(err)
	}

	fmt.Println("App ID:", result.ClientID)
	fmt.Println("App Secret:", result.ClientSecret)

	client := lark.NewClient(result.ClientID, result.ClientSecret)
	_ = client
}

Custom Scopes, Events, Callbacks, And Updating An Existing App

When creating an app, use Options.Addons to incrementally request scopes, event subscriptions, and callbacks on top of the platform base template. The values are pre-filled into the confirmation page after the user opens the QR code URL and take effect after confirmation. Options.CreateOnly=true only allows creating a new app. Options.AppID starts the update flow for an existing app.

_, err := registration.RegisterApp(ctx, &registration.Options{
	Addons: &registration.AppAddons{
		Scopes: registration.AppAddonsScopes{
			Tenant: []string{"im:message:send_as_bot"},
			User:   []string{"calendar:calendar:read"},
		},
		Events: registration.AppAddonsEvents{
			Items: registration.AppAddonsEventItems{
				Tenant: []string{"im.message.receive_v1"},
			},
		},
		Callbacks: registration.AppAddonsCallbacks{
			Items: []string{"card.action.trigger"},
		},
	},
	CreateOnly: true,
	OnQRCode: func(info *registration.QRCodeInfo) {
		fmt.Println(info.URL)
	},
})
if err != nil {
	panic(err)
}

_, err = registration.RegisterApp(ctx, &registration.Options{
	AppID: "cli_xxx",
	Addons: &registration.AppAddons{
		Scopes: registration.AppAddonsScopes{
			Tenant: []string{"drive:drive.metadata:readonly"},
		},
	},
	OnQRCode: func(info *registration.QRCodeInfo) {
		fmt.Println(info.URL)
	},
})
if err != nil {
	panic(err)
}

Notes: Addons is additive only and cannot remove config from the base template. The SDK validates shape and non-empty strings, but does not validate whether scope, event, or callback names exist.

Choosing The Base Template With Preset

AppAddons.Preset selects the base template for app creation (it is unrelated to Options.AppPreset, which pre-fills the app name, description, and avatar):

ValueBase templateBehavior
unset (nil)Platform default templateSame as before this field existed; the encoded payload carries no preset key.
falseMinimal base templateThe confirmation page shows only the scopes, events, and callbacks declared in Addons.
truePlatform default templateExplicitly requests the default base, same as unset.

With Preset set to false, an Addons without any scope, event, or callback is valid โ€” the page still enters the confirmation flow and creates an app with only the minimal base capabilities:

preset := false
_, err := registration.RegisterApp(ctx, &registration.Options{
	Addons: &registration.AppAddons{
		Preset: &preset,
	},
	OnQRCode: func(info *registration.QRCodeInfo) {
		fmt.Println(info.URL)
	},
})
if err != nil {
	panic(err)
}

registration.RegisterApp Parameters

ParameterDescriptionTypeRequiredDefault
ctxControls timeout and cancellation for the registration flow; canceling the context stops polling.context.ContextYes-
Options.SourceSource identifier appended to the QR URL as go-sdk/{source}.stringNogo-sdk
Options.DomainCustom Feishu accounts domain. A full base URL such as https://accounts.feishu.cn is supported.stringNohttps://accounts.feishu.cn
Options.LarkDomainCustom Lark accounts domain used when tenant_brand=lark is detected.stringNohttps://accounts.larksuite.com
Options.AppPresetPre-filled app creation values; users can still edit them on the page.*registration.AppPresetNo-
Options.AppPreset.AvatarApp avatar URLs, 1-6 entries; first entry is selected by default. Pass raw URLs and the SDK encodes them. Page-side display rules are handled by the app creation page.[]stringNo-
Options.AppPreset.NameApp name with {user} placeholder support; pass raw value and the SDK encodes it.stringNo-
Options.AppPreset.DescApp description with {user} placeholder support; pass raw value and the SDK encodes it.stringNo-
Options.AddonsIncremental scopes, events, and callbacks pre-filled into the confirmation page.*registration.AppAddonsNo-
Options.Addons.PresetBase template selector: unset for the platform default template, false for the minimal base template (empty increments allowed), true for explicitly requesting the default base.*boolNo-
Options.Addons.Scopes.TenantApp-identity scopes, for example im:message:send_as_bot.[]stringNo-
Options.Addons.Scopes.UserUser-identity scopes, for example calendar:calendar:read.[]stringNo-
Options.Addons.Events.Items.TenantApp-identity events, for example im.message.receive_v1.[]stringNo-
Options.Addons.Events.Items.UserUser-identity events, for example calendar.calendar.event.changed_v4.[]stringNo-
Options.Addons.Callbacks.ItemsCallback names, for example card.action.trigger.[]stringNo-
Options.CreateOnlyWhen true, the landing page only allows creating a new app. When used together with Options.AppID, the page gives create-new-app flow precedence.boolNofalse
Options.AppIDExisting app ID carried as clientID in the QR URL for the update flow.stringNo-
Options.OnQRCodeCallback invoked when the verification URL is ready. The callback receives { URL, ExpireIn }.func(info *registration.QRCodeInfo)Yes-
Options.OnStatusChangeCallback for polling status changes. Status can be polling, slow_down, or domain_switched.func(info *registration.StatusChangeInfo)No-

Return Value

FieldTypeDescription
ClientIDstringApp ID
ClientSecretstringApp Secret
UserInfo*registration.UserInfoScanning user info
UserInfo.OpenIDstringUser open_id
UserInfo.TenantBrandstring"feishu" or "lark"

Error Handling

Returned errors usually expose Code and Description through registration.RegisterAppError. More specific types include registration.AccessDeniedError and registration.ExpiredError.

CodeDescription
access_deniedUser denied authorization
expired_tokenQR code expired or polling timed out
invalid_responseResponse is empty or missing required fields

Extended Examples

We also provide common API composition examples and business scenario examples based on the SDK, such as:

For more examples, see https://github.com/larksuite/oapi-sdk-go-demo

Community

Join the support group

License

MIT