Overview
π What's New in v1.1.0 (March 2026)
- Project-based organisation: Resource strings are now grouped under Projects. Each tenant can have multiple projects (plan-limited), each with its own per-project language quota.
- New
/api/v1/projectsREST endpoints: full CRUD with subscription limit enforcement and a/language-limitsub-resource. project_idon all strings endpoints:GET,POST,GET /languages,GET /export/:lang, andPOST /importall accept an optionalproject_id.- Subscription plan limits updated:
max_projectsandlanguages_per_projectcolumns added to plan features, with tier values Free 1/1 β Starter 1/2 β Growth 2/4 β Enterprise 4/8. - Export / Import gated by plan: Free / Trial tenants do not have access to the export and import endpoints.
Tenlixor is a powerful multi-tenant SaaS platform built on Cloudflare Workers. Our REST API allows you to:
- Manage users with fine-grained permissions
- Handle authentication with JWT tokens and refresh tokens
- Organize translations into projects with per-project language limits
- Internationalize your application with multi-language resource strings
- Secure API access with API keys
- Store and manage files in R2 cloud storage
- Track all changes with comprehensive audit logging
π Built on Cloudflare
Tenlixor leverages Cloudflare's global edge network for ultra-low latency and high availability. Your API requests are processed at the edge, closest to your users.
Subscription Plans
Tenlixor uses a tiered subscription model. Limits are enforced at the API level and returned in response metadata.
| Plan | Projects | Users | Languages / Project | Key-Values | Export / Import | Audit Log Retention |
|---|---|---|---|---|---|---|
| Free / Trial | 1 | 1 | 1 | 500 | β | 7 days |
| Starter | 1 | 2 | 2 | 5,000 | β | 30 days |
| Growth | 2 | 5 | 4 | 25,000 | β | 90 days |
| Enterprise | 4 | 10 | 8 | 100,000 | β | 365 days |
π¦ Plan Limits in API Responses
The GET /api/v1/projects endpoint returns a limit_check object in its meta field showing current usage against your plan's project limit. Similarly, GET /api/v1/projects/:id/language-limit returns language usage per project.
Getting Started
π§ Tenant Registration
To use Tenlixor API, you need a tenant account. Contact the Tenlixor administrator at support@verbytes.com to create your tenant and receive your credentials.
1. Authenticate Your Requests
Use the credentials provided by the admin to login and get your access token:
POST /api/v1/auth/login
Content-Type: application/json
{
"tenant_slug": "your-company",
"email": "admin@yourcompany.com",
"password": "YourPassword123!",
"remember_me": false
}
{
"success": true,
"data": {
"user": {
"id": "usr_...",
"email": "admin@yourcompany.com",
"roles": ["tenant_admin"]
},
"access_token": "eyJhbGc...",
"refresh_token": "rt_...",
"expires_in": 900
}
}
Use the access token in all subsequent requests:
GET /api/v1/users
Authorization: Bearer eyJhbGc...
X-Tenant-Slug: your-company
2. Make Your First API Call
Try listing users in your tenant:
curl -X GET https://api-tenlixor.verbytes.com/api/v1/users \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-Tenant-Slug: your-company"
Authentication
Tenlixor uses JWT (JSON Web Tokens) for authentication. Every request must include a valid access token in the Authorization header.
Token Types
| Token Type | Lifetime | Purpose |
|---|---|---|
access_token |
15 minutes | Authenticate API requests |
refresh_token |
30 days (90 if remember_me) | Obtain new access tokens |
Login
Obtain access and refresh tokens by logging in:
POST /api/v1/auth/login
Content-Type: application/json
{
"tenant_slug": "your-company",
"email": "user@yourcompany.com",
"password": "SecurePassword123!",
"remember_me": false
}
Refresh Token
When your access token expires, use the refresh token to get a new one:
POST /api/v1/auth/refresh
Content-Type: application/json
{
"refresh_token": "rt_..."
}
β οΈ Security Best Practices
- Never expose your tokens in client-side code or public repositories
- Store refresh tokens securely (httpOnly cookies recommended)
- Implement token rotation on refresh
- Use HTTPS for all API requests
Rate Limiting
To ensure fair usage and system stability, Tenlixor implements rate limiting on all endpoints.
| Endpoint Type | Rate Limit | Window |
|---|---|---|
| Authentication (login, register) | 5 requests | 15 minutes |
| General API endpoints | 100 requests | 1 minute |
| File uploads | 20 requests | 1 minute |
Rate limit information is included in response headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640000000
Error Handling
Tenlixor uses conventional HTTP response codes and returns detailed error messages in JSON format.
HTTP Status Codes
| Status Code | Meaning |
|---|---|
200 |
OK - Request succeeded |
201 |
Created - Resource created successfully |
400 |
Bad Request - Invalid request parameters |
401 |
Unauthorized - Invalid or missing authentication |
403 |
Forbidden - Insufficient permissions |
404 |
Not Found - Resource doesn't exist |
409 |
Conflict - Resource already exists |
429 |
Too Many Requests - Rate limit exceeded |
500 |
Internal Server Error - Something went wrong |
Error Response Format
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid email format",
"details": {
"field": "email",
"issue": "must be a valid email address"
}
},
"request_id": "req_abc123"
}
Common Error Codes
| Error Code | Description |
|---|---|
VALIDATION_ERROR |
Request validation failed |
UNAUTHORIZED |
Authentication required or token invalid |
FORBIDDEN |
User lacks required permissions |
NOT_FOUND |
Requested resource not found |
CONFLICT |
Resource already exists |
RATE_LIMIT_EXCEEDED |
Too many requests |
API Reference
Complete reference for all Tenlixor API endpoints.
Authentication Endpoints
Authenticate a user and receive access and refresh tokens.
Request Body
{
"tenant_slug": "your-company",
"email": "user@yourcompany.com",
"password": "SecurePassword123!",
"remember_me": false
}
Response (200 OK)
{
"success": true,
"data": {
"user": {
"id": "usr_...",
"email": "user@yourcompany.com",
"roles": ["user"]
},
"access_token": "eyJhbGc...",
"refresh_token": "rt_...",
"expires_in": 900
}
}
Obtain a new access token using a refresh token.
Request Body
{
"refresh_token": "rt_..."
}
Response (200 OK)
{
"success": true,
"data": {
"access_token": "eyJhbGc...",
"refresh_token": "rt_new...",
"expires_in": 900
}
}
Invalidate a refresh token and log out.
Request Headers
Authorization |
Yes | Bearer {access_token} |
Request Body
{
"refresh_token": "rt_..."
}
Response (200 OK)
{
"success": true,
"data": {
"message": "Logged out successfully"
}
}
Verify the current access token and get user information.
Request Headers
Authorization |
Yes | Bearer {access_token} |
Response (200 OK)
{
"success": true,
"data": {
"id": "usr_...",
"tenant_id": "tnt_...",
"email": "user@yourcompany.com",
"first_name": "John",
"last_name": "Doe",
"roles": ["user"],
"is_active": true,
"created_at": "2026-02-24T10:00:00Z"
}
}
User Management
List all users in your tenant with pagination.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 | Page number |
limit |
integer | 20 | Items per page (max: 100) |
Response (200 OK)
{
"success": true,
"data": {
"users": [
{
"id": "usr_...",
"email": "user@company.com",
"first_name": "John",
"last_name": "Doe",
"roles": ["user"],
"is_active": true
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 50,
"total_pages": 3
}
}
}
Get detailed information about a specific user.
Response (200 OK)
{
"success": true,
"data": {
"id": "usr_...",
"tenant_id": "tnt_...",
"email": "user@company.com",
"first_name": "John",
"last_name": "Doe",
"roles": ["user"],
"is_active": true,
"force_password_change": false,
"created_at": "2026-02-24T10:00:00Z",
"updated_at": "2026-02-24T12:00:00Z"
}
}
Create a new user in your tenant.
Request Body
{
"email": "newuser@company.com",
"password": "SecurePassword123!",
"first_name": "Jane",
"last_name": "Smith",
"roles": ["user"],
"force_password_change": false
}
Response (201 Created)
{
"success": true,
"data": {
"id": "usr_...",
"email": "newuser@company.com",
"first_name": "Jane",
"last_name": "Smith",
"roles": ["user"],
"is_active": true
}
}
Update user information.
Request Body
{
"first_name": "Jane",
"last_name": "Doe",
"is_active": true
}
Soft delete a user (sets is_active to false).
Projects
Projects group language resources within a tenant. Each project has its own language limit enforced by the subscription plan. Existing tenants have their strings automatically migrated to a Default Project.
π Project-scoped Strings
When creating or querying resource strings, pass the project_id field to scope them to a project. Without a project_id the strings are unscoped (legacy behaviour).
List all projects for the authenticated tenant, including stats and plan limit information.
Response (200 OK)
{
"success": true,
"data": {
"projects": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "3f634556-4c89-4a98-9519-f443de23ce90",
"name": "My App",
"slug": "my-app",
"description": "Main application strings",
"language_count": 2,
"key_value_count": 48,
"created_at": "2026-03-01T00:00:00Z",
"updated_at": "2026-03-01T00:00:00Z"
}
]
},
"meta": {
"limit_check": {
"allowed": true,
"current": 1,
"limit": 2,
"remaining": 1,
"upgrade_required": false,
"plan_type": "growth"
}
}
}
Get a single project by ID.
Create a new project. Returns a 403 error if the plan project limit has been reached.
Request Body
{
"name": "My App",
"slug": "my-app", // optional β auto-generated from name if omitted
"description": "Main application strings"
}
Response (201 Created)
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "3f634556-4c89-4a98-9519-f443de23ce90",
"name": "My App",
"slug": "my-app",
"description": "Main application strings",
"created_at": "2026-03-01T00:00:00Z",
"updated_at": "2026-03-01T00:00:00Z"
}
}
Update a project's name, slug, or description.
Request Body
{
"name": "Renamed App",
"description": "Updated description"
}
Hard-delete a project and all its resource strings (cascade via FK).
Check how many languages this project uses against the plan's per-project language limit.
Response (200 OK)
{
"success": true,
"data": {
"allowed": false,
"current": 2,
"limit": 2,
"remaining": 0,
"upgrade_required": true,
"plan_type": "starter"
}
}
Resource Strings (i18n)
Manage internationalization strings for your application. All string endpoints accept an optional project_id parameter to scope strings to a specific project.
List all resource strings grouped by language.
Query Parameters
language_code |
string | - | Filter by language code (e.g., "en", "tr") |
project_id |
string (UUID) | - | Scope results to a specific project |
key |
string | - | Filter by key pattern (supports partial match) |
page |
number | 1 | Page number for pagination |
limit |
number | 50 | Number of items per page (max: 100) |
Response (200 OK)
{
"success": true,
"data": {
"tenant_id": "3f634556-4c89-4a98-9519-f443de23ce90",
"languages": [
{
"code": "en",
"resources": [
{
"id": "5c75a48a-e02e-45ed-b258-bb79fb4374ea",
"key": "app.goodbye",
"value": "Thank you for using Tenlixor.",
"description": "Farewell message",
"created_by": null,
"updated_by": null,
"created_at": "2026-02-24 14:26:52",
"updated_at": "2026-02-25 01:03:18"
},
{
"id": "6cc5569e-0592-436d-a5f9-5fb8c8e0c614",
"key": "app.welcome",
"value": "Welcome to Tenlixor!",
"description": "",
"created_by": null,
"updated_by": null,
"created_at": "2026-02-24 14:26:52",
"updated_at": "2026-02-24 14:26:52"
}
]
},
{
"code": "tr",
"resources": [
{
"id": "b1164929-2641-4fa9-9275-036e2d8733ab",
"key": "app.goodbye",
"value": "Tenlixor kullandΔ±ΔΔ±nΔ±z iΓ§in teΕekkΓΌrler",
"description": "",
"created_by": null,
"updated_by": null,
"created_at": "2026-02-24 14:26:52",
"updated_at": "2026-02-24 14:26:52"
},
{
"id": "e8a93d4c-b4f7-4338-bdce-d6e2d7d11c56",
"key": "app.welcome",
"value": "Tenlixor'a hoΕ geldiniz!",
"description": "",
"created_by": null,
"updated_by": null,
"created_at": "2026-02-24 14:26:52",
"updated_at": "2026-02-24 14:26:52"
}
]
}
]
},
"meta": {
"page": 1,
"limit": 50,
"total": 4
},
"request_id": "237a0037-0d32-4451-9f61-830087c90224"
}
Create a new resource string.
Request Body
{
"language_code": "en",
"key": "greeting.hello",
"value": "Hello, World!",
"description": "Friendly greeting message",
"project_id": "550e8400-e29b-41d4-a716-446655440000"
}
Response (201 Created)
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "3f634556-4c89-4a98-9519-f443de23ce90",
"language_code": "en",
"key": "greeting.hello",
"value": "Hello, World!",
"description": "Friendly greeting message",
"created_by": "user_123",
"updated_by": null,
"created_at": "2026-02-25 10:30:00",
"updated_at": "2026-02-25 10:30:00"
},
"request_id": "abc123..."
}
Update an existing resource string.
Request Body
{
"value": "Updated translation text",
"description": "Updated description (optional)"
}
Response (200 OK)
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "3f634556-4c89-4a98-9519-f443de23ce90",
"language_code": "en",
"key": "greeting.hello",
"value": "Updated translation text",
"description": "Updated description",
"created_by": "user_123",
"updated_by": "user_456",
"created_at": "2026-02-25 10:30:00",
"updated_at": "2026-02-25 11:45:00"
},
"request_id": "xyz789..."
}
Delete a resource string.
Response (200 OK)
{
"success": true,
"data": {
"message": "Resource string deleted successfully"
},
"request_id": "def456..."
}
API Key Management
Generate and manage API keys for programmatic access.
List all API keys for the authenticated user.
Response (200 OK)
{
"success": true,
"data": [
{
"id": "key_...",
"name": "Production API Key",
"prefix": "tlx_",
"is_active": true,
"last_used_at": "2026-02-24T10:00:00Z",
"created_at": "2026-02-20T10:00:00Z"
}
]
}
Generate a new API key.
Request Body
{
"name": "Production API Key",
"expires_at": "2027-02-24T10:00:00Z"
}
Response (201 Created)
{
"success": true,
"data": {
"id": "key_...",
"name": "Production API Key",
"api_key": "tlx_live_abc123...",
"prefix": "tlx_"
}
}
β οΈ Important
The full API key is only shown once during creation. Store it securely!
Revoke an API key.
File Storage (R2)
Upload and manage files in Cloudflare R2 storage.
Upload a file to R2 storage.
Request
Use multipart/form-data encoding:
POST /api/v1/files
Content-Type: multipart/form-data
Authorization: Bearer {token}
X-Tenant-Slug: your-company
file: [binary file data]
category: "documents"
is_public: false
Response (201 Created)
{
"success": true,
"data": {
"id": "file_...",
"filename": "document.pdf",
"mime_type": "application/pdf",
"size": 102400,
"category": "documents",
"is_public": false,
"url": "https://cdn-tenlixor.verbytes.com/..."
}
}
List uploaded files with optional filtering.
Download a file.
Delete a file from storage.
Audit Logs
Track and review all actions performed in your tenant.
List audit log entries with filtering and pagination.
Query Parameters
action |
string | Filter by action type (e.g. create, delete) |
user_id |
string | Filter by user ID |
resource_type |
string | Filter by resource type (e.g. resource_string, user) |
from_date |
string (ISO 8601) | Filter entries on or after this date |
to_date |
string (ISO 8601) | Filter entries on or before this date |
page |
integer | Page number (default: 1) |
limit |
integer | Items per page (default: 25, max: 100) |
Response (200 OK)
{
"success": true,
"data": {
"logs": [
{
"id": "log_...",
"action": "user.create",
"user_id": "usr_...",
"resource_type": "user",
"resource_id": "usr_new...",
"ip_address": "192.168.1.1",
"user_agent": "Mozilla/5.0...",
"created_at": "2026-02-24T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 150
}
}
}
Webhooks
Webhook support is coming soon. Subscribe to real-time events in your application.
π’ Coming Soon
Webhook functionality is under development. You'll be able to subscribe to events like:
- user.created
- user.updated
- user.deleted
- file.uploaded
- And more...
SDKs & Libraries
Tenlixor provides official SDKs for easy integration of localization features into your web applications. Our SDKs automatically fetch and manage translations, with built-in support for popular frameworks.
π― Localization SDK Features
- Automatic DOM Translation: Scans and translates your page content automatically
- Smart Caching: localStorage with in-memory fallback for optimal performance
- Framework Support: Ready-made adapters for Angular, Vue, and React
- Language Switching: Change languages at runtime without page reload
- Zero Dependencies: Lightweight and fast
- Error Resilient: Graceful fallbacks, never breaks your page
π¦ SDK Download & Access
Choose the SDK that best fits your project:
π JavaScript SDK v1.0.1
Pure vanilla JS, zero dependencies
<script src="https://cdn.jsdelivr.net/gh/bbesli/Tenlixor-SDK@v1.0.1/sdk/javascript/tenlixor.min.js"></script>
π TypeScript SDK v1.0.6
Full TypeScript support with types
npm install @verbytes-tenlixor/sdk
π Quick Start: JavaScript (CDN)
The easiest way to get started with Tenlixor localization:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My App</title>
<!-- Include Tenlixor SDK -->
<script src="https://cdn.jsdelivr.net/gh/bbesli/Tenlixor-SDK@v1.0.1/sdk/javascript/tenlixor.min.js"></script>
</head>
<body>
<!-- Method 1: Text content matching -->
<h1>app.welcome</h1>
<!-- Method 2: Data attribute -->
<p data-txr-key="app.description"></p>
<button id="lang-btn">Switch Language</button>
<script>
// Initialize SDK
const txr = new Tenlixor({
tenantSlug: 'your-tenant-slug',
token: 'YOUR_API_TOKEN',
language: 'en'
});
// Initialize and auto-scan DOM
txr.init();
// Language switcher
document.getElementById('lang-btn').addEventListener('click', async () => {
await txr.setLanguage('tr');
});
// Manual translation
console.log(txr.t('app.welcome')); // "Welcome!"
</script>
</body>
</html>
π TypeScript/NPM Installation
# NPM
npm install @verbytes-tenlixor/sdk
# Yarn
yarn add @verbytes-tenlixor/sdk
# PNPM
pnpm add @verbytes-tenlixor/sdk
import { Tenlixor } from '@verbytes-tenlixor/sdk';
const txr = new Tenlixor({
tenantSlug: 'your-tenant-slug',
token: 'YOUR_API_TOKEN',
language: 'en',
cache: true,
cacheTTL: 300000 // 5 minutes
});
await txr.init();
// Translate
const greeting = txr.t('app.welcome');
console.log(greeting); // "Welcome to Tenlixor!"
// Change language
await txr.setLanguage('tr');
βοΈ Framework Integration
Angular
import { NgModule } from '@angular/core';
import { TenlixorModule, setTenlixorInstance } from '@verbytes-tenlixor/sdk/angular';
import { Tenlixor } from '@verbytes-tenlixor/sdk';
const txr = new Tenlixor({
tenantSlug: 'your-tenant-slug',
token: 'YOUR_API_TOKEN',
language: 'en'
});
txr.init();
setTenlixorInstance(txr);
@NgModule({
imports: [TenlixorModule],
// ...
})
export class AppModule { }
<h1>{{ 'app.welcome' | txrTranslate }}</h1>
<p>{{ 'app.description' | txrTranslate:'tr' }}</p>
Vue 3
import { createApp } from 'vue';
import { createTenlixorPlugin } from '@verbytes-tenlixor/sdk/vue';
const app = createApp(App);
app.use(createTenlixorPlugin({
tenantSlug: 'your-tenant-slug',
token: 'YOUR_API_TOKEN',
language: 'en'
}));
app.mount('#app');
<script setup>
import { useTenlixorTranslate } from '@verbytes-tenlixor/sdk/vue';
const { t, currentLanguage, setLanguage } = useTenlixorTranslate();
</script>
<template>
<div>
<h1>{{ t('app.welcome') }}</h1>
<button @click="setLanguage('tr')">Turkish</button>
</div>
</template>
React
import { TenlixorProvider } from '@verbytes-tenlixor/sdk/react';
function App() {
return (
<TenlixorProvider config={{ tenantSlug: 'your-tenant-slug', token: 'YOUR_API_TOKEN', language: 'en' }}>
<YourApp />
</TenlixorProvider>
);
}
import { useTenlixor } from '@verbytes-tenlixor/sdk/react';
function MyComponent() {
const { t, currentLanguage, setLanguage, isReady } = useTenlixor();
if (!isReady) return <div>Loading...</div>;
return (
<div>
<h1>{t('app.welcome')}</h1>
<button onClick={() => setLanguage('tr')}>Turkish</button>
</div>
);
}
βοΈ Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
tenantSlug |
string |
required | Your tenant slug identifier |
token |
string |
required | Your Tenlixor API token |
language |
string |
'en' |
Default language code |
cache |
boolean |
true |
Enable caching |
cacheTTL |
number |
300000 |
Cache TTL in ms (default: 5 min) |
fallbackLanguage |
string |
'en' |
Fallback if key not found |
autoScan |
boolean |
true |
Auto-scan DOM on init |
π Core API Methods
init()
Initialize SDK and fetch translations
t(key: string, languageCode?: string): string
Translate a key to its value
setLanguage(languageCode: string)
Change active language and re-scan DOM
reload()
Force reload from API (bypass cache)
scan()
Manually trigger DOM scan
on(event: string, callback: Function)
Register event listener ('loaded', 'error', 'language-changed')
π§ͺ Test Applications
Explore our comprehensive test suite with working examples for all major frameworks. Each test application demonstrates the complete SDK functionality including initialization, translation methods, language switching, and error handling.
π Vanilla JavaScript
Pure JavaScript implementation with no dependencies
- SDK initialization
- Text matching translation
- Data attribute translation
- Manual translation
- Language switching
- Event system
- Cache functionality
βοΈ React + TypeScript
React 18 with hooks and context API
- React 18.2.0
- TypeScript 5.3.0
- Vite 5.0.0
- TenlixorProvider context
- useTenlixor() hook
npm install && npm run dev
π Vue 3 + TypeScript
Vue 3 Composition and Options API
- Vue 3.3.0
- TypeScript 5.3.0
- Vite 5.0.0
- Composition API (useTenlixorTranslate)
- Options API ($t, $txr)
npm install && npm run dev
π °οΈ Angular
Angular module with pipe and service
- TenlixorModule
- txrTranslate pipe
- Template bindings
- Service injection
- Language switching
π Running Test Applications
All test applications are located in the test/ directory of the SDK repository. For detailed instructions on building and running each test:
- Clone the repository:
git clone https://github.com/bbesli/Tenlixor-SDK.git - Build TypeScript SDK first:
cd sdk/typescript && npm install && npm run build - Navigate to desired test:
cd test/[framework]-test - Follow the README in each test folder for specific instructions
π Full Documentation
For comprehensive guides, advanced usage, troubleshooting, and API reference, visit our complete SDK documentation:
View Complete SDK Guideπ‘ How to Get Your API Token
- Login to your Tenlixor dashboard at
https://tenlixor.verbytes.com - Navigate to Settings β API Keys
- Click "Generate New API Key"
- Copy the token and use it in your SDK configuration
π SDK Resources
β οΈ Important Notes
- Keep your API token secure - never commit it to public repositories
- For production use, consider using environment variables for token storage
- The SDK requires a modern browser with ES6+ support
- Translations are cached locally to minimize API calls
Support
Need help? We're here for you!