FieldStone SDK

v0.2.4

Embed an assistant on any website. Drop in the chat widget for booking and service questions, or wire your own forms straight to your inbox — both powered by your FieldStone account.

Get your credentials

Every snippet below authenticates with two values from a FieldStone account. Grab them once and drop them into FieldStone.init().

  • Business ID — identifies the business the assistant answers for. Required for the chat widget; replaces the your-business-id placeholder.
  • API key — a secret that starts with fsk_. Required for free-agent forms and recommended for the widget; replaces fsk_your_api_key.

Where to find them

  1. In FieldStone, open Settings → SDK.
  2. Copy your Business ID from the top of the page.
  3. Click Generate Key to create an API key. It's shown once — copy it immediately; for security it can't be retrieved later. You can keep up to 3 active keys and revoke any of them anytime.

Integrating on behalf of a business? Ask the account owner to send you their Business ID and a freshly generated API key from that screen. Free-agent keys are issued separately by a FieldStone super admin, bundled with the notify email and phone — see Free Agent Forms. Keep keys private and never commit them to a public repository.

Chat Widget

Quick Start

Add these two lines before your closing </body> tag. Works with any website — no build step required. Swap in your Business ID and API key.

html
<script src="https://pub-cc5ed544ca014859991114c0d7808ed7.r2.dev/v1/sdk.js"></script>
<script>
  FieldStone.init({
    businessId: "your-business-id",
    apiKey: "fsk_your_api_key",
  });
</script>

Install via Package Manager

For React, Vue, Svelte, or any project with a build step.

npm install @fieldstoneapp/sdk

React

Import the component and wrap your app.

tsx
import { FieldStoneAI } from "@fieldstoneapp/sdk";

function App() {
  return (
    <FieldStoneAI
      businessId="your-business-id"
      apiKey="fsk_your_api_key"
    >
      <YourApp />
    </FieldStoneAI>
  );
}

Configuration

Options passed to FieldStone.init() or the <FieldStoneAI> component.

OptionTypeDescription
businessId*stringYour business identifier. Required for the chat widget; omit it in free-agent mode.
apiKeystringYour SDK API key from Settings → SDK. Defaults to businessId for public-key flows. Required in free-agent mode.
modestring"widget" (default) mounts the chat widget. "free_agent" is headless — no widget, no bootstrap, only submitForm(). See Free Agent Forms below.
apiUrlstringOverride the FieldStone API base URL. Only needed for self-hosted or staging environments.
positionstring"bottom-right", "bottom-left", "top-right", or "top-left".
themeobjectOverride colors: primaryColor, fontFamily, borderRadius, dark.
voiceobject{ enabled: boolean } — toggle the push-to-talk mic button in the chat input.
contextstringExtra business info (pricing, policies, hours) sent with every message. Silently capped at 2000 characters. See Business Context below.

Programmatic Control

The widget mounts itself and runs on its own, but you can drive it from your own UI via the singleton on FieldStone.getInstance(). A second FieldStone.init() call warns and returns the existing instance.

js
// Open / close the chat panel from your own UI
FieldStone.getInstance()?.open();
FieldStone.getInstance()?.close();

// Wait for the bootstrap call to finish
const business = await FieldStone.getInstance()?.ready();

// Tear it down completely (e.g. on logout)
FieldStone.getInstance()?.destroy();

Business Context

Pass short, business-specific facts that the assistant should know on every turn — pricing rules, service minimums, seasonal hours, promotions. The string is forwarded to the agent as background data (not instructions), so it's a safe place for facts, not for telling the assistant how to behave.

js
FieldStone.init({
  businessId: "your-business-id",
  apiKey: "fsk_your_api_key",
  context: `
    Emergency plumbing calls answered 24/7.
    Minimum call-out fee: $150 (waived if you book a full repair).
    Service area: within 40 miles of downtown.
  `,
});

// Update context later — the next message the visitor sends
// will include the new value.
FieldStone.getInstance()?.setContext("Updated pricing: $175 minimum.");
  • Soft cap of 2000 characters. Anything longer is silently truncated server-side — no error is thrown.
  • Read at send time, so setContext() updates apply to the very next message.
  • Not persisted across page reloads. Set it on init, or refresh it whenever your underlying data changes.
  • Use it for facts ("minimum fee $150"), not behavior ("always upsell"). Assistant tone and personality belong in Settings → Assistant.

Free Agent Forms

Headless submissions

Keep your own form and design. A free-agent key runs headless — no chat widget mounts and there's no bootstrap call. You collect your own fields and submit them; the server fans each submission out to the email and phone bound to the key and stores no visitor data — only a non-PII usage event. Destinations are server-bound to the key and can't be set from the client.

Keys are issued by a FieldStone super admin together with the notify email, phone, and logo. The same key is used in every snippet below.

Plain fetch

No SDK required. POST your fields with the key in the X-API-Key header.

js
await fetch(
  "https://stone-production-d2ea.up.railway.app/api/sdk/free-agent/submit",
  {
    method: "POST",
    headers: {
      "X-API-Key": "fsk_your_free_agent_key",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      fields: [
        { label: "Name", value: "Jane Doe" },
        { label: "Project", value: "Kitchen remodel" },
      ],
      images: ["https://example.com/site-photo.jpg"], // optional, https only
    }),
  },
);

Script tag (headless)

Initialize with mode: "free_agent" and call FieldStone.submitForm() from your own submit handler. No chatbot appears.

html
<script src="https://pub-cc5ed544ca014859991114c0d7808ed7.r2.dev/v1/sdk.js"></script>
<script>
  // Headless: no chat widget mounts, no bootstrap call.
  FieldStone.init({
    apiKey: "fsk_your_free_agent_key",
    mode: "free_agent",
  });

  // Submit from your own form's handler.
  FieldStone.submitForm({
    fields: [{ label: "Name", value: "Jane Doe" }],
    images: [],
  });
</script>

React hook

useFreeAgentSubmit lazily initializes a headless instance and returns submit plus loading / error state.

tsx
import { useFreeAgentSubmit } from "@fieldstoneapp/sdk";

function ContactForm() {
  const { submit, loading, error } = useFreeAgentSubmit({
    apiKey: "fsk_your_free_agent_key",
  });

  async function onSubmit(e: React.FormEvent) {
    e.preventDefault();
    const result = await submit({
      fields: [{ label: "Name", value: "Jane Doe" }],
      images: [],
    });
    // result → { ok, email, sms }
  }

  return (
    <form onSubmit={onSubmit}>
      {/* your own fields */}
      <button type="submit" disabled={loading}>Send</button>
      {error && <p>{error.message}</p>}
    </form>
  );
}

Response & limits

A successful submit resolves with each channel's dispatch result. A non-2xx response rejects (the hook surfaces it as error).

json
{ "ok": true, "email": true, "sms": true }
  • fields — up to 20 { label, value } pairs. Labels max 100 chars, values max 2000.
  • images — optional, up to 10 https:// URLs, embedded inline in the notification email.
  • 409 — the key has no notification email configured, so it can't deliver. Ask the admin who issued it to set a destination.
  • 429 — more than 20 submissions per minute for a single key. Back off and retry.
  • Nothing about the visitor is persisted — no contact, no submission row, no stored image. Only an anonymous free_agent_submitted usage event (counts + origin) is recorded.

Capture submissions into your CRM

Use the same submitForm({ fields, images }) call with your own account API key (from Settings → SDK) instead of a free-agent key. Rather than an email relay, each submission is saved into your CRM: a contact is created or matched, the full field list is logged as a note, and any image URLs are downloaded into StoneBox and linked to the contact.

The identity fields Name, Email, Phone, and Address map onto the contact (matched by email, then phone); every field is kept verbatim in the note. The submit resolves with the contact instead of the notify flags.

json
{ "ok": true, "contactId": "…", "created": true, "photosSaved": 1 }

More

Privacy

The widget never collects identifying data until the visitor explicitly enters it (name, phone, email) during a booking. It also respects browser-level privacy signals at init time:

  • Global Privacy Control (navigator.globalPrivacyControl === true)
  • Do Not Track (navigator.doNotTrack === "1")

When either signal is set, all client-side analytics calls become no-ops for the entire session. The chat itself keeps working.