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
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.
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)const result = await serenities.entities.Tasks.create({
Title: 'New Task',
Status: 'pending',
Priority: 'high'
});
// Returns: { success: true, id: 'new-row-id' }await serenities.entities.Tasks.update('row-id', {
Status: 'completed'
});
// Returns: { success: true }await serenities.entities.Tasks.delete('row-id');
// Returns: { success: true }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.
const completed = await serenities.entities.Tasks.filter({
Status: 'completed'
});const urgentOpen = await serenities.entities.Tasks.filter({
Status: 'open',
Priority: 'high',
});const results = await serenities.entities.Products.filter({
Price: { $gt: 10, $lte: 100 },
Category: { $in: ['Electronics', 'Books'] },
Name: { $contains: 'Pro' },
});const recent = await serenities.entities.Tasks.filter(
{ Status: 'open' },
{ sort: '-createdAt', limit: 20, offset: 0 }
);Available Operators
| Operator | Description | Example |
|---|---|---|
| $eq | Equals | { Status: { $eq: "done" } } |
| $ne | Not equals | { Status: { $ne: "" } } |
| $gt | Greater than | { Price: { $gt: 10 } } |
| $gte | Greater or equal | { Price: { $gte: 10 } } |
| $lt | Less than | { Price: { $lt: 100 } } |
| $lte | Less or equal | { Price: { $lte: 100 } } |
| $contains | String contains | { Name: { $contains: "Pro" } } |
| $in | Value in array | { Status: { $in: ["a", "b"] } } |
| $isEmpty | Field is empty/null | { Notes: { $isEmpty: true } } |
| $isNotEmpty | Field 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.
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() responsesawait serenities.entities.Tasks.link('task-id', 'Project', ['proj1', 'proj2']);
// Returns: { success: true, linked: 2 }await serenities.entities.Tasks.unlink('task-id', 'Project', ['proj2']);
// Returns: { success: true, unlinked: 1 }const { rows, total, hasMore } = await serenities.entities.Tasks.getLinked(
'task-id',
'Project',
{ limit: 50, offset: 0 }
);
// rows = [{ id, Name, Status, ... }] — full target row datauser - Authentication
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;// 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: '...' }// 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 } }await serenities.user.update({ name: 'New Name', company: 'New Company' });
// Returns: { success: true, user: { id, email, name, role, profileData } }await serenities.user.logout(); // Clears token and reloads page
// 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 a File object (e.g., from <input type="file">)
const result = await serenities.files.upload(fileObj, {
folder: 'uploads',
});
// Returns: { id, url, filename, ... }// 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
// 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;// 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