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.comWhen this list is empty, Landed allows all origins during setup. For production, restrict the list to your real app origins.
LandedClient.create(options)
| Option | Required | Default | Notes |
|---|---|---|---|
publishableKey | Yes | — | Your wk_pub_... key |
baseUrl | No | https://landed.so | Landed host (NEXT_PUBLIC_APP_URL) |
fetch | No | globalThis.fetch | Custom fetch implementation (testing) |
Client methods
| Method | Purpose |
|---|---|
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();| Field | Notes |
|---|---|
kind | local, production, or absent based on the request Origin |
localSetupAt / productionSetupAt | First successful ping timestamps |
updated | true 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",
},
});| Field | Notes |
|---|---|
text | Feedback body. Required unless storageRefIds is non-empty. |
storageRefIds | Attachment IDs from upload(). Required unless text is non-empty. |
conversationId | Continue a specific conversation when the customer has several. |
createConversation | Start a new conversation even when one already exists. Omit conversationId when using this. |
idempotencyKey | Replay the same submission without creating a second message. |
context | Optional page URL, user agent, or custom string metadata. |
newUser | Guest 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:
| Hook | Purpose |
|---|---|
LandedProvider | Provides 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/pingGET /api/v1/widget/configPOST /api/v1/widget/sessionGET /api/v1/widget/conversationsGET /api/v1/widget/conversations/{conversationId}POST /api/v1/feedback/submissionsPOST /api/v1/uploadsGET /api/v1/changelog/releasesGET /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:
| Status | Code | Meaning |
|---|---|---|
400 | CONTENT_REQUIRED | Missing text and attachments |
403 | WIDGET_DISABLED | Widget turned off in settings |
403 | ANONYMOUS_FEEDBACK_DISABLED | Anonymous not allowed and no user/email provided |
401 | Invalid widget key. | Bad publishable key |
The SDK throws LandedApiError with status and code properties for failed requests.