Landed
Custom UI SDK

SDK configuration

Client options, React hooks, and the public API used by @landedso/sdk.

Configure feedback collection from Settings → Widget in your Landed workspace. The SDK reads the same public config and publishable key as the hosted widget.

Admin settings

Publishable key

Each workspace has one publishable key, created automatically when the workspace is created. It is safe to use in client-side code. Restrict access with allowed origins if needed.

Enable / disable

While the widget is disabled, public config and submission endpoints reject traffic even with a valid key.

Allowed origins

Add one origin per line in widget settings, for example:

https://app.example.com
https://staging.example.com

When this list is empty, Landed allows all origins during setup. For production, restrict the list to your real app origins.

LandedClient.create(options)

OptionRequiredDefaultNotes
publishableKeyYes—Your wk_pub_... key
baseUrlNohttps://landed.soLanded host (NEXT_PUBLIC_APP_URL)
fetchNoglobalThis.fetchCustom fetch implementation (testing)

Client methods

MethodPurpose
ping()Verify local or production setup from the current browser origin
getConfig()Load workspace + widget settings
identify(user | null)Attach or clear signed-in user context. Host product id and email are required.
getIdentifiedUser()Read the current identified user
getVisitorToken() / setVisitorToken(token)Read or restore the visitor session token
logout()Rotate the visitor token and clear identify context
listConversations()List the current identity's widget conversations and auto-open behavior
getConversation(id)Load customer-visible messages for a conversation the current identity owns
reportTyping(id)Tell Landed the customer is typing a reply, so a bot waits for them to pause (throttled, never creates a message)
upload(file, filename?)Upload an attachment, returns storageRefId
submit(input)Submit a customer conversation message
listChangelogReleases(options?)List published changelog releases
getChangelogRelease(releaseId)Fetch a published release with markdown content
clearConfigCache()Force the next getConfig() to refetch

ping()

Call ping() from your product origin after install so Landed can mark local or production setup:

const result = await landed.ping();
FieldNotes
kindlocal, production, or absent based on the request Origin
localSetupAt / productionSetupAtFirst successful ping timestamps
updatedtrue when this ping wrote a timestamp for the first time

Do not set or spoof Origin — browsers send it automatically. Prefer ping() over calling the REST endpoint directly when using the SDK.

submit(input) shape

submit() accepts the fields required to create or continue a conversation:

await landed.submit({
  text: "Optional feedback body",
  storageRefIds: ["storage_ref_from_upload"],
  conversationId: "optional_existing_conversation_id",
  idempotencyKey: "optional-retry-key",
  context: {
    pageUrl: "https://app.example.com/settings",
    userAgent: navigator.userAgent,
    custom: { plan: "pro" },
  },
  newUser: {
    email: "guest@example.com",
    name: "Guest User",
  },
});
FieldNotes
textFeedback body. Required unless storageRefIds is non-empty.
storageRefIdsAttachment IDs from upload(). Required unless text is non-empty.
conversationIdContinue a specific conversation when the customer has several.
createConversationStart a new conversation even when one already exists. Omit conversationId when using this.
idempotencyKeyReplay the same submission without creating a second message.
contextOptional page URL, user agent, or custom string metadata.
newUserGuest email/name when identify() was not called.

Call identify() before submit() to attach a signed-in user instead of using newUser. Host product id and email are required.

React hooks

Import from @landedso/sdk/react:

HookPurpose
LandedProviderProvides a client to the tree
useLanded()Access the underlying LandedClient
useLandedConfig()Load and cache public widget config
useIdentify()Returns an identify(user | null) function
useSubmitFeedback(){ submit, isSubmitting, error, result, reset }
useLandedUpload(){ upload, isUploading, error }

Public API

The SDK calls Landed public endpoints:

  • POST /api/v1/ping
  • GET /api/v1/widget/config
  • POST /api/v1/widget/session
  • GET /api/v1/widget/conversations
  • GET /api/v1/widget/conversations/{conversationId}
  • POST /api/v1/feedback/submissions
  • POST /api/v1/uploads
  • GET /api/v1/changelog/releases
  • GET /api/v1/changelog/releases/{releaseId}

See the full REST API reference for request parameters, response schemas, and interactive examples.

Authentication header:

Authorization: Bearer wk_pub_...

Alternate header:

X-Landed-Widget-Key: wk_pub_...

Submission response

Success (201):

{
  "conversationId": "cuid2_id",
  "messageId": "cuid2_id",
  "status": "accepted",
  "visitorToken": "opaque_visitor_token"
}

Common errors:

StatusCodeMeaning
400CONTENT_REQUIREDMissing text and attachments
403WIDGET_DISABLEDWidget turned off in settings
403ANONYMOUS_FEEDBACK_DISABLEDAnonymous not allowed and no user/email provided
401Invalid widget key.Bad publishable key

The SDK throws LandedApiError with status and code properties for failed requests.

Next step