SDK

Access Fireberry context and data from your embedded applications

Fireberry SDK

The Fireberry SDK (@fireberry/sdk) enables your app to communicate with the Fireberry platform. Use it to access information about the current user and record, and to perform data operations.

Resources:


Installation

npm install @fireberry/sdk

Quick Start

import FireberryClientSDK from "@fireberry/sdk/client";

// 1. Create the client
const client = new FireberryClientSDK();

// 2. Initialize (required before using context or API)
await client.initializeContext();

// 3. Use context and API
const { user, record } = client.context;
const { fullName, license } = user;

const results = await client.api.query(1, {
  fields: "accountid,accountname",
  query: 'status = "active"',
});

How It Works

Your app runs in an iframe inside Fireberry. The SDK handles all communication with the parent window:

sequenceDiagram
    participant App as Your App (iframe)
    participant SDK as Fireberry SDK
    participant FB as Fireberry Platform

    App->>SDK: initializeContext()
    SDK->>FB: Request context
    FB-->>SDK: User + Record info
    SDK-->>App: Context ready

    App->>SDK: api.query(...)
    SDK->>FB: Query API request
    FB-->>SDK: Data response
    SDK-->>App: Results

Context

Context provides information about who's using your app and what they're looking at.

Accessing Context

await client.initializeContext();

const { user, record } = client.context;

// User info (always available)
console.log(user.id); // User's unique ID
console.log(user.fullName); // User's display name
console.log(user.organizationId); // User's organization ID
console.log(user.license); // License details (or null)

// Record info (only in Record components)
console.log(record.id); // Current record ID
console.log(record.type); // Object type number
console.log(record.storage); // File storage scoped to this record

Context by Component Type

ContextRecord ComponentSide MenuGlobal Menu
user.id
user.fullName
user.organizationId
user.license
user.permissions
record.id
record.type
record.storage
📘

Note

Record context is only available in Record components. For Side Menu and Global Menu components, record will be empty.

License (user.license)

user.license provides information about the organization's Fireberry license. It is available in all component types (Record, Side Menu, Global Menu). When license information is not available, user.license is null.

Structure:

FieldTypeDescription
licenseLevelnumberLicense tier/level
invoiceNamestringName on the license invoice
subscriptionobjectOptional. Present when the org has a subscription

subscription (when present):

FieldTypeDescription
seatsnumberNumber of seats
billingCyclePlannumber1 = Annual, 2 = Monthly
statusnumberSubscription status (see below)
endDateDateSubscription end date

Subscription status values: 1 = Active, 2 = Inactive, 3 = ChargeError, 4 = BlockedPayment, 5 = Trial, 6 = TrialExpired.



Permissions

After initialization, context.user.permissions contains the current user's role-based permissions. Use these to adapt your app's behavior to what the user is allowed to do in the organization — for example, hiding a "Create" button when the user lacks create access, or gating a feature behind a permission check.

Permissions are split into two categories: object permissions (CRUD access per CRM object) and feature permissions (access to platform capabilities).

Object Permissions

Each CRM object type carries four boolean flags indicating what the current user can do:

import FireberryClientSDK, { OBJECTS } from "@fireberry/sdk/client";

const client = new FireberryClientSDK();
await client.initializeContext();

const { permissions } = client.context.user;

const accountPerms = permissions.objects[OBJECTS.account];
accountPerms.create; // boolean
accountPerms.read; // boolean
accountPerms.update; // boolean
accountPerms.delete; // boolean

ObjectPermission:

PropertyTypeDescription
createbooleanWhether the user can create records
readbooleanWhether the user can view records
updatebooleanWhether the user can edit records
deletebooleanWhether the user can delete records

Use the OBJECTS constant for readable, type-safe lookups. For custom object types (not included in OBJECTS), use the numeric ID directly with optional chaining — or register them via declaration merging for full type safety:

// Built-in object — type-safe, no optional chaining needed
permissions.objects[OBJECTS.contact].read;

// Custom object by numeric ID — may be undefined
permissions.objects[1001]?.read;

The OBJECTS constant includes all built-in CRM entities (account, contact, opportunity, task, order, product, etc.). Import it to see the full list in your editor's autocomplete.


Feature Permissions

Feature permissions control access to platform capabilities. Each feature has a single allowed boolean:

const { permissions } = client.context.user;

if (permissions.features.billing.allowed) {
  // user has access to billing features
}

if (permissions.features.settings.allowed) {
  // user can access org settings
}

FeaturePermission:

PropertyTypeDescription
allowedbooleanWhether the user has access to the feature

Features are accessed by their string name (e.g. "billing", "settings", "webservices"). The available features depend on the organization's plan and configuration.


Permissions Example

Guard actions and conditionally render UI based on the user's permissions:

import FireberryClientSDK, { OBJECTS } from "@fireberry/sdk/client";

const client = new FireberryClientSDK();
await client.initializeContext();

const { permissions } = client.context.user;
const objectType = client.context.record.type ?? OBJECTS.account;

// Guard an update operation
if (permissions.objects[objectType]?.update) {
  await client.api.update(objectType, recordId, { status: "active" });
} else {
  console.warn("User does not have update access to this object type");
}

// Check a feature before offering it in the UI
const canExport = permissions.features.exportexel?.allowed ?? false;
📘

Note

Permissions are read-only. They reflect the logged-in user's role as configured by the organization admin and cannot be changed from the SDK.


API Methods

The SDK provides methods for working with Fireberry data and discovering object schemas:

MethodDescription
api.query()Retrieve records
api.create()Create a new record
api.update()Update an existing record
api.delete()Delete a record
api.metadata.getObjects()List all CRM object types
api.metadata.getFields()List field names for an object type
api.metadata.getField()Get metadata for a specific field

query

Retrieve records based on criteria.

const results = await client.api.query(objectType, {
  fields: "accountid,accountname,status,createdon",
  query: 'status = "active"',
  page_size: 20, // optional
  page_number: 1, // optional
});

Parameters:

ParameterTypeDescription
objectTypestring | numberThe object type to query
fieldsstringComma-separated list of fields to return
querystringFilter expression
page_sizenumberRecords per page (optional)
page_numbernumberPage number, 1-indexed (optional)

Query syntax:

OperatorExample
=status = "active"
!=status != "closed"
ANDstatus = "active" AND priority = "high"
ORstatus = "open" OR status = "pending"

create

Create a new record.

const result = await client.api.create(objectType, {
  name: "New Project",
  status: "active",
  priority: "high",
});

if (result.success) {
  console.log("Created:", result.data.id);
}

update

Update an existing record.

const result = await client.api.update(objectType, recordId, {
  status: "completed",
});

if (result.success) {
  console.log("Updated successfully");
}

delete

Delete a record.

const result = await client.api.delete(objectType, recordId);

if (result.success) {
  console.log("Deleted");
}

metadata.getObjects

List all CRM object types available to the current user.

const objects = await client.api.metadata.getObjects();
// e.g. [{ type: 1, name: "Account", pluralName: "Accounts" }, ...]

Returns: Promise<ObjectMeta[]> — an array of object type metadata.

ObjectMeta:

PropertyTypeDescription
typenumberNumeric object type code
namestringSingular display name (e.g. "Account")
pluralNamestringPlural display name (e.g. "Accounts")

metadata.getFields

List all field names available on an object type.

const fieldNames = await client.api.metadata.getFields(objectType);
// e.g. ["accountid", "accountname", "status", "email", "telephone1"]

Parameters:

ParameterTypeDescription
objectTypestring | numberThe object type to inspect

Returns: Promise<string[]> — an array of field names.


metadata.getField

Get detailed metadata for a single field, including its display label, data type, and whether it is read-only.

const field = await client.api.metadata.getField(objectType, "status");
console.log(field.label); // "Status"
console.log(field.type); // "picklist"
console.log(field.readonly); // false

Parameters:

ParameterTypeDescription
objectTypestring | numberThe object type to inspect
fieldNamestringThe field to retrieve

Returns: Promise<FieldMeta> — metadata for the requested field.


FieldMeta

FieldMeta is a discriminated union — the type property determines which additional fields are present. TypeScript narrows the type automatically when you check type.

Base properties (always present):

PropertyTypeDescription
namestringSystem name of the field
labelstringDisplay label
typeFieldTypeData type (see table below)
readonlybooleanWhether the field can be written to

Additional properties by type:

type valueExtra propertyDescription
'lookUp'relatedObjectType: numberThe object type this lookup points to
'picklist'options: PicklistOption[]The available options for the picklist
All othersNo additional properties

PicklistOption:

PropertyTypeDescription
valuenumberNumeric value stored in the record
textValuestringDisplay text for this option
ordernumberSort order

FieldType values:

date · dateTime · emailAddress · lookUp · number · picklist · richText · text · textArea · url · telephone · formula · summary

📘

Runtime constant

The FIELD_TYPES array is exported from @fireberry/sdk/client so you can iterate over the possible values at runtime:

import { FIELD_TYPES } from "@fireberry/sdk/client";
// FIELD_TYPES = ['date', 'dateTime', 'emailAddress', ...] as const

Example — inspecting fields and narrowing by type:

const fields = await client.api.metadata.getFields(objectType);

for (const name of fields) {
  const field = await client.api.metadata.getField(objectType, name);

  if (field.readonly) {
    continue; // skip computed / system fields
  }

  switch (field.type) {
    case "picklist":
      // TypeScript knows `field.options` exists here
      console.log(
        `${field.label} choices:`,
        field.options.map((o) => o.textValue),
      );
      break;
    case "lookUp":
      // TypeScript knows `field.relatedObjectType` exists here
      console.log(`${field.label} → object type ${field.relatedObjectType}`);
      break;
    default:
      console.log(`${field.label} (${field.type})`);
  }
}

Response Format

All API methods return the same structure:

interface ResponseData {
  success: boolean;
  data: any;
  error?: {
    status: number;
    statusText: string;
    data: { Message?: string };
  };
  requestId: string;
}

Handling responses:

const result = await client.api.query(1, {
  fields: "accountid,accountname",
  query: 'status = "active"',
});

if (result.success) {
  console.log("Data:", result.data);
} else {
  console.error("Error:", result.error?.data.Message);
}

Settings API

The Settings API provides persistent storage scoped to your app. Use it to save configuration, user preferences, or any state that should survive page reloads. Because all components within the same app share a single settings store, it also serves as the primary mechanism for cross-component communication.

MethodDescription
app.settings.get()Read the current settings value
app.settings.set()Write a new settings value

app.settings.get

Read the stored settings for your app.

const settings = await client.app.settings.get();

app.settings.set

Replace the stored settings with a new value. Returns the stored value after writing.

await client.app.settings.set({ theme: "dark", language: "en" });
🚧

Important: Full replace, not merge

app.settings.set() replaces the entire stored value — it does not merge or patch. If you only want to update one key, read the current settings first and spread them:

// ❌ WRONG — overwrites everything; "language" is lost
await client.app.settings.set({ theme: "light" });

// ✅ CORRECT — merge with existing settings, then write
const current = await client.app.settings.get();
await client.app.settings.set({ ...current, theme: "light" });

How Settings Scoping Works

Settings are per-app, per-organization:

  • All components in the same app read and write the same settings store.
  • Different apps cannot access each other's settings.
  • Settings are scoped to the organization, not to individual users — every user in the org sees the same data.
  • Settings persist across page reloads and browser sessions.
flowchart TB
    subgraph App["Your App"]
        S[(Settings Store)]
    end

    R[Record Component] -->|read / write| S
    G[Global Menu Component] -->|read / write| S
    SM[Side Menu Component] -->|read / write| S

When your app has multiple components, coordinate which keys each component owns. For example, a Record component might own focusedAccount, while a Side Menu component owns taskSummary. Each component reads keys written by others, but only writes to its own.

📘

Note

Components do not receive real-time updates when another component changes settings. To see changes made by a sibling component, call app.settings.get() again — for example, in response to a user action or a manual "Refresh" button.


Storage

The Storage API lets you upload, list, and manage files. Storage is available in two scopes:

ScopeAccessUse case
Appclient.app.storageFiles for your app (any component; list, upload, get, delete).
Recordclient.context.record.storageFiles attached to the current record (Record components only; list and upload).

App storage is available in all component types. Record storage is only available after initializeContext() in Record components and is scoped to the current record.


app.storage

App-level file storage. Available in Record, Side Menu, and Global Menu components.

MethodDescription
app.storage.uploadFile()Upload a file; returns url and id.
app.storage.getFile()Get a file by ID (returns a File).
app.storage.deleteFile()Delete a file by ID.
app.storage.getFiles()List files with pagination.

app.storage.uploadFile

Upload a file to app storage.

const { url, id } = await client.app.storage.uploadFile(file);

Parameters: file — a File (e.g. from <input type="file">).

Returns: Promise<{ url: string; id: string }>.


app.storage.getFile

Retrieve a file by ID.

const file = await client.app.storage.getFile(fileId);

Parameters: fileIdstring (ID returned from uploadFile or from file metadata).

Returns: Promise<File>.


app.storage.deleteFile

Remove a file from app storage.

await client.app.storage.deleteFile(fileId);

Parameters: fileIdstring.


app.storage.getFiles

List files with optional pagination.

const result = await client.app.storage.getFiles({
  pageNumber: 1,
  pageSize: 20,
});
// result: { data: FileMetadata[], pageNumber, pageSize, isLastPage }

Parameters (optional):

ParameterTypeDescription
pageNumbernumberPage number (optional).
pageSizenumberItems per page (optional).

Returns: Promise<GetFilesResponse>{ data: FileMetadata[]; pageNumber: number; pageSize: number; isLastPage: boolean }.

FileMetadata: { url: string; id: string; name: string; size: number }.


record.storage

Record-scoped file storage. Available only in Record components via client.context.record.storage after initializeContext(). Use it to attach files to the current record.

MethodDescription
record.storage.uploadFile()Upload a file to the current record.
record.storage.getFiles()List files for the current record.

Record storage does not expose getFile or deleteFile; files are managed in the context of the record.

record.storage.uploadFile

Upload a file to the current record.

await client.initializeContext();
const { record } = client.context;
if (record?.storage) {
  const { url, id } = await record.storage.uploadFile(file);
}

Parameters: file — a File.

Returns: Promise<{ url: string; id: string }>.


record.storage.getFiles

List files attached to the current record, with optional pagination.

await client.initializeContext();
const { record } = client.context;
if (record?.storage) {
  const result = await record.storage.getFiles({
    pageNumber: 1,
    pageSize: 20,
  });
  console.log(result.data); // FileMetadata[]
  console.log(result.isLastPage);
}

Parameters (optional): Same as app.storage.getFiles{ pageNumber?: number; pageSize?: number }.

Returns: Promise<GetFilesResponse> — same shape as app storage.


Storage examples

Example 1: Upload and list in a Record component

await client.initializeContext();
const { record } = client.context;
// Upload a file to the current record
if (record?.storage) {
  const { id, url } = await record.storage.uploadFile(file);
}

// List files on the record
if (record?.storage) {
  const { data, isLastPage } = await record.storage.getFiles({ pageSize: 10 });
  data.forEach((f) => console.log(f.name, f.size));
}

Example 2: App-level storage with pagination

// List all app files, page by page
const res = await client.app.storage.getFiles({ pageNumber: 1, pageSize: 20 });

System Methods

In addition to data operations, the SDK provides methods to control UI elements in the Fireberry platform.

MethodDescription
system.badge.show()Display a badge notification
system.badge.hide()Hide the badge notification
system.callbar.show()Display a callbar with call info
system.callbar.hide()Hide the callbar

Badge

Display notification badges to alert users about important information or status updates.

Show Badge

Display a badge with a number and type indicator.

await client.system.badge.show({
  number: 5,
  badgeType: "info",
});

Parameters:

ParameterTypeDescription
numbernumberThe number to display on the badge
badgeType'success' | 'warning' | 'error' | 'info'Visual style of the badge

Badge Types:

  • success — Green badge for positive notifications
  • warning — Yellow badge for warnings
  • error — Red badge for errors or critical alerts
  • info — Blue badge for informational messages

Example:

// Show unread messages count
await client.system.badge.show({
  number: 12,
  badgeType: "info",
});

// Show error count
await client.system.badge.show({
  number: 3,
  badgeType: "error",
});

Hide Badge

Remove the badge notification.

await client.system.badge.hide();

Callbar

Display a callbar interface with call information and related records.

Show Callbar

Display a callbar with call status and associated record information.

await client.system.callbar.show({
  callInfo: {
    number: 1234567890,
    status: "Talking",
  },
  objectConfig: [
    {
      objectType: "1",
      order: 1,
      fields: [{ name: "accountname" }, { name: "email" }],
    },
  ],
  placemment: "bottom-end",
});

Parameters:

ParameterTypeDescription
callInfoCallInfoInformation about the current call
objectConfigObjectConfig[]Configuration for displaying records
placemment'bottom-start' | 'bottom-end'Position of the callbar

CallInfo:

FieldTypeDescription
numbernumberPhone number of the call
status'Talking' | 'Ringing' | 'Missed' | 'Dialing'Current status of the call

ObjectConfig:

FieldTypeDescription
objectTypestringThe object type to display records from
fieldsField[]Array of fields to show
ordernumberDisplay order (optional)

Field:

FieldTypeDescription
namestringField name

Example:

// Display callbar for incoming call
await client.system.callbar.show({
  callInfo: {
    number: 5551234567,
    status: "Ringing",
  },
  objectConfig: [
    {
      objectType: "1",
      fields: [{ name: "accountname" }, { name: "phone" }, { name: "email" }],
    },
  ],
  placemment: "bottom-end",
});

Hide Callbar

Remove the callbar from display.

await client.system.callbar.hide();

Toasts

Display temporary notification messages to provide feedback to users about actions, status updates, or important information.

Show Toast

Display a toast notification with customizable content, styling, and behavior.

await client.system.toast.show({
  content: "Record saved successfully!",
  toastType: "success",
  placement: "top-end",
  withCloseButton: true,
  autoDismissTimeout: 5000,
});

Parameters:

ParameterTypeDescriptionDefault
contentstringThe message text to display in the toast
toastType'success' | 'warning' | 'error' | 'info'Visual style of the toast
placementToastPlacementPosition where the toast appears
withCloseButtonbooleanWhether to show a close button (optional)true
autoDismissTimeoutnumberTime in milliseconds before auto-dismiss (optional)3000

Toast Types:

  • success — Green toast for successful operations
  • warning — Yellow toast for warnings or cautions
  • error — Red toast for errors or failures
  • info — Blue toast for informational messages

Placement Options:

PlacementDescription
top-leftTop left corner (absolute)
top-centerTop center
top-rightTop right corner (absolute)
top-startTop start (left in LTR, right in RTL)
top-endTop end (right in LTR, left in RTL)
bottom-leftBottom left corner (absolute)
bottom-centerBottom center
bottom-rightBottom right corner (absolute)
bottom-startBottom start (left in LTR, right in RTL)
bottom-endBottom end (right in LTR, left in RTL)
📘

Note

Use start/end placements for internationalized applications to automatically adapt to right-to-left (RTL) languages. Use left/right for absolute positioning.

Optional Parameters:

  • withCloseButton — When true, displays an X button to manually dismiss the toast. Defaults to true
  • autoDismissTimeout — Time in milliseconds before the toast automatically disappears. Defaults to 3000 (3 seconds). Set to 0 or omit to disable auto-dismiss

Examples:

// Success notification with auto-dismiss
await client.system.toast.show({
  content: "Changes saved successfully!",
  toastType: "success",
  placement: "top-end",
  autoDismissTimeout: 5000,
});

// Error notification with close button (using defaults)
await client.system.toast.show({
  content: "Failed to save. Please try again.",
  toastType: "error",
  placement: "top-center",
  // withCloseButton defaults to true
  // autoDismissTimeout defaults to 3000ms
});

// Warning without auto-dismiss
await client.system.toast.show({
  content: "This action cannot be undone.",
  toastType: "warning",
  placement: "bottom-start",
  autoDismissTimeout: 0, // Disable auto-dismiss
});

// Info notification without close button
await client.system.toast.show({
  content: "Processing in background...",
  toastType: "info",
  placement: "bottom-end",
  withCloseButton: false,
});

Hide Toast

Manually dismiss the currently displayed toast.

await client.system.toast.hide();

TypeScript Support

The SDK includes full TypeScript definitions. You can provide two generic type parameters for stronger typing:

import FireberryClientSDK from "@fireberry/sdk/client";

const client = new FireberryClientSDK<ResponseDataType, SettingsType>();
//                                    ^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^
//                                    1st: API data     2nd: Settings shape
GenericPurposeDefault
ResponseDataTypeShape of data returned by api.query(), api.create(), etc.any
SettingsTypeShape of the value stored by app.settings.get() / app.settings.set()JsonValue

Example

import FireberryClientSDK from "@fireberry/sdk/client";

// Define the shape of records your API calls return
interface Account {
  accountid: string;
  accountname: string;
  status: string;
  telephone1?: string;
}

// Define the shape of your app's settings
interface MySettings {
  theme?: string;
  pinnedItems?: { id: string; name: string }[];
}

// Create a fully-typed client
const client = new FireberryClientSDK<Account, MySettings>();
await client.initializeContext();

// API responses are typed
const results = await client.api.query(1, {
  fields: "accountid,accountname,status",
  query: 'status = "active"',
});
// results.data is typed as Account

// Settings are typed
const settings = await client.app.settings.get();
// settings is typed as MySettings

📘

Note

If you only need to type API responses and don't use the Settings API, you can pass a single generic as before: new FireberryClientSDK<Account>().


Extending SDK Types with Declaration Merging

If your Fireberry organization has custom objects, you can register them with the SDK's type system using TypeScript declaration merging. This gives you type-safe object type IDs across your entire app — the compiler will catch typos and invalid object types.

Step 1. Create a declaration file that augments the Objects interface with your custom object types:

// fireberry.d.ts
import "@fireberry/sdk/client";

declare module "@fireberry/sdk/client" {
  interface Objects {
    shipment: 1001;
    warehouse: 1002;
  }
}

Step 2. Create a runtime constants object that includes your custom entries alongside the built-in ones:

// objects.ts
import { OBJECTS } from "@fireberry/sdk/client";
import type { Objects } from "@fireberry/sdk/client";

export const objects = {
  ...OBJECTS,
  shipment: 1001,
  warehouse: 1002,
} as const satisfies Objects;

The satisfies Objects check ensures that your runtime values match the types you declared in Step 1. If they drift apart, TypeScript will flag it.

Step 3. Use your constants with SDK methods — TypeScript now recognizes them as valid object types:

import FireberryClientSDK from "@fireberry/sdk/client";
import { objects } from "./objects";

const client = new FireberryClientSDK();
await client.initializeContext();

const fields = await client.api.metadata.getFields(objects.shipment);
const results = await client.api.query(objects.warehouse, {
  fields: "warehouseid,name,location",
  query: 'location = "US-East"',
});
📘

Tip

Make sure your tsconfig.json include array covers the .d.ts file. If the file is in src/, the default "include": ["src"] is sufficient.


Complete Example

A React component that displays and updates data:

import { useEffect, useState } from "react";
import FireberryClientSDK from "@fireberry/sdk/client";

function TaskList() {
  const [client] = useState(() => new FireberryClientSDK());
  const [tasks, setTasks] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function init() {
      await client.initializeContext();

      const result = await client.api.query(1, {
        fields: "accountid,accountname,status",
        query: 'status != "completed"',
      });

      if (result.success) {
        setTasks(Array.isArray(result.data) ? result.data : [result.data]);
      }
      setLoading(false);
    }

    init();
  }, [client]);

  const completeTask = async (taskId) => {
    const result = await client.api.update(1, taskId, { status: "completed" });
    if (result.success) {
      setTasks(tasks.filter((t) => t.accountid !== taskId));
    }
  };

  if (loading) return <div>Loading...</div>;

  return (
    <ul>
      {tasks.map((task) => (
        <li key={task.accountid}>
          {task.accountname}
          <button onClick={() => completeTask(task.id)}>Complete</button>
        </li>
      ))}
    </ul>
  );
}

Cleanup

When your component unmounts, clean up the SDK:

// Clean up event listeners and pending requests
client.destroy();

In React:

useEffect(() => {
  const client = new FireberryClientSDK();
  // ... setup

  return () => client.destroy();
}, []);

Timeouts

API requests timeout after 60 seconds by default. If a request takes longer, the promise rejects with a timeout error.


Next Steps

  • Design System — Build UIs with Fireberry components
  • Component Types — Understand context availability by component type
  • CLI — Deploy and manage your apps