← All documentationContents ↓

TypeScript SDK

The official @simply360/sdk package — installation, configuration, the resource clients it exposes, and code examples for common operations.

Overview

The official Simply360 TypeScript SDK (@simply360/sdk) provides a typed client for the Simply360 REST API. It works in Node.js 18+, modern browsers, and edge runtimes. The SDK is a thin, typed wrapper over the HTTP API — one resource client per top-level resource, each method maps to a single HTTP request.

Installation

The SDK ships as the @simply360/sdk package. It is currently in developer preview and is not yet published to the public npm registry — contact developers@simply360.app for access, and watch the API Changelog for the public release announcement. Once the package is available in your registry, install it with:

npm install @simply360/sdk

Basic Setup

import { Simply360 } from '@simply360/sdk';

const s360 = new Simply360({
  apiKey: process.env.S360_API_KEY!, // s360_live_...
});

Configuration Options

OptionTypeDefaultDescription
apiKey string Static API key. Provide either apiKey or getToken.
getToken () => Promise<string> | string Dynamic token provider, used for Cognito JWT or OAuth2 flows where the token refreshes.
teamSimplyId string Team simplyId, sent as X-Team-Id. Required when using a Cognito JWT.
allowUnauthenticated boolean false Allow requests without credentials to documented unauthenticated public endpoints.
baseUrl string https://api.simply360.app Override to target a non-production environment.
timeout number 30000 Request timeout in milliseconds.
headers Record<string, string> {} Extra headers added to every request.

Switching Teams Mid-Session

If you build an app that switches between teams (e.g., the Simply360 webapp), you can update the team header on the existing client without recreating it:

s360.setTeamSimplyId('NEW0-TEAM-SIMP');

The older setTeamId method still works but is a deprecated alias for setTeamSimplyId.

Resource Clients

The Simply360 class exposes one resource client per top-level resource. The most commonly used clients are:

PropertyResource
s360.dataRecordsData records (CRUD, search, batch, audit log, files, children/related)
s360.dataCollectionsData collections and field schemas
s360.dataViewsData views and view records
s360.dataWizardsData wizards, schemas, and execution
s360.messagesOutgoing messages and message status
s360.messageTemplatesMessage templates (CRUD)
s360.conversationsConversations and conversation messages
s360.reportsReports and report execution
s360.automationsAutomations, executions, manual triggers
s360.filesFile metadata, download URLs, image variants
s360.actionTagsSimplyTags and scan history
s360.webhooksWebhook subscriptions and delivery logs
s360.apiKeysAPI keys (create, list, rotate, revoke)
s360.sessionTokensSession tokens (create, list, revoke)
s360.usersUsers (current, list, get)
s360.auditLogAudit log entries
s360.constituentAuthConstituent identity verification flows
s360.appStoreApp-store purchase verification and entitlements

Newer platform surfaces follow the same pattern — the SDK also exposes clients such as s360.signingRequests, s360.billing, s360.paymentAccounts, s360.contentTemplates, s360.styleKits, s360.websites (plus the related Website Studio clients), s360.actionCredentials, and s360.teamAgent. Every method maps to a single documented REST endpoint.

Presigned File Uploads

Some API methods return a short-lived presigned upload URL for direct-to-storage file staging. Use the SDK helper to PUT the bytes instead of writing a raw fetch call in app code:

import { uploadPresignedFile } from '@simply360/sdk';

const presigned = await s360.dataRecords.presignAiGraphUpload({
  fileName: file.name,
  contentType: file.type || 'application/octet-stream',
});

await uploadPresignedFile({
  uploadUrl: presigned.data.uploadUrl,
  body: file,
  contentType: file.type || 'application/octet-stream',
});

The helper resolves after any 2xx response and throws when the storage service returns a non-2xx response.

Common Operations

List Records

const response = await s360.dataRecords.list({
  dataCollectionSimplyId: 'XXXX-XXXX-XXXX',
  limit: 50,
  sort: '-createdAt',
  fields: 'FLDS-FRST-NAME,FLDS-LAST-NAME,FLDS-EMAL-ADDR',
});

// response.data: DataRecord[]
// response.meta.pagination.total: number

Get a Record

const response = await s360.dataRecords.get('WXYZ-5678-IJKL');
console.log(response.data.fields);

Create a Record

const response = await s360.dataRecords.create({
  dataCollectionSimplyId: 'XXXX-XXXX-XXXX',
  fields: {
    'FLDS-FRST-NAME': 'Jane',
    'FLDS-LAST-NAME': 'Doe',
    'FLDS-EMAL-ADDR': 'jane@example.com',
  },
});

Update a Record (partial)

await s360.dataRecords.update('WXYZ-5678-IJKL', {
  fields: { 'FLDS-EMAL-ADDR': 'jane.doe@newdomain.com' },
});

Replace a Record (full overwrite)

await s360.dataRecords.replace('WXYZ-5678-IJKL', {
  fields: { /* full set of fields */ },
});

Delete a Record

await s360.dataRecords.delete('WXYZ-5678-IJKL');

Search Records

const response = await s360.dataRecords.search({
  dataCollectionSimplyId: 'XXXX-XXXX-XXXX',
  query: 'Jane',
  filters: [
    { field: 'FLDS-EMAL-ADDR', operator: 'contains', value: '@example.com' },
  ],
  limit: 10,
});

Batch Operations

await s360.dataRecords.batchCreate({
  dataCollectionSimplyId: 'XXXX-XXXX-XXXX',
  records: [
    { fields: { 'FLDS-NAME': 'A' } },
    { fields: { 'FLDS-NAME': 'B' } },
  ],
});

await s360.dataRecords.batchUpdate({
  records: [
    { dataRecordSimplyId: 'WXYZ-5678-IJKL', fields: { 'FLDS-EMAL': 'a@x.com' } },
    { dataRecordSimplyId: 'MNOP-9012-QRST', fields: { 'FLDS-EMAL': 'b@x.com' } },
  ],
});

await s360.dataRecords.batchDelete({
  dataRecordSimplyIds: ['WXYZ-5678-IJKL', 'MNOP-9012-QRST'],
});

Working with Collections

const collections = await s360.dataCollections.list();

const detail = await s360.dataCollections.get('XXXX-XXXX-XXXX');
console.log(detail.data.fields); // included by default

const fields = await s360.dataCollections.listFields('XXXX-XXXX-XXXX');

Executing a Data View

Get the records that match a saved view's filters and sort:

const records = await s360.dataViews.getRecords('VIEW-1234-ABCD', {
  limit: 50,
  offset: 0,
});

Simply Action Tags

The actionTags client exposes typed URL/Link Page CRUD, retained scan analytics, CSV import/export, custom domains, and signed webhooks. Standalone API-key use requires the 500-Action band or higher. Account, Team User, saved-payment-method, and billing methods are first-party Cognito Team Admin operations even though they share generated SDK types; an API key cannot use them.

const action = await s360.actionTags.create({
  name: 'Visitor resources',
  actionType: 'LINK_PAGE',
  linkPageSettings: {
    title: 'Welcome',
    theme: 'SYSTEM',
    callsToAction: [{ label: 'Hours', url: 'https://example.org/hours' }],
  },
});

const dryRun = await s360.actionTags.importCsv({ csv, dryRun: true });
if (dryRun.data.validRows === dryRun.data.totalRows) {
  await s360.actionTags.importCsv({
    csv,
    dryRun: false,
    idempotencyKey: crypto.randomUUID(),
  });
}

Reuse an import idempotency key only when retrying the exact same CSV. Webhook creation returns its HMAC secret once. Domain listing is a pure read; verifyDomain() is the explicit reconciliation mutation. Powered record/Wizard fields are rejected for a standalone Team rather than silently removed.

Pagination

The SDK uses manual offset pagination — advance the offset yourself until response.meta.pagination.hasMore is false. Maximum page size is 100. See Pagination, Sorting & Field Selection for details.

let offset = 0;
const limit = 100;
const all: DataRecord[] = [];

while (true) {
  const response = await s360.dataRecords.list({ dataCollectionSimplyId, limit, offset });
  all.push(...response.data);
  if (!response.meta.pagination.hasMore) break;
  offset += limit;
}

Error Handling

The SDK throws ApiError for any non-2xx response. Responses that cannot be parsed (for example, an HTML error page from an intermediary) are also surfaced as ApiError rather than a raw parse error. See Error Handling for details.

import { Simply360, ApiError } from '@simply360/sdk';

try {
  await s360.dataRecords.get('NONE-XIST-ENT0');
} catch (error) {
  if (error instanceof ApiError) {
    console.error(error.statusCode, error.code, error.message);
  }
}

TypeScript Types

All request and response types are exported from the SDK. Use them to type your own functions and data structures.

import type {
  Simply360Config,
  ApiResponse,
  ApiListResponse,
  DataRecord,
  DataCollection,
  ListRecordsParams,
  CreateRecordParams,
  SearchRecordsParams,
} from '@simply360/sdk';

function processRecords(response: ApiListResponse<DataRecord>) {
  for (const record of response.data) {
    // record is fully typed
    console.log(record.dataRecordSimplyId, record.fields);
  }
}

Targeting a Different Environment

The SDK targets production by default. To use an enabled non-production environment, override baseUrl with the matching API base URL:

const s360 = new Simply360({
  apiKey: process.env.S360_API_KEY!,
  baseUrl: process.env.SIMPLY360_API_URL ?? 'https://api.simply360.app',
});
# Staging
export SIMPLY360_API_URL="https://api.staging.simply360.app"

# Development
export SIMPLY360_API_URL="https://api.dev.simply360.app"

Staging is a paid add-on or an included offer benefit. Development is a separate, limited preview add-on or beta grant; Staging access does not include Development. Use an API key created in the selected environment — credentials do not cross environment boundaries. See Environments for the complete endpoint and access matrix.