Landed
Custom UI SDK

SDK installation

Install @landedso/sdk to collect feedback in your own UI instead of the hosted widget.

Use @landedso/sdk when you want full control over the feedback UI in your app — a custom button, modal, sidebar, or inline form — while still sending submissions to Landed.

This is an alternative to the hosted feedback widget, which loads Landed's iframe UI via script tags.

How it works

Your app installs the npm package and calls Landed's public API with your workspace publishable key (wk_pub_...). The SDK handles authentication, config loading, uploads, and submission payloads.

sequenceDiagram
  participant App as Your app
  participant SDK as @landedso/sdk
  participant API as Landed API

  App->>SDK: LandedClient.create({ publishableKey })
  App->>SDK: identify({ id, email })
  App->>SDK: submit({ text, storageRefIds })
  SDK->>API: POST /api/v1/feedback/submissions
  API->>API: Create or continue a conversation, append a customer message

Submissions appear as conversations in the support inbox.

Quick actions

Prerequisites

  • Admin access in Landed
  • Your workspace publishable key from Settings → Widget (use the Custom UI SDK tab)
  • A JavaScript/TypeScript app (React recommended for hooks)

Install

npm install @landedso/sdk

Headless client

import { LandedClient } from "@landedso/sdk";

const landed = LandedClient.create({
  publishableKey: "wk_pub_YOUR_KEY",
  // Optional — defaults to https://landed.so
  baseUrl: "https://landed.so",
});

// Verify Local / Production Setup from the current browser origin.
// Do not set or spoof Origin — browsers send it automatically.
await landed.ping();

// Optional: attach a signed-in user
landed.identify({
  id: "user_123",
  email: "jane@example.com",
});

// Load widget settings (fields, copy, anonymous allowed)
const config = await landed.getConfig();

// Submit feedback from your custom UI
await landed.submit({
  text: "Love the new dashboard",
  context: {
    pageUrl: window.location.href,
    custom: { plan: "pro" },
  },
});

// Display published changelog releases in your app
const releases = await landed.listChangelogReleases({ limit: 5 });
const latest = await landed.getChangelogRelease(releases.data[0].id);

Verify setup with ping()

After installing the SDK in your product, call landed.ping() once from the real app origin (localhost or production). Landed uses the browser Origin to mark Local setup or Production setup in the dashboard. These checks are informational; they never block your workspace, your trial, or checkout.

  • Prefer ping() over calling POST /api/v1/ping yourself when using the SDK.
  • Call it from the browser (or same-origin frontend) so the request carries a real product origin.
  • Do not set, spoof, or strip the Origin header — use browser defaults.
await landed.ping();
// { kind: "production" | "local" | "absent", localSetupAt, productionSetupAt, updated }

React hooks

import {
  LandedProvider,
  useLandedConfig,
  useIdentify,
  useSubmitFeedback,
} from "@landedso/sdk/react";

function FeedbackForm() {
  const { config, isLoading } = useLandedConfig();
  const identify = useIdentify();
  const { submit, isSubmitting } = useSubmitFeedback();

  if (isLoading || !config) return null;

  return (
    <form
      onSubmit={async (event) => {
        event.preventDefault();
        await submit({ text: "Great product!" });
      }}
    >
      <button type="submit" disabled={isSubmitting}>
        {config.widget.copy.submitLabel}
      </button>
    </form>
  );
}

export function App() {
  return (
    <LandedProvider publishableKey="wk_pub_YOUR_KEY">
      <FeedbackForm />
    </LandedProvider>
  );
}

Attachments

Upload files before submitting:

const file = document.querySelector("input[type=file]")!.files![0];
const upload = await landed.upload(file);

await landed.submit({
  text: "Bug report with screenshot",
  storageRefIds: [upload.storageRefId],
});

Verify

  1. Call await landed.ping() from your product origin and confirm local or production setup updates in Landed.
  2. Wire up a test form in your app.
  3. Submit feedback through your custom UI.
  4. Open Inbox in Landed and confirm the submission appears as a conversation.

Widget vs SDK

Hosted widget@landedso/sdk
UILanded iframe + floating buttonYour own components
InstallScript tagsnpm install @landedso/sdk
APISame public endpointsSame public endpoints
Keywk_pub_...wk_pub_...

Next step

If your users sign in, follow identify users so submissions are attributed correctly.