π What You Can Build
Database Integration
A fully featured API from your existing database (no schema lock-in)
JWT Authentication
Built-in JWT authentication with access & refresh tokens
Multi-Tenant Support
Optional row-level security (RLS) for multi-tenant applications
Email Flows
Password reset flows via your SMTP provider
Social Login
Google & GitHub authentication with your OAuth credentials
Role-Based Access
RBAC with roles, permissions, and user mappings
You control the table and column namesβour mapping UI adapts to your schema.
π Prerequisites
- Database Access: You have DB credentials (host, port, DB type, DB name, username, password) for one or more stages (Development / Staging / Production)
- Database Permissions: You are authorized to read/write the target database
- SMTP Configuration: If using email features: SMTP host, port, username/password, From address & From name, Base URL (e.g., your website/app origin), and Password Reset Path (must include a %s placeholder for the token)
- OAuth Setup: If using social login: Google & GitHub OAuth client ID/secret and approved redirect URLs
β‘ Workflow at a Glance
Create Project
A simple container for your connections and settings
Create Connection
Point to a database for a specific stage
Sync Schema
We scan tables, columns, types, PK/FK/constraints
Authentication Settings
Enable auth, set JWT secrets & expiries, optional RLS, email settings
User Mapping
Map your tables/columns to what the auth layer expects
Social Auth
Enter Google/GitHub OAuth credentials (optional)
Authorization
Define roles & permissions if you need RBAC
Generate & Use API
Your endpoints are live; use API keys or JWTs from your app/SDKs
1οΈβ£ Step 1 β Create a Project
- Go to Projects β New Project
- Give it a name and (optionally) a description
- Think of a project as a workspace that can contain multiple connections (e.g., dev/staging/prod)
Tip: A project serves as a container for all your API configurations across different environments.
2οΈβ£ Step 2 β Create a Connection
Navigate to Project β Connections β New Connection and fill the form:
Required Fields
- Stage: Development / Staging / Production
- DB Type: e.g., PostgreSQL, MySQL, etc.
- Host & Port: Your database server hostname and port number
- Database Name: The specific database to connect to
- Username & Password: Database credentials for access
Tips
- Credentials: Use read/write credentials. The platform may add columns when you enable tenant scoping
- Firewall: If your DB is behind a firewall, allow access from the platform's egress IPs (if applicable)
Click Create. The connection appears in your project.
Security Note: Ensure your database credentials are stored securely and follow your organization's security policies.
3οΈβ£ Step 3 β Sync the Schema
- Click Sync Schema on your connection
- We scan and import tables, columns, types, and constraints (PKs, FKs, uniques, nullability)
- You'll see a visual summary of your schema
Why this matters
Schema Metadata: The mapping UI (auth, roles/permissions, etc.) uses this metadata to let you select your own tables/columns.
Automatic Detection: The platform will automatically identify primary keys, foreign keys, and data types from your existing schema.
4οΈβ£ Step 4 β Authentication Settings
Open Authentication β General Settings. There are three cards.
4.1 General Security
Toggle and configure:
- Enable Authentication: Turns on JWT-based auth for protected routes
- Enable Row-Level Security (RLS): Scope records by tenant (see 4.2)
- JWT Secret: A long, random string used to sign tokens. Change only with caution
- Access Token Expiry: e.g.,
1h - Refresh Token Expiry: e.g.,
7h - Password Token Expiry: e.g.,
1h(for "forgot password" links) - Public Routes (Exclude from Auth): Comma-select API paths that do not require auth (e.g.,
/auth/login,/productsif public)
Note: Public routes bypass JWT checks but still respect any business rules in your handlers.
4.2 Tenant Scoping (Row-Level Security)
Use this when multiple users belong to an account/tenant, or when each user must only see their own records.
- Choose Tenant Model: Pick the table representing tenants (e.g.,
accountsfor org-scoped apps orusersfor simple single-user tenancy) - Default Tenant Field Name: Choose the column name to add for scoping (e.g.,
account_idoruser_id) - Exclude Tables: Select tables not to receive the tenant column (e.g.,
products,countries, other global catalogs)
What happens
- We add the chosen tenant column to selected tables and enforce it in generated queries when RLS is enabled
- On each request, the user's tenant is injected into filters so they only see/write what they should
Tip: If your schema already contains a tenant column with a different name, you can point the mapping to it during user/role mappings. Avoid duplicating tenant columns.
4.3 Email (SMTP) Settings
Used for password reset emails.
- SMTP Host & Port: Your email provider's SMTP server details
- SMTP Username & Password: Credentials for email sending
- From Address: e.g.,
no-reply@yourdomain.com - From Name: e.g.,
Your App - Base URL: The origin where users complete reset (e.g.,
https://siteclear.mobix-sites.co.uk) - Password Reset Path: Must include
%splaceholder for the tokenβe.g.,/reset-password?token=%s
How it works
When users request a reset, the API generates a token and emails a link: {BaseURL}{PasswordResetPath} with %s replaced by the token.
5οΈβ£ Step 5 β User Mapping (9 Cards)
Map your own tables/columns to what our auth and RBAC layers expect. You can name the tables anything; mapping tells the platform how to use them.
Important: Passwords should be stored hashed in your DB. The platform never requires plain-text passwords.
8.1 User (required for authentication)
- Required columns:
id,email,firstname,password,isactive - Map each to the corresponding columns in your user table
8.2 Password Reset Token (required for reset password flow)
- Required columns:
id,userid(FK to User),token,expiresAt,usedAt,createdAt
8.3 Session (optional; for DB-backed sessions)
- Columns:
id,userId,refreshToken,expiresAt,lastUsed
8.4 API Keys (recommended for SDK/server-to-server)
- Columns:
id,userId(oraccountId),key,createdAt,revokedAt(nullable)
8.5 Social Profile (optional; for social login)
- Columns:
id,userId,provider(google/github),providerUserId,email,createdAt
8.6 Role
- Columns:
id,name,description
8.7 Permission
- Columns:
id,name,description
8.8 Role Permission (join table)
- Columns:
id,roleId,permissionId
8.9 User Role (assign roles to users)
- Columns:
id,userId,roleId
Extensible: You can extend these tables (e.g., add tenant_id). Just map the required fields so the platform can operate.
Important: Ensure all 9 mapping cards are properly configured for full authentication functionality.
6οΈβ£ Step 6 β Social Authentication (Google/GitHub)
- Open Authentication β Social
- Enter your Google and/or GitHub OAuth Client ID/Secret
- Configure redirect/callback URLs in your provider dashboards to match the URLs shown in the platform
- Once enabled and mapped (see 8.5), users can sign in/up via social providers
GitHub OAuth Setup & Google OAuth Setup
Configuration Steps: Make sure the redirect URI in Google/GitHub console exactly matches the URI shown in our platform for proper callback handling.
7οΈβ£ Step 7 β Authorization (Roles & Permissions)
Turn on RBAC to restrict which users can access which actions/routes.
- Go to Authorization in the project
- Create Roles (e.g.,
admin,manager,member) - Create Permissions (e.g.,
orders.read,orders.create,orders.update,orders.delete) - Link them via Role Permission
- Assign roles to users via User Role
Creating Roles & Defining Permissions
Admin Role
Full system access
Manager Role
Departmental permissions
Member Role
Limited user access
At runtime
- The API checks the caller's roles β permissions map to allow/deny actions
- Works alongside RLS: a user might be allowed to read orders, but only within their tenant scope
8οΈβ£ Step 8 β Generate, Deploy & Use the API
Once mapping & settings are complete:
Generate API
Create build and deploy your API
After your API is deployed, check the connection settings you will see your end point url
Receive Your Endpoints
You'll receive your complete API configuration
What You Get
- Base URL for your project/stage
- Auth endpoints (e.g.,
/auth/register,/auth/login,/auth/refresh,/auth/reset-password) - CRUD endpoints for your synced tables
Use SDKs or Direct Calls
Use the provided SDKs or call endpoints directly using any http library of your choice
Need SDKs fast? Jump to the Swain CLI section for installation and interactive generation guidance.
Typical Flows
- Register/Login β Get access & refresh tokens
- Use Access Token in
Authorization: Bearer <token>to call protected routes until it expires - Refresh using the refresh token to get a new access token
- Password Reset:
/auth/request-resetβ user receives email link β/auth/confirm-resetconsumes the token
Congratulations! Your API is now ready for production use. You can integrate it into your applications using the provided SDKs or direct HTTP requests.
π API Documentation
Interactive Swagger Documentation: Once your API is deployed, you'll have access to a complete interactive API documentation at:
{Your-API-Base-URL}/swagger/index.html
This Swagger interface allows you to:
- Explore all endpoints with detailed descriptions and examples
- Test API calls directly from your browser
- View request/response schemas for all endpoints
- Download OpenAPI specification for integration with other tools
π οΈ Swain CLI SDK Generation
swain_cli is our zero-setup command-line wrapper around OpenAPI Generator. Use it when you want fresh SDKs without leaving your terminal or installing Java yourself.
Quick Start
Install once, generate everywhere.
- Install:
pipx install swain_cli(Python 3.8+ or a virtualenv works great). - Authenticate: Run
swain_cli auth loginto sign in with your Swain username and password (the interactive wizard uses this credential flow when no token exists). For automation, pass a token via--tokenor--stdin. - Schema source: Without flags, the CLI pulls your deployed dynamic swagger; target a specific project with
--swain-project-id/--swain-connection-idor supply a local file using-i ./openapi.yaml. - Interactive mode: Launch
swain_cli interactiveto choose projects, connections, and languages, then preview (and optionally run) the assembled command. - Generators: Repeat
-l/--langfor every SDK you need and forward extra OpenAPI Generator options via-p,-c, or--generator-arg.
Interactive Mode Walkthrough
$ swain_cli interactive
[swain_cli] interactive SDK generation wizard
[swain_cli] press Ctrl+C at any time to cancel
[swain_cli] no authentication token configured.
? Sign in before continuing? Yes
? Username or email: founder@acme.co
? Password: ********
[swain_cli] stored access token (foun...co) in system keyring
? Select a project > Customer API (#123)
? Select a connection > #981 - prod-db (postgres, schema=public)
? Output directory (sdks): ./sdks
? Target languages (comma separated, e.g. python,typescript): python,typescript
[swain_cli] configuration preview
[swain_cli] swain base: https://api.swain.technology
[swain_cli] project: Customer API (#123)
[swain_cli] connection: #981 (prod-db)
[swain_cli] dynamic swagger: https://api.swain.technology/api/dynamic_swagger?project=123&connection=981
[swain_cli] output: ./sdks
[swain_cli] languages: python, typescript
[swain_cli] extra generator args: ['--global-property=apiDocs=false,apiTests=false,modelDocs=false,modelTests=false']
[swain_cli] engine: embedded
[swain_cli] skip validate: False
[swain_cli] verbose: False
[swain_cli] java options: -Xms2g -Xmx10g -XX:+UseG1GC
[swain_cli] equivalent command: swain_cli gen --swain-project-id 123 --swain-connection-id 981 -o ./sdks -l python -l typescript --generator-arg --global-property=apiDocs=false,apiTests=false,modelDocs=false,modelTests=false --java-opt -Xms2g --java-opt -Xmx10g --java-opt -XX:+UseG1GC
? Run generation now? Yes
[swain_cli] Fetching OpenAPI schemaβ¦
[swain_cli] Preparing embedded OpenAPI Generator (7.6.0)
[OpenAPI Generator] generating python client... done
[OpenAPI Generator] generating typescript client... done
[swain_cli] SDKs written to ./sdks/python and ./sdks/typescript
The wizard echoes the exact swain_cli gen command before running it. Prefer scripting? Use the same flags directly:
swain_cli gen --swain-project-id <project_id> --swain-connection-id <connection_id> \
-l python -l typescript -o ./sdks \
-p packageName=swain_api -p packageVersion=1.0.0
Tip: The first run downloads and caches a Temurin JRE plus the pinned OpenAPI Generator JAR. Prime CI with swain_cli engine install-jre, inspect caches via swain_cli doctor, and tweak JVM memory using repeatable --java-opt flags.
What Gets Generated
The command above writes one subfolder per generator (for example ./sdks/python and ./sdks/typescript). Each directory is ready to publish or vendor directly into your app:
./sdks/
python/
setup.py
README.md
swain_api/
__init__.py
api/<ModelName>_api.py # per-model API classes (list/create/get/update/delete)
model/<ModelName>.py # dataclasses for your tables
configuration.py # auth, base URL, timeouts
rest.py # HTTP transport helpers
typescript/
package.json
tsconfig.json
src/
apis/<ModelName>Api.ts # strongly-typed API clients per model
models/<ModelName>.ts # generated TypeScript interfaces
runtime.ts # shared fetch/config plumbing
Docs and test stubs stay disabled because swain_cli forwards --global-property=apiDocs=false,apiTests=false,modelDocs=false,modelTests=false by default.
Model APIs & Operations
The dynamic swagger builder expands every database model into a dedicated tag and gives each path a predictable operationId. When the SDKs are generated, those IDs become method names:
list<Model>:GET /<model>β paginated query returning<Model>FilterResponsewithdata,total,page,page_size, andtotal_pages.create<Model>:POST /<model>β insert a record and return the created payload.getById<Model>:GET /<model>/{id}β fetch a single row by primary key.updateById<Model>:PUT /<model>/{id}β update and return the row.deleteById<Model>:DELETE /<model>/{id}β delete and return the deleted record (when the backend exposes it).filter<Model>/filterQuery<Model>:POST /<model>/filterand/filter/queryβ submit JSON filter expressions and receive the typed filter response.count<Model>:GET /<model>/countβ lightweight count queries without fetching data.
Bulk helpers such as bulkCreate<Model> or bulkUpdate<Model> appear when the path ends in /bulk, and the generator removes the placeholder {model} parameter so each API class feels first-party.
Using the Generated SDKs
Python β install locally then instantiate the per-model client:
pip install ./sdks/python
from swain_api import Configuration, ApiClient
from swain_api.api import UsersApi
config = Configuration()
config.host = "https://your-api.example.com"
config.username = "user@example.com"
config.password = "super-secret" # OAuth2 password flow
with ApiClient(config) as api_client:
users_api = UsersApi(api_client)
page = users_api.list_users(page=1, page_size=25)
created = users_api.create_user({"email": "new@acme.co"})
TypeScript (Axios) β wire up configuration and call the same operations:
npm install ./sdks/typescript --legacy-peer-deps
import { Configuration, UsersApi } from "./sdks/typescript";
const config = new Configuration({
basePath: "https://your-api.example.com",
baseOptions: { auth: { username: "user@example.com", password: "super-secret" } },
});
const usersApi = new UsersApi(config);
const { data } = await usersApi.listUsers({ page: 1, pageSize: 25 });
Advanced Filtering & Querying
Use the JSON filter system documented in filtering_process.md to build rich queries. The payload below combines field expressions, logical groups, and relationship filters:
{
"expressions": [
{ "field": "status", "operator": "eq", "value": "active" },
{
"operator": "AND",
"expressions": [
{ "field": "createdAt", "operator": "gte", "value": "2024-01-01T00:00:00Z" },
{ "field": "role", "operator": "in", "value": ["admin", "manager"] }
]
},
{
"relationship": "Orders",
"scope": "filterParent",
"include": false,
"expressions": [
{ "field": "total", "operator": "gt", "value": 500 }
]
}
],
"sort": [
{ "field": "createdAt", "direction": "desc" }
]
}
Python β request a filtered page of users. page and page_size map to the query parameters documented in controller_filter.go.
filter_payload = {
"expressions": [
{"field": "status", "operator": "eq", "value": "active"},
{
"operator": "AND",
"expressions": [
{"field": "createdAt", "operator": "gte", "value": "2024-01-01T00:00:00Z"},
{"field": "role", "operator": "in", "value": ["admin", "manager"]}
]
}
],
"sort": [{"field": "createdAt", "direction": "desc"}]
}
page = users_api.filter_users(filter_payload=filter_payload, page=1, page_size=50)
for user in page.data:
print(user["email"], user.get("createdAt"))
TypeScript β the Axios client exposes matching helpers (filterUsers, page, pageSize).
const filterPayload = {
expressions: [
{ field: "status", operator: "eq", value: "active" },
{
operator: "AND",
expressions: [
{ field: "createdAt", operator: "gte", value: "2024-01-01T00:00:00Z" },
{ field: "role", operator: "in", value: ["admin", "manager"] }
]
}
],
sort: [{ field: "createdAt", direction: "desc" }]
};
const { data: usersPage } = await usersApi.filterUsers(filterPayload, 1, 50);
usersPage.data.forEach((user) => {
console.log(user.email, user.createdAt);
});
Need raw SQL-style expressions or column projections? Call filterQuery<Model> (maps to POST /{model}/filter/query) with the same payload to retrieve the generated SQL preview alongside the data.
Counts & Aggregations
POST /{model}/count returns a lightweight {"count": N} response that reuses the same filter grammar. Aggregations can be attached to filter payloads to compute grouped metrics:
{
"expressions": [
{
"relationship": "Orders",
"include": true,
"expressions": [
{ "field": "status", "operator": "eq", "value": "completed" }
],
"aggregations": {
"functions": [{ "type": "SUM", "field": "total", "alias": "order_value" }],
"group_by": ["user_id"]
}
}
],
"distinct": true
}
Python β count active users and inspect grouped totals.
count_payload = {
"expressions": [{"field": "status", "operator": "eq", "value": "active"}]
}
count_response = users_api.count_users(filter_payload=count_payload)
print(f"Active users: {count_response.count}")
agg_payload = {
"expressions": [{
"relationship": "Orders",
"include": True,
"aggregations": {
"functions": [{"type": "SUM", "field": "total", "alias": "order_value"}],
"group_by": ["user_id"]
}
}]
}
report = users_api.filter_users(filter_payload=agg_payload, page=1, page_size=1)
print(report.data[0]["aggregations"]["order_value"])
TypeScript β the count endpoint mirrors the Python client.
const { data: count } = await usersApi.countUsers({
expressions: [{ field: "status", operator: "eq", value: "active" }],
});
console.log(`Active users: ${count.count}`);
const aggPayload = {
expressions: [
{
relationship: "Orders",
include: true,
aggregations: {
functions: [{ type: "SUM", field: "total", alias: "order_value" }],
group_by: ["user_id"],
},
},
],
};
const { data: summary } = await usersApi.filterUsers(aggPayload);
summary.data.forEach((row) => {
console.log(row.user_id, row.aggregations?.order_value);
});
Bulk Operations & Mass Updates
CrudSQL exposes bulk create/update endpoints (/{model}/bulk) plus filter-driven mass update/delete operations (PUT/DELETE /{model}/filter). Responses follow the BulkCreateResult shape documented in controller_bulk.go with succeeded and failed arrays.
Python β capture multi-status bulk results and mass-update by filter.
bulk_body = [
{"email": "new-1@example.com", "password": "Secret123!"},
{"email": "new-2@example.com", "password": "Secret123!"}
]
bulk_result = users_api.bulk_create_users(bulk_body)
for success in bulk_result.succeeded:
print("created", success.id)
for failure in bulk_result.failed:
print("failed", failure.error.code, failure.error.message)
update_payload = {
"expressions": [{"field": "status", "operator": "eq", "value": "trial"}],
"updateData": {"status": "expired"}
}
users_api.update_by_filter_users(update_filter_payload=update_payload)
users_api.delete_by_filter_users(filter_payload={
"expressions": [{"field": "status", "operator": "eq", "value": "churned"}]
})
TypeScript β the bulk helpers return the same structure.
const bulkBody = [
{ email: "new-3@example.com", password: "Secret123!" },
{ email: "new-4@example.com", password: "Secret123!" },
];
const { data: bulk } = await usersApi.bulkCreateUsers(bulkBody);
bulk.succeeded.forEach((row) => console.log("created", row.id));
bulk.failed.forEach((row) => console.warn(row.error?.code, row.error?.message));
await usersApi.updateByFilterUsers({
expressions: [{ field: "status", operator: "eq", value: "trial" }],
updateData: { status: "expired" },
});
await usersApi.deleteByFilterUsers({
expressions: [{ field: "status", operator: "eq", value: "churned" }],
});
Bulk endpoints enforce the same RLS and validation rules as single-record operations. For large updates, pair a count<Model> call with your filter first to preview the scope before mutating data.
Because the swagger is generated dynamically from your synced schema, any new table or column you map is reflected the next time you run swain_cli gen. Regenerate whenever you deploy a new build to keep SDKs in lockstep with your API.
π Authentication Endpoints Usage
Once your API is deployed, you'll have access to several authentication endpoints. Here's how to use them and what's required for each to work properly.
Prerequisites: For authentication endpoints to work, you must have enabled authentication in Step 4 and mapped the User table in Step 5.
π User Registration
Endpoint: /auth/signup
Allows new users to create accounts in your system
Requirements:
- Authentication enabled in General Security settings
- User table mapped with required fields:
id,email,firstname,password,isactive
Payload Example:
{
"email": "user@example.com",
"password": "securePassword123",
"is_active": true,
"additional_data": {
"first_name": "John",
"last_name": "Doe"
// Any other data you want to map
}
}
πͺ User Login
Endpoint: /auth/login
Authenticates users and returns access & refresh tokens
Requirements:
- Authentication enabled in General Security settings
- User table mapped with email and password fields
- JWT settings configured (access/refresh token expiry)
Payload Example:
{
"email": "user@example.com",
"password": "securePassword123"
}
π Password Reset Request
Endpoint: /auth/password_reset
Sends a password reset email to the user
Requirements:
- Authentication enabled in General Security settings
- User table mapped with email field
- Password Reset Token table mapped with required fields:
id,userid,token,expiresAt,usedAt,createdAt - SMTP settings configured in Email settings (Step 4.3)
- Base URL and Password Reset Path properly configured with
%splaceholder
Payload Example:
{
"email": "user@example.com"
}
β Password Reset Confirmation
Endpoint: /auth/password_reset/confirm
Confirms the reset token and sets a new password
Requirements:
- All password reset requirements from above
- Valid reset token generated from
/auth/password_reset - Token not expired (within Password Token Expiry time)
- Token not already used (usedAt field should be null)
Payload Example:
{
"token": "abc123def456ghi789",
"new_password": "newSecurePassword123"
}
π§ SMTP Configuration Examples
For password reset emails to work, you need to configure SMTP settings. Here are examples for popular email providers:
SendGrid Configuration
- SMTP Host:
smtp.sendgrid.net - SMTP Port:
587 - SMTP Username:
apikey - SMTP Password:
[YOUR_SENDGRID_API_KEY] - From Address:
[YOUR_VERIFIED_SENDER_EMAIL] - From Name:
[YOUR_APP_NAME]
Gmail/Google Workspace
- SMTP Host:
smtp.gmail.com - SMTP Port:
587(TLS) or465(SSL) - SMTP Username:
your-email@gmail.com - SMTP Password:
your-app-password(use App Passwords, not account password)
Other Providers
Most email providers support SMTP. Check your provider's documentation for specific SMTP settings including host, port, and authentication requirements.
π§ͺ Testing Your Authentication Flow
Test Registration
Use /auth/signup to create a test user and verify it appears in your database
Test Login
Use /auth/login with the test user credentials to receive access and refresh tokens
Test Password Reset
Use /auth/password_reset to send a reset email and verify the email is received
Test Reset Confirmation
Use the token from the email with /auth/password_reset/confirm to set a new password
Pro Tip: Always test the complete authentication flow in a development environment before deploying to production.
Testing & Verification Checklist
Use this comprehensive checklist to verify that all components of your API are working correctly:
- Connection: Test DB connectivity from Connections page
- Schema: Confirm all tables, PK/FK, and types appear after Sync
- Auth: Toggle Enable Authentication and set token expiries
- RLS: Set tenant model & default tenant field; exclude public tables
- Email: Send a test reset email (SMTP settings + reset path with %s)
- User Mapping: Ensure all 9 cards are mapped
- Social: Verify OAuth callback works (login with Google/GitHub)
- Authorization: Create roles/permissions and verify restricted endpoints
- Public Routes: Confirm excluded paths are accessible without JWT
- SDK/API Keys: Create API key, call a test endpoint via SDK
β Troubleshooting & FAQs
Q: My password reset link doesn't work.
- Ensure Password Reset Path contains %s and Base URL is correct
- Check Password Token Expiry isn't too short
- Verify the Password Reset Token table mapping and that tokens are being written
Q: Users can see other tenants' data.
- Confirm RLS is enabled
- Verify the tenant field exists on relevant tables
- Ensure your app sets/derives the correct tenant for authenticated users
Q: Public routes are still asking for JWT.
In General Security, add the exact paths to Exclude from Auth. Paths are matched literally.
Q: Social login fails on callback.
- Make sure the redirect URI in Google/GitHub console exactly matches the URI shown in our platform
- Confirm the Social Profile table is mapped
π Security Best Practices
- JWT Security: Use a strong JWT secret (32+ random bytes). Rotate carefully
- Token Management: Keep access tokens short-lived (e.g., 15β60 minutes). Use refresh tokens for longevity
- Password Security: Store passwords hashed using a modern algorithm (e.g., bcrypt, argon2). Never store plain text
- Route Protection: Limit the number of public routes. Prefer auth unless a route is intentionally public
- Database Access: Restrict database access by network policy and least-privilege credentials
- SMTP Security: Use app passwords or provider-scoped credentials when possible
- Audit Trails: Log admin actions and use audit trails where needed
π Glossary
RLS (Row-Level Security)
Restricts records returned/modified to those owned by the authenticated user's tenant.
Tenant/Account
The organization or unit that owns data (users may belong to an account).
JWT
JSON Web Token; a signed token used to authenticate API requests.
RBAC
Role-Based Access Control; assigns permissions to roles, and roles to users.
SDK
Software Development Kit for easier integration with your API.
Example Values
- Base URL: https://siteclear.mobix-sites.co.uk
- Password Reset Path: /reset-password?token=%s
- Public Routes: /auth/login, /auth/signup, /auth/password_reset, /auth/password_reset/confirm
- Tenant Field: account_id (for account-scoped apps) or user_id (single-user tenancy)
You're done! Connect your app (or use our SDKs), create users, and start building features on top of your auto-generated API.