Serenities SDK

The official JavaScript/TypeScript SDK for building apps with Serenities.

Quick Start

The SDK is auto-configured in App Builder projects. Just import and use.

Import Patterns

Option 1: Default Import (Recommended)

import serenities from '../api/sdk';

await serenities.entities.Tasks.list();
await serenities.user.me();

Option 2: Named Imports

import { entities, user, email, files, payments, flows, serverFetch } from '../api/sdk';

// React Router helpers
import { Link, useNavigate, useLocation, useParams, useSearchParams } from '../api/sdk';

Option 3: Custom Client (NPM / API Key)

import { createClient } from '@serenities/sdk';

const client = createClient({
  projectId: 'your-project-id',
  apiBase: 'https://app.serenitiesai.com',
  apiKey: 'mk_live_xxx',  // optional, for server-side
});

await client.entities.Tasks.list();

Available Modules

entities

CRUD, filtering, and relations for your tables

user

Authentication and user management

email

Send transactional emails

files

File upload and management

payments

Stripe checkout, subscriptions, and billing portal

flows

Trigger automation flows

serverFetch

Server-side proxy for secret API keys

entities - Table CRUD

Access your database tables dynamically. Use table names exactly as they appear in your database.

List All Rows
const tasks = await serenities.entities.Tasks.list();
// Returns: [{ id, Title, Status, ... }, ...]

// With sorting, limit, and offset
const recent = await serenities.entities.Tasks.list('-createdAt', 50, 0);
// Params: sort (prefix - for desc), limit (default 100, max 1000), offset (default 0)
Create
const result = await serenities.entities.Tasks.create({
  Title: 'New Task',
  Status: 'pending',
  Priority: 'high'
});
// Returns: { success: true, id: 'new-row-id' }
Update
await serenities.entities.Tasks.update('row-id', {
  Status: 'completed'
});
// Returns: { success: true }
Delete
await serenities.entities.Tasks.delete('row-id');
// Returns: { success: true }
Get Single Row
const task = await serenities.entities.Tasks.get('row-id');
// Returns: { id, Title, Status, Priority, _linked: { ... } }

entities - Filtering

Filter rows with equality checks or operators. All conditions are combined with AND.

Simple Equality
const completed = await serenities.entities.Tasks.filter({
  Status: 'completed'
});
Multiple Conditions (AND)
const urgentOpen = await serenities.entities.Tasks.filter({
  Status: 'open',
  Priority: 'high',
});
With Operators
const results = await serenities.entities.Products.filter({
  Price: { $gt: 10, $lte: 100 },
  Category: { $in: ['Electronics', 'Books'] },
  Name: { $contains: 'Pro' },
});
With Sort / Limit / Offset
const recent = await serenities.entities.Tasks.filter(
  { Status: 'open' },
  { sort: '-createdAt', limit: 20, offset: 0 }
);

Available Operators

OperatorDescriptionExample
$eqEquals{ Status: { $eq: "done" } }
$neNot equals{ Status: { $ne: "" } }
$gtGreater than{ Price: { $gt: 10 } }
$gteGreater or equal{ Price: { $gte: 10 } }
$ltLess than{ Price: { $lt: 100 } }
$lteLess or equal{ Price: { $lte: 100 } }
$containsString contains{ Name: { $contains: "Pro" } }
$inValue in array{ Status: { $in: ["a", "b"] } }
$isEmptyField is empty/null{ Notes: { $isEmpty: true } }
$isNotEmptyField has value{ Title: { $isNotEmpty: true } }

entities - Linked Records (Relations)

Work with linked records between tables. Rows with link fields automatically include a _linked property with display values.

Auto-enriched _linked
const task = await serenities.entities.Tasks.get('row-id');
// task._linked = {
//   Project: [{ id: 'proj1', display: 'Website Redesign' }],
//   Assignee: [{ id: 'user1', display: 'John' }],
// }

// Also included in list() and filter() responses
Link Rows Together
await serenities.entities.Tasks.link('task-id', 'Project', ['proj1', 'proj2']);
// Returns: { success: true, linked: 2 }
Unlink Rows
await serenities.entities.Tasks.unlink('task-id', 'Project', ['proj2']);
// Returns: { success: true, unlinked: 1 }
Get Linked Rows (Paginated)
const { rows, total, hasMore } = await serenities.entities.Tasks.getLinked(
  'task-id',
  'Project',
  { limit: 50, offset: 0 }
);
// rows = [{ id, Name, Status, ... }] — full target row data

user - Authentication

Get Current User
const result = await serenities.user.me();
// Returns: { user: { id, email, name, role, profileData, emailVerified, createdAt } }
// Returns: null if not logged in
const currentUser = result?.user;
Login
// Redirect to hosted login page
serenities.user.login();
serenities.user.login('/dashboard');  // with redirect after login

// Login with credentials (for custom login forms)
const result = await serenities.user.loginWithCredentials(email, password);
// Returns: { success: true, token: '...' }
Sign Up
// 3 params: email, password, data object (name + custom profile fields)
const result = await serenities.user.signup(email, password, {
  name: 'John Doe',
  company: 'Acme Inc',
});
// Returns: { success: true, user: { id, email, name, role } }
Update Profile
await serenities.user.update({ name: 'New Name', company: 'New Company' });
// Returns: { success: true, user: { id, email, name, role, profileData } }
Logout
await serenities.user.logout();
// Clears token and reloads page
Password Reset & Email Verification
// Password reset flow
await serenities.user.forgotPassword(email);
await serenities.user.resetPassword(token, newPassword);

// Email verification
await serenities.user.verifyEmail(token);
await serenities.user.resendVerification(email);

email - Send Emails

await serenities.email.send({
  to: 'user@example.com',
  subject: 'Welcome!',
  body: '<h1>Hello</h1><p>Welcome to our app!</p>'
});

files - File Management

Upload
// Upload a File object (e.g., from <input type="file">)
const result = await serenities.files.upload(fileObj, {
  folder: 'uploads',
});
// Returns: { id, url, filename, ... }
List / Get / Delete
// List all files
const files = await serenities.files.list();

// Get file by ID
const file = await serenities.files.get('file-id');

// Delete file
await serenities.files.delete('file-id');

payments - Stripe Integration

Checkout & Products
// List available products
const products = await serenities.payments.listProducts();

// Create checkout session
const { url } = await serenities.payments.createCheckoutSession({
  priceId: 'price_xxxxx',
  successUrl: '/success',
  cancelUrl: '/cancel'
});
window.location.href = url;
Subscriptions & Billing Portal
// Get current user's subscription
const subscription = await serenities.payments.getSubscription();

// Open Stripe billing portal (manage subscription, update payment method)
const { url } = await serenities.payments.createPortalSession({
  returnUrl: '/settings'
});
window.location.href = url;

flows - Automation

// Invoke a flow by name
const result = await serenities.flows.invoke('send-welcome-email', {
  userId: 'user-123',
  email: 'user@example.com'
});

serverFetch - Server-Side Proxy

Call external APIs that require secret keys. Use {{ENV_VAR_NAME}} placeholders — they are resolved on the server, so secrets never reach the browser.

// Call an external API using secret env vars
const data = await serenities.serverFetch(
  'https://api.openai.com/v1/chat/completions',
  {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer {{OPENAI_API_KEY}}',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'gpt-4',
      messages: [{ role: 'user', content: 'Hello!' }],
    }),
  }
);

Complete Example

A full React component showing authentication, CRUD, and filtering:

import React, { useState, useEffect } from 'react';
import serenities from '../api/sdk';

export default function TasksPage() {
  const [tasks, setTasks] = useState([]);
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function loadData() {
      // Check auth
      const result = await serenities.user.me();
      setUser(result?.user || null);

      // Load only open tasks using filter
      const openTasks = await serenities.entities.Tasks.filter(
        { Status: { $ne: 'completed' } },
        { sort: '-createdAt', limit: 50 }
      );
      setTasks(openTasks);
      setLoading(false);
    }
    loadData();
  }, []);

  const addTask = async (title) => {
    const result = await serenities.entities.Tasks.create({
      Title: title,
      Status: 'pending'
    });
    // Reload the list
    const updated = await serenities.entities.Tasks.filter(
      { Status: { $ne: 'completed' } },
      { sort: '-createdAt', limit: 50 }
    );
    setTasks(updated);
  };

  const completeTask = async (id) => {
    await serenities.entities.Tasks.update(id, { Status: 'completed' });
    setTasks(tasks.filter(t => t.id !== id));
  };

  const deleteTask = async (id) => {
    await serenities.entities.Tasks.delete(id);
    setTasks(tasks.filter(t => t.id !== id));
  };

  if (loading) return <div className="p-8">Loading...</div>;

  if (!user) {
    return (
      <div className="p-8 text-center">
        <p className="mb-4">Please log in to view tasks</p>
        <button
          onClick={() => serenities.user.login()}
          className="px-4 py-2 bg-blue-500 text-white rounded"
        >
          Log In
        </button>
      </div>
    );
  }

  return (
    <div className="p-8 max-w-2xl mx-auto">
      <h1 className="text-2xl font-bold mb-6">My Tasks</h1>
      {/* ... rest of UI */}
    </div>
  );
}

NPM Installation (Coming Soon)

The SDK will be available on npm for use outside App Builder:

npm install @serenities/sdk
JavaScript/TypeScript SDK Documentation | Serenities AI