Frontend Workers
Frontend Workers enable you to deploy and host static frontend applications on the Taruvi cloud platform. Upload your built React, Next.js, Vue, or any static web application, and deploy to either a Taruvi-hosted subdomain or your own custom domain.
Overview
Frontend Workers provide:
- Automated Hosting: Upload a zip or tar file and your application is automatically deployed
- Flexible Domain Options: Choose between Taruvi-hosted subdomains (e.g.,
https://my-app.taruvi.app/) or your own custom domains (e.g.,https://app.yourcompany.com/) - Version Control: Track all deployments with build history and easy rollback
- HTTPS by Default: All deployments are automatically secured with HTTPS
- Zero Infrastructure: No need to manage servers, domains, or SSL certificates
- Immutable Identifiers: Each worker has a permanent slug for API access, while subdomains can be updated
Domain Types
Frontend Workers support two domain types:
Internal Domains (Taruvi-hosted)
- Default option - Automatically managed subdomains under
*.taruvi.app - Automatic DNS - No DNS configuration required
- Unique subdomains - Globally unique subdomains (e.g.,
my-app,dashboard,portal) - Instant deployment - Just upload and your app is live
External Domains (Custom domains)
- Your own domain - Use any domain you own (e.g.,
app.yourcompany.comoryourcompany.com) - Client DNS setup required - You must configure DNS records to point to the Taruvi platform
- SSL/TLS - Handled at your CDN/proxy level (not managed by Taruvi)
- Full control - Use your branding and domain infrastructure
Slug vs Subdomain
Important: Frontend workers use two distinct identifiers:
Slug (Immutable)
- Permanent API identifier - Used in API URLs for accessing the worker
- Set once at creation - Cannot be changed after creation
- Used in API endpoints - Example:
/api/frontend_workers/taruvi-crm/ - Auto-generated - Generated from subdomain if not provided
- Always shown in responses
Subdomain (Mutable)
- User-facing domain - Forms the web URL where your app is hosted
- Can be updated - Change via PATCH request (triggers S3 migration)
- Unprefixed value - Base subdomain without environment prefix (e.g.,
taruvi-crm) - Environment prefix applied automatically - System adds prefix based on environment setting
Example:
{
"slug": "taruvi-crm", // Immutable API identifier
"subdomain": "taruvi-crm", // Unprefixed base subdomain (can be updated)
"web_url": "https://dev-taruvi-crm.taruvi.app/" // Full URL with environment prefix
}
Later, you can update the subdomain:
PATCH /api/frontend_workers/taruvi-crm/ # slug stays the same in URL
{"subdomain_input": "hey-crm"} # update subdomain
# Result:
# - slug: "taruvi-crm" (unchanged)
# - subdomain: "hey-crm" (updated)
# - web_url: "https://dev-hey-crm.taruvi.app/" (updated)
Environment-Based Domain Prefixing
Frontend Workers automatically prefix subdomains based on the deployment environment to separate test, staging, and production deployments.
How It Works
The ENVIRONMENT configuration variable controls subdomain prefixing:
- Production (
ENVIRONMENT=production): No prefix applied- Example:
portal.taruvi.app
- Example:
- Staging (
ENVIRONMENT=staging): Addsstaging-prefix- Example:
staging-portal.taruvi.app
- Example:
- Development (
ENVIRONMENT=dev): Addsdev-prefix- Example:
dev-portal.taruvi.app
- Example:
- Testing (
ENVIRONMENT=test): Addstest-prefix- Example:
test-portal.taruvi.app
- Example:
Benefits
- Clear Separation: Easily distinguish between environments by URL
- Collision Prevention: Test and production workers can have the same base subdomain without conflicts
- Automatic: No manual configuration needed - prefix is applied automatically based on environment
- Consistent: Works for both internal (Taruvi-hosted) and external (custom) domains
Examples
When you create a worker with subdomain dashboard:
Production Environment:
- REST API
- Python
- JavaScript
# ENVIRONMENT=production
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-F "name=Dashboard" \
-F "subdomain_input=dashboard" \
-F "[email protected]"
# Result: dashboard.taruvi.app
import requests
# ENVIRONMENT=production
response = requests.post(
"https://your-tenant.taruvi.app/api/frontend_workers/",
headers={"Authorization": "Bearer YOUR_ACCESS_TOKEN"},
data={"name": "Dashboard", "subdomain_input": "dashboard"},
files={"file": open("dashboard.zip", "rb")}
)
# Result: dashboard.taruvi.app
// ENVIRONMENT=production
const formData = new FormData();
formData.append("name", "Dashboard");
formData.append("subdomain_input", "dashboard");
formData.append("file", dashboardZipFile);
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/",
{
method: "POST",
headers: { "Authorization": "Bearer YOUR_ACCESS_TOKEN" },
body: formData
}
);
// Result: dashboard.taruvi.app
Staging Environment:
- REST API
- Python
- JavaScript
# ENVIRONMENT=staging
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-F "name=Dashboard" \
-F "subdomain_input=dashboard" \
-F "[email protected]"
# Result: staging-dashboard.taruvi.app
import requests
# ENVIRONMENT=staging
response = requests.post(
"https://your-tenant.taruvi.app/api/frontend_workers/",
headers={"Authorization": "Bearer YOUR_ACCESS_TOKEN"},
data={"name": "Dashboard", "subdomain_input": "dashboard"},
files={"file": open("dashboard.zip", "rb")}
)
# Result: staging-dashboard.taruvi.app
// ENVIRONMENT=staging
const formData = new FormData();
formData.append("name", "Dashboard");
formData.append("subdomain_input", "dashboard");
formData.append("file", dashboardZipFile);
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/",
{
method: "POST",
headers: { "Authorization": "Bearer YOUR_ACCESS_TOKEN" },
body: formData
}
);
// Result: staging-dashboard.taruvi.app
Custom Domain (Staging):
- REST API
- Python
- JavaScript
# ENVIRONMENT=staging
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-F "name=Portal" \
-F "subdomain_input=portal" \
-F "is_internal=false" \
-F "domain=walmart.com" \
-F "[email protected]"
# Result: staging-portal.walmart.com
import requests
# ENVIRONMENT=staging
response = requests.post(
"https://your-tenant.taruvi.app/api/frontend_workers/",
headers={"Authorization": "Bearer YOUR_ACCESS_TOKEN"},
data={
"name": "Portal",
"subdomain_input": "portal",
"is_internal": "false",
"domain": "walmart.com"
},
files={"file": open("portal.zip", "rb")}
)
# Result: staging-portal.walmart.com
// ENVIRONMENT=staging
const formData = new FormData();
formData.append("name", "Portal");
formData.append("subdomain_input", "portal");
formData.append("is_internal", "false");
formData.append("domain", "walmart.com");
formData.append("file", portalZipFile);
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/",
{
method: "POST",
headers: { "Authorization": "Bearer YOUR_ACCESS_TOKEN" },
body: formData
}
);
// Result: staging-portal.walmart.com
Note: You always provide the base subdomain (e.g., dashboard, portal). The system automatically applies the appropriate environment prefix.
Quick Start
1. Prepare Your Application
Build your frontend application for production:
# React/Next.js example
npm run build
# This typically creates a 'build' or 'dist' folder
2. Create an Archive
Package your built files into a supported format:
# Create a zip file
cd build && zip -r ../my-app.zip . && cd ..
# Or create a tar.gz file
tar -czf my-app.tar.gz -C build .
Supported formats: .zip, .tar, .tar.gz, .tgz
3. Deploy via API
- REST API
- Python
- JavaScript
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-F "name=My React App" \
-F "subdomain_input=my-app" \
-F "[email protected]"
import requests
response = requests.post(
"https://your-tenant.taruvi.app/api/frontend_workers/",
headers={"Authorization": "Bearer YOUR_ACCESS_TOKEN"},
data={"name": "My React App", "subdomain_input": "my-app"},
files={"file": open("my-app.zip", "rb")}
)
result = response.json()
print(result["data"]["web_url"])
const formData = new FormData();
formData.append("name", "My React App");
formData.append("subdomain_input", "my-app");
formData.append("file", myAppZipFile);
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/",
{
method: "POST",
headers: { "Authorization": "Bearer YOUR_ACCESS_TOKEN" },
body: formData
}
);
const result = await response.json();
console.log(result.data.web_url);
Your application is now live at https://my-app.taruvi.app/ (with environment prefix if applicable)
4. Deploy with Custom Domain (Optional)
To deploy to your own custom domain instead:
- REST API
- Python
- JavaScript
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-F "name=Client Portal" \
-F "subdomain_input=portal" \
-F "is_internal=false" \
-F "domain=walmart.com" \
-F "[email protected]"
import requests
response = requests.post(
"https://your-tenant.taruvi.app/api/frontend_workers/",
headers={"Authorization": "Bearer YOUR_ACCESS_TOKEN"},
data={
"name": "Client Portal",
"subdomain_input": "portal",
"is_internal": "false",
"domain": "walmart.com"
},
files={"file": open("my-app.zip", "rb")}
)
result = response.json()
const formData = new FormData();
formData.append("name", "Client Portal");
formData.append("subdomain_input", "portal");
formData.append("is_internal", "false");
formData.append("domain", "walmart.com");
formData.append("file", myAppZipFile);
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/",
{
method: "POST",
headers: { "Authorization": "Bearer YOUR_ACCESS_TOKEN" },
body: formData
}
);
const result = await response.json();
How it works: The system combines your subdomain with the base domain to create the full domain:
subdomain=portal+domain=walmart.com=portal.walmart.comsubdomain=support+domain=example.com=support.example.com
Important:
- Before deploying to a custom domain, ensure you've configured DNS records pointing to the Taruvi platform. See Custom Domain Setup for details.
- The base domain (e.g.,
walmart.com) must be provided without any subdomain - Your application will be accessible at
https://portal.walmart.com/
Custom Domain Setup
To use a custom external domain, you need to configure DNS records:
DNS Configuration
Point your domain to the Taruvi platform by creating one of these records:
Option 1: CNAME Record (recommended for subdomains)
Type: CNAME
Host: app (or your subdomain)
Value: <TARUVI_CDN_ENDPOINT>
TTL: 3600
Option 2: A Record (for apex domains)
Type: A
Host: @ (or your apex domain)
Value: <TARUVI_IP_ADDRESS>
TTL: 3600
Note: Contact your Taruvi administrator for the correct TARUVI_CDN_ENDPOINT or TARUVI_IP_ADDRESS values for your installation.
SSL/TLS Certificates
SSL certificates for custom domains must be managed at your CDN or proxy level. Taruvi does not provision SSL certificates for external domains.
Domain Restrictions
The following domains are reserved and cannot be used as external domains:
*.taruvi.app- Reserved for Taruvi-hosted subdomains- Any subdomain of your configured
FRONTEND_WORKER_BASE_DOMAIN
Domain Validation
Domain format must follow standard DNS naming conventions:
- Valid characters: letters, numbers, hyphens, dots
- Must have a valid TLD (e.g.,
.com,.org,.io) - Examples:
app.example.com,example.com,dashboard.mysite.io
API Reference
Base URL
Frontend Workers API is available at tenant-level:
https://your-tenant.taruvi.app/api/frontend_workers/
Authentication
All endpoints require JWT authentication. Include your access token in the Authorization header:
Authorization: Bearer YOUR_ACCESS_TOKEN
See API Overview for details on obtaining tokens.
Response Format
All API endpoints return responses in a standardized format:
Success Response Structure:
{
"success": true,
"message": "Operation successful",
"status_code": 200,
"data": { ... } // Single object or array of objects
}
Paginated Response Structure:
{
"success": true,
"message": "Data retrieved successfully",
"status_code": 200,
"data": [ ... ],
"total": 50,
"page": 1,
"page_size": 10,
"total_pages": 5
}
Error Response Structure:
{
"success": false,
"message": "Error description",
"status_code": 400,
"data": { ... } // Optional error details
}
Key Fields:
success: Boolean indicating if the request was successfulmessage: Human-readable message describing the resultstatus_code: HTTP status code (200, 201, 400, 404, etc.)data: The actual response data (object, array, or null)total: Total count of items (for list endpoints)page: Current page number (for paginated endpoints)page_size: Number of items per pagetotal_pages: Total number of pages available
List Frontend Workers
Retrieve all frontend workers for your site.
Endpoint
GET /api/frontend_workers/
Query Parameters
search(optional): Search workers by nameordering(optional): Order by field (created_at,updated_at,-created_at,-updated_at)app(optional): Filter by associated app ID
Response
{
"success": true,
"message": "Data retrieved successfully",
"status_code": 200,
"data": [
{
"slug": "my-react-app",
"name": "My React App",
"subdomain": "my-react-app",
"web_url": "https://dev-my-react-app.taruvi.app/",
"active_build_uuid": "660e8400-e29b-41d4-a716-446655440001",
"app_name": "My App"
}
],
"total": 2,
"page": 1,
"page_size": 10,
"total_pages": 1
}
Example
- REST API
- Python
- JavaScript
# List all workers
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-tenant.taruvi.app/api/frontend_workers/
# Search workers by name
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-tenant.taruvi.app/api/frontend_workers/?search=react
# Filter by app and order by creation date
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-tenant.taruvi.app/api/frontend_workers/?app=123&ordering=-created_at
import requests
headers = {"Authorization": "Bearer YOUR_TOKEN"}
# List all workers
response = requests.get(
"https://your-tenant.taruvi.app/api/frontend_workers/",
headers=headers
)
# Search workers by name
response = requests.get(
"https://your-tenant.taruvi.app/api/frontend_workers/",
headers=headers,
params={"search": "react"}
)
# Filter by app and order by creation date
response = requests.get(
"https://your-tenant.taruvi.app/api/frontend_workers/",
headers=headers,
params={"app": 123, "ordering": "-created_at"}
)
workers = response.json()
const headers = { "Authorization": "Bearer YOUR_TOKEN" };
// List all workers
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/",
{ headers }
);
const workers = await response.json();
// Search workers by name
const searchResponse = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/?search=react",
{ headers }
);
// Filter by app and order by creation date
const filteredResponse = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/?app=123&ordering=-created_at",
{ headers }
);
Check Subdomain Availability
Check if a subdomain is available before creating or updating a worker. This endpoint features an intelligent suggestion algorithm that provides context-aware, professional alternatives when a subdomain is unavailable.
Endpoint
GET /api/frontend_workers/check-subdomain-availability/
Query Parameters
subdomain(required): Base subdomain to check (unprefixed, e.g.,taruvi-crm)exclude_slug(optional): Slug of worker to exclude from check (for edit forms)is_internal(optional, default:true): Whether checking for internal or external domaindomain(optional): Custom domain (required ifis_internal=false)
Response (Available)
{
"success": true,
"data": {
"subdomain": "taruvi-crm",
"available": true,
"full_domain": "dev-taruvi-crm.taruvi.app",
"domain": "taruvi.app",
"is_internal": true,
"message": "Subdomain is available"
}
}
Response (Not Available - With Tenant Context)
When a subdomain is unavailable, the response includes intelligent suggestions based on your tenant/organization name and other contextual factors.
{
"success": true,
"data": {
"subdomain": "analytics",
"available": false,
"full_domain": "dev-analytics.taruvi.app",
"domain": "taruvi.app",
"is_internal": true,
"message": "Subdomain is already in use",
"conflicts_with": {
"worker_slug": "analytics-dashboard",
"worker_name": "Analytics Dashboard"
},
"suggestions": [
{
"subdomain": "analytics-taruvi",
"full_domain": "dev-analytics-taruvi.taruvi.app",
"reason": "context-aware"
},
{
"subdomain": "taruvi-analytics",
"full_domain": "dev-taruvi-analytics.taruvi.app",
"reason": "context-aware"
},
{
"subdomain": "analytics-app",
"full_domain": "dev-analytics-app.taruvi.app",
"reason": "semantic"
},
{
"subdomain": "analytics-portal",
"full_domain": "dev-analytics-portal.taruvi.app",
"reason": "semantic"
},
{
"subdomain": "analytics-2024",
"full_domain": "dev-analytics-2024.taruvi.app",
"reason": "context-aware"
}
]
}
}
Response Fields (When Not Available)
conflicts_with(object, optional): Information about the worker using this subdomainworker_slug: The slug of the conflicting workerworker_name: The name of the conflicting worker
suggestions(array): List of intelligent alternative suggestions (max 5)subdomain: The suggested base subdomain (unprefixed)full_domain: The complete domain with environment prefix appliedreason: Strategy used to generate this suggestion:context-aware: Uses tenant/org name or current yearsemantic: Professional suffix (app, portal, hub, platform, workspace)smart-numeric: Meaningful numbers (year, 10, 24, 100)fallback: Sequential numbers (only as last resort)
Suggestion Algorithm
The intelligent suggestion algorithm prioritizes suggestions in the following order:
-
Context-Aware (Target: 2 suggestions)
- Incorporates your tenant/organization name
- Uses current year for time-based uniqueness
- Examples:
analytics-acme,acme-analytics,portal-2024
-
Semantic Suffixes (Target: 2 suggestions)
- Common professional suffixes
- Examples:
dashboard-app,api-portal,admin-hub
-
Smart Numerics (Target: 1 suggestion)
- Meaningful numbers instead of sequential 2, 3, 4
- Examples:
app-2024,portal-24,api-10
-
Fallback (Only if needed)
- Sequential numbers as last resort
- Examples:
portal-2,portal-3
Response (Not Available - Without Tenant Context)
When tenant/organization name is not available, suggestions focus on semantic and year-based alternatives:
{
"success": true,
"data": {
"subdomain": "portal",
"available": false,
"full_domain": "dev-portal.taruvi.app",
"domain": "taruvi.app",
"is_internal": true,
"message": "Subdomain is already in use",
"suggestions": [
{
"subdomain": "portal-2024",
"full_domain": "dev-portal-2024.taruvi.app",
"reason": "context-aware"
},
{
"subdomain": "portal-app",
"full_domain": "dev-portal-app.taruvi.app",
"reason": "semantic"
},
{
"subdomain": "portal-hub",
"full_domain": "dev-portal-hub.taruvi.app",
"reason": "semantic"
},
{
"subdomain": "portal-platform",
"full_domain": "dev-portal-platform.taruvi.app",
"reason": "semantic"
},
{
"subdomain": "portal-workspace",
"full_domain": "dev-portal-workspace.taruvi.app",
"reason": "semantic"
}
]
}
}
Response (Not Available - External Domain)
For external domains, suggestions use the custom domain without environment prefix:
{
"success": true,
"data": {
"subdomain": "app",
"available": false,
"full_domain": "app.walmart.com",
"domain": "walmart.com",
"is_internal": false,
"message": "Subdomain is already in use",
"suggestions": [
{
"subdomain": "app-walmart",
"full_domain": "app-walmart.walmart.com",
"reason": "context-aware"
},
{
"subdomain": "walmart-app",
"full_domain": "walmart-app.walmart.com",
"reason": "context-aware"
},
{
"subdomain": "app-portal",
"full_domain": "app-portal.walmart.com",
"reason": "semantic"
},
{
"subdomain": "app-hub",
"full_domain": "app-hub.walmart.com",
"reason": "semantic"
},
{
"subdomain": "app-2024",
"full_domain": "app-2024.walmart.com",
"reason": "context-aware"
}
]
}
}
Response (Invalid Format)
{
"success": false,
"message": "Invalid subdomain format",
"status_code": 400,
"errors": {
"subdomain": [
"Subdomain must contain only lowercase letters, numbers, and hyphens (no leading/trailing hyphens)"
]
}
}
Examples
- REST API
- Python
- JavaScript
# Check availability for create form
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://your-tenant.taruvi.app/api/frontend_workers/check-subdomain-availability/?subdomain=taruvi-crm"
# Check availability for edit form (exclude current worker)
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://your-tenant.taruvi.app/api/frontend_workers/check-subdomain-availability/?subdomain=taruvi-crm&exclude_slug=taruvi-crm"
# Check availability for external domain
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://your-tenant.taruvi.app/api/frontend_workers/check-subdomain-availability/?subdomain=portal&is_internal=false&domain=walmart.com"
import requests
headers = {"Authorization": "Bearer YOUR_TOKEN"}
# Check availability for create form
response = requests.get(
"https://your-tenant.taruvi.app/api/frontend_workers/check-subdomain-availability/",
headers=headers,
params={"subdomain": "taruvi-crm"}
)
availability = response.json()
# Check availability for edit form (exclude current worker)
response = requests.get(
"https://your-tenant.taruvi.app/api/frontend_workers/check-subdomain-availability/",
headers=headers,
params={"subdomain": "taruvi-crm", "exclude_slug": "taruvi-crm"}
)
# Check availability for external domain
response = requests.get(
"https://your-tenant.taruvi.app/api/frontend_workers/check-subdomain-availability/",
headers=headers,
params={"subdomain": "portal", "is_internal": "false", "domain": "walmart.com"}
)
const headers = { "Authorization": "Bearer YOUR_TOKEN" };
// Check availability for create form
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/check-subdomain-availability/?subdomain=taruvi-crm",
{ headers }
);
const availability = await response.json();
// Check availability for edit form (exclude current worker)
const editResponse = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/check-subdomain-availability/?subdomain=taruvi-crm&exclude_slug=taruvi-crm",
{ headers }
);
// Check availability for external domain
const externalResponse = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/check-subdomain-availability/?subdomain=portal&is_internal=false&domain=walmart.com",
{ headers }
);
Use Cases
- Real-time validation: Check availability as users type in forms
- Pre-validation: Validate before submitting create/update requests
- Intelligent suggestions: Show context-aware alternatives when subdomain is taken
- Professional branding: Suggestions incorporate your organization name
- Edit forms: Use
exclude_slugto exclude the current worker from the check - Conflict resolution: See which worker is using a subdomain via
conflicts_with
Suggestion Quality Features
- All suggestions are verified as available before being returned
- Suggestions are unique (no duplicates)
- Maximum of 5 suggestions returned
- All suggestions pass subdomain format validation
- Tenant/organization name is automatically extracted and slugified
- Year-based suggestions use current year dynamically (not hardcoded)
Create Frontend Worker
Create a new frontend worker and optionally upload an initial build.
Endpoint
POST /api/frontend_workers/
Request Parameters
name(required): Name for your frontend workersubdomain_input(optional): Custom base subdomain (auto-generated from name if not provided)is_internal(optional): Boolean, defaults totrue. Set tofalseto use custom external domaindomain(optional): Custom base domain (e.g., "walmart.com", "example.com"). Required whenis_internal=falseapp(optional): ID of associated appfile(optional): Build archive file to deploy
Domain Requirements
- For internal domains (
is_internal=true):- Uses
FRONTEND_WORKER_BASE_DOMAIN(e.g.,taruvi.app) domainparameter should NOT be provided (automatically uses base domain)subdomain_input: Forms the base subdomain (e.g.,portal.taruvi.app)
- Uses
- For external domains (
is_internal=false):domain: Required, must be a valid base domain name- Cannot use
*.taruvi.appor any reserved Taruvi domains - Must follow DNS naming conventions (letters, numbers, hyphens, dots, valid TLD)
- Examples:
walmart.com,example.com,my-company.co.uk subdomain_input: Combines with your custom domain (e.g.,subdomain=portal+domain=walmart.com=portal.walmart.com)- Client must configure DNS records
Subdomain Requirements
- Format: 3-100 characters, lowercase letters, numbers, and hyphens only
- Pattern:
^[a-z0-9]+(?:-[a-z0-9]+)*$(no leading/trailing hyphens) - Auto-generation: If not provided, automatically generated from the worker name
- Uniqueness: The combination of
(subdomain, domain)must be globally unique - Environment prefix: System automatically adds environment prefix (e.g.,
dev-,staging-)
Subdomain Collision Behavior
- Explicit subdomain provided: If the subdomain is already in use for the specified domain, the request fails with a validation error. You must choose a different subdomain.
- Auto-generated subdomain: If not provided, the subdomain is generated from the name. If a collision occurs, a numeric suffix is automatically added (e.g.,
portal,portal-1,portal-2).
Slug Generation
- The immutable
slugis automatically set to the same value as the final subdomain (after collision resolution) - This slug becomes the permanent API identifier for the worker
- Cannot be changed after creation
Examples:
- User provides
subdomain_input=helpdesk,is_internal=trueandhelpdeskexists -> Error - User provides only
name=Helpdesk, auto-generatessubdomain=helpdesk-> Auto-increments tohelpdesk-1if needed
File Requirements
- Maximum size: 10MB
- Supported formats:
.zip,.tar,.tar.gz,.tgz
Response
{
"success": true,
"message": "Frontend worker created successfully",
"status_code": 201,
"data": {
"slug": "my-react-app",
"name": "My React App",
"subdomain": "my-react-app",
"web_url": "https://dev-my-react-app.taruvi.app/",
"active_build_uuid": "660e8400-e29b-41d4-a716-446655440001",
"app_name": "My App",
"latest_build": {
"uuid": "660e8400-e29b-41d4-a716-446655440001",
"status": "completed",
"uploaded_at": "2025-11-10T12:00:00Z"
},
"active_build_info": {
"uuid": "660e8400-e29b-41d4-a716-446655440001",
"status": "completed",
"uploaded_at": "2025-11-10T12:00:00Z"
},
"created_at": "2025-11-10T12:00:00Z",
"updated_at": "2025-11-10T12:00:00Z"
}
}
Examples
- REST API
- Python
- JavaScript
# Create internal domain worker with initial deployment (default)
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "name=Dashboard App" \
-F "subdomain_input=dashboard" \
-F "[email protected]"
# Result: https://dev-dashboard.taruvi.app/ (if dev environment)
# slug: "dashboard" (immutable)
# Create internal domain worker without initial deployment
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Analytics Portal",
"subdomain_input": "analytics"
}'
# Result: https://dev-analytics.taruvi.app/
# Create external domain worker with custom domain
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "name=Client Portal" \
-F "subdomain_input=portal" \
-F "is_internal=false" \
-F "domain=walmart.com" \
-F "[email protected]"
# Result: https://dev-portal.walmart.com/ (if dev environment)
# Create worker with auto-generated subdomain
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "name=My Portal App" \
-F "[email protected]"
# Auto-generates subdomain from name: "my-portal-app"
# If collision occurs, becomes: "my-portal-app-1"
import requests
headers = {"Authorization": "Bearer YOUR_TOKEN"}
# Create internal domain worker with initial deployment
response = requests.post(
"https://your-tenant.taruvi.app/api/frontend_workers/",
headers=headers,
data={"name": "Dashboard App", "subdomain_input": "dashboard"},
files={"file": open("dashboard-build.zip", "rb")}
)
# Create without initial deployment
response = requests.post(
"https://your-tenant.taruvi.app/api/frontend_workers/",
headers={**headers, "Content-Type": "application/json"},
json={"name": "Analytics Portal", "subdomain_input": "analytics"}
)
# Create external domain worker
response = requests.post(
"https://your-tenant.taruvi.app/api/frontend_workers/",
headers=headers,
data={
"name": "Client Portal",
"subdomain_input": "portal",
"is_internal": "false",
"domain": "walmart.com"
},
files={"file": open("client-portal.zip", "rb")}
)
// Create internal domain worker with initial deployment
const formData = new FormData();
formData.append("name", "Dashboard App");
formData.append("subdomain_input", "dashboard");
formData.append("file", dashboardBuildZip);
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/",
{
method: "POST",
headers: { "Authorization": "Bearer YOUR_TOKEN" },
body: formData
}
);
// Create without initial deployment
const jsonResponse = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/",
{
method: "POST",
headers: {
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json"
},
body: JSON.stringify({
name: "Analytics Portal",
subdomain_input: "analytics"
})
}
);
// Create external domain worker
const externalForm = new FormData();
externalForm.append("name", "Client Portal");
externalForm.append("subdomain_input", "portal");
externalForm.append("is_internal", "false");
externalForm.append("domain", "walmart.com");
externalForm.append("file", clientPortalZip);
const externalResponse = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/",
{
method: "POST",
headers: { "Authorization": "Bearer YOUR_TOKEN" },
body: externalForm
}
);
Error Responses
Subdomain Collision Error (when providing explicit subdomain that already exists):
{
"success": false,
"status_code": 400,
"message": "Validation error",
"errors": {
"subdomain_input": ["Subdomain 'portal' is already in use for domain 'taruvi.app'. Please choose a different subdomain."]
}
}
Invalid Domain Format Error:
{
"success": false,
"status_code": 400,
"message": "Validation error",
"errors": {
"domain": ["Invalid domain format. Domain must follow DNS naming conventions (e.g., 'walmart.com', 'my-company.co.uk')"]
}
}
Reserved Domain Error (trying to use *.taruvi.app for external worker):
{
"success": false,
"status_code": 400,
"message": "Validation error",
"errors": {
"domain": ["Cannot use 'taruvi.app' or its subdomains for external workers. Use is_internal=true for workers on taruvi.app."]
}
}
Missing Domain Error (when is_internal=false but no domain provided):
{
"success": false,
"status_code": 400,
"message": "Validation error",
"errors": {
"domain": ["Domain is required when is_internal=false (external workers must specify a custom domain)."]
}
}
Get Frontend Worker Details
Retrieve detailed information about a specific frontend worker.
Endpoint
GET /api/frontend_workers/{slug}/
URL Parameters
slug(required): The immutable slug identifier of the worker
Response
{
"success": true,
"message": "Frontend worker retrieved successfully",
"status_code": 200,
"data": {
"slug": "my-react-app",
"name": "My React App",
"subdomain": "my-react-app",
"web_url": "https://dev-my-react-app.taruvi.app/",
"active_build_uuid": "660e8400-e29b-41d4-a716-446655440001",
"app_name": "My App",
"created_by": "[email protected]",
"modified_by": "[email protected]",
"latest_build": {
"uuid": "660e8400-e29b-41d4-a716-446655440001",
"status": "completed",
"uploaded_at": "2025-11-10T12:00:00Z"
},
"active_build_info": {
"uuid": "660e8400-e29b-41d4-a716-446655440001",
"status": "completed",
"uploaded_at": "2025-11-10T12:00:00Z"
},
"created_at": "2025-11-10T12:00:00Z",
"updated_at": "2025-11-10T12:00:00Z"
}
}
Example
- REST API
- Python
- JavaScript
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/
import requests
response = requests.get(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/",
headers={"Authorization": "Bearer YOUR_TOKEN"}
)
worker = response.json()
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/",
{
headers: { "Authorization": "Bearer YOUR_TOKEN" }
}
);
const worker = await response.json();
Update Frontend Worker
Update a frontend worker's configuration or deploy a new build.
Endpoint
PATCH /api/frontend_workers/{slug}/
URL Parameters
slug(required): The immutable slug identifier of the worker
Request Parameters
name(optional): Update worker namesubdomain_input(optional): Change subdomain (triggers automatic S3 file migration)app(optional): Change associated appfile(optional): Upload new build
Important Notes
- Slug is immutable: Cannot be changed after creation. Use slug in URL to identify worker.
- Subdomain changes: Updating
subdomain_inputmigrates all existing files to the new subdomain path in S3 - Subdomain collision: If you provide a subdomain that's already in use for the same domain, the request will fail with a validation error
- File uploads: Creates a new build but doesn't activate it automatically
- Activating builds: Use the Set Active Build endpoint to activate a specific build
Response
{
"success": true,
"message": "Frontend worker updated successfully",
"status_code": 200,
"data": {
"slug": "my-react-app",
"name": "Updated App Name",
"subdomain": "new-subdomain",
"web_url": "https://dev-new-subdomain.taruvi.app/",
"active_build_uuid": "660e8400-e29b-41d4-a716-446655440001",
"app_name": "My App",
"created_at": "2025-11-10T12:00:00Z",
"updated_at": "2025-11-10T14:30:00Z"
}
}
Example
- REST API
- Python
- JavaScript
# Update worker name (slug remains the same in URL)
curl -X PATCH https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Updated Dashboard"}'
# Change subdomain (slug in URL stays the same, subdomain value changes)
curl -X PATCH https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"subdomain_input": "new-dashboard"}'
# Result: slug still "my-react-app", but web_url changes to new-dashboard
# Upload new build
curl -X PATCH https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "[email protected]"
import requests
headers = {"Authorization": "Bearer YOUR_TOKEN"}
# Update worker name
response = requests.patch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/",
headers={**headers, "Content-Type": "application/json"},
json={"name": "Updated Dashboard"}
)
# Change subdomain
response = requests.patch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/",
headers={**headers, "Content-Type": "application/json"},
json={"subdomain_input": "new-dashboard"}
)
# Upload new build
response = requests.patch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/",
headers=headers,
files={"file": open("new-version.zip", "rb")}
)
// Update worker name
const nameResponse = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/",
{
method: "PATCH",
headers: {
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json"
},
body: JSON.stringify({ name: "Updated Dashboard" })
}
);
// Change subdomain
const subdomainResponse = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/",
{
method: "PATCH",
headers: {
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json"
},
body: JSON.stringify({ subdomain_input: "new-dashboard" })
}
);
// Upload new build
const formData = new FormData();
formData.append("file", newVersionZip);
const buildResponse = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/",
{
method: "PATCH",
headers: { "Authorization": "Bearer YOUR_TOKEN" },
body: formData
}
);
Delete Frontend Worker
Delete a frontend worker and all associated resources.
Endpoint
DELETE /api/frontend_workers/{slug}/
URL Parameters
slug(required): The immutable slug identifier of the worker
What Gets Deleted
- The frontend worker record
- All associated builds
- All deployment files from storage
- Domain mapping (subdomain becomes available for reuse)
Response
{
"success": true,
"message": "Frontend worker \"My React App\" and all related data deleted successfully",
"status_code": 204
}
Example
- REST API
- Python
- JavaScript
curl -X DELETE https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/ \
-H "Authorization: Bearer YOUR_TOKEN"
import requests
response = requests.delete(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/",
headers={"Authorization": "Bearer YOUR_TOKEN"}
)
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/",
{
method: "DELETE",
headers: { "Authorization": "Bearer YOUR_TOKEN" }
}
);
Set Active Build
Deploy a specific build as the active version of your frontend worker.
Endpoint
PATCH /api/frontend_workers/{slug}/set-active-build/
URL Parameters
slug(required): The immutable slug identifier of the worker
Request
{
"build_uuid": "660e8400-e29b-41d4-a716-446655440001"
}
Validation
- Build must exist and belong to this worker
- Build status must be "completed"
- Build must have an archive file
Process
- Downloads the build archive from storage
- Clears existing deployment files
- Extracts and deploys the archive contents
- Updates the active build reference
Response
{
"success": true,
"message": "Successfully redeployed and set build 660e8400-e29b-41d4-a716-446655440001 as active build",
"status_code": 200,
"data": {
"slug": "my-react-app",
"name": "My React App",
"subdomain": "my-react-app",
"web_url": "https://dev-my-react-app.taruvi.app/",
"active_build_uuid": "660e8400-e29b-41d4-a716-446655440001",
"app_name": "My App",
"created_at": "2025-11-10T12:00:00Z",
"updated_at": "2025-11-10T15:00:00Z"
},
"deployment_url": "https://dev-my-react-app.taruvi.app/"
}
Example
- REST API
- Python
- JavaScript
curl -X PATCH https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/set-active-build/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"build_uuid": "660e8400-e29b-41d4-a716-446655440001"}'
import requests
response = requests.patch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/set-active-build/",
headers={
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json"
},
json={"build_uuid": "660e8400-e29b-41d4-a716-446655440001"}
)
result = response.json()
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/set-active-build/",
{
method: "PATCH",
headers: {
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json"
},
body: JSON.stringify({
build_uuid: "660e8400-e29b-41d4-a716-446655440001"
})
}
);
const result = await response.json();
List Builds
Retrieve all builds for a specific frontend worker.
Endpoint
GET /api/frontend_workers/{slug}/builds/
URL Parameters
slug(required): The immutable slug identifier of the worker
Query Parameters
search(optional): Search builds by nameordering(optional): Order by field (uploaded_at,created_at,-uploaded_at,-created_at)
Response
{
"success": true,
"message": "Frontend worker builds retrieved successfully",
"status_code": 200,
"data": [
{
"uuid": "660e8400-e29b-41d4-a716-446655440001",
"frontend_worker_name": "My React App",
"status": "completed",
"uploaded_at": "2025-11-10T12:00:00Z",
"created_by_username": "john.doe",
"created_at": "2025-11-10T12:00:00Z"
},
{
"uuid": "770e8400-e29b-41d4-a716-446655440002",
"frontend_worker_name": "My React App",
"status": "completed",
"uploaded_at": "2025-11-09T10:30:00Z",
"created_by_username": "john.doe",
"created_at": "2025-11-09T10:30:00Z"
}
],
"total": 5,
"page": 1,
"page_size": 10,
"total_pages": 1
}
Build Status Values
pending: Upload in progresscompleted: Successfully deployed and archivedfailed: Deployment failed
Example
- REST API
- Python
- JavaScript
# List all builds for a worker
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/
# List builds ordered by upload date
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/?ordering=-uploaded_at
import requests
headers = {"Authorization": "Bearer YOUR_TOKEN"}
# List all builds
response = requests.get(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/",
headers=headers
)
# List builds ordered by upload date
response = requests.get(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/",
headers=headers,
params={"ordering": "-uploaded_at"}
)
builds = response.json()
const headers = { "Authorization": "Bearer YOUR_TOKEN" };
// List all builds
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/",
{ headers }
);
const builds = await response.json();
// List builds ordered by upload date
const orderedResponse = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/?ordering=-uploaded_at",
{ headers }
);
Get Build Details
Retrieve detailed information about a specific build.
Endpoint
GET /api/frontend_workers/{slug}/builds/{build_uuid}/
URL Parameters
slug(required): The immutable slug identifier of the workerbuild_uuid(required): The UUID of the build
Response
{
"success": true,
"message": "Build retrieved successfully",
"status_code": 200,
"data": {
"uuid": "660e8400-e29b-41d4-a716-446655440001",
"frontend_worker_name": "My React App",
"status": "completed",
"uploaded_at": "2025-11-10T12:00:00Z",
"created_by_username": "john.doe",
"created_at": "2025-11-10T12:00:00Z",
"updated_at": "2025-11-10T12:00:30Z"
}
}
Example
- REST API
- Python
- JavaScript
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/660e8400.../
import requests
response = requests.get(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/660e8400.../",
headers={"Authorization": "Bearer YOUR_TOKEN"}
)
build = response.json()
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/660e8400.../",
{
headers: { "Authorization": "Bearer YOUR_TOKEN" }
}
);
const build = await response.json();
Download Build Archive
Download the original archived zip file for a specific build. This is useful for:
- Creating backups of your deployments
- Reviewing the exact files that were deployed
- Deploying the same build to another environment
Endpoint
GET /api/frontend_workers/{slug}/builds/{build_uuid}/download/
URL Parameters
slug(required): The immutable slug identifier of the workerbuild_uuid(required): The UUID of the build
Requirements
- Build status must be
completed - Build must have an archive file in storage
Response
- Content-Type:
application/zip - Content-Disposition:
attachment; filename="build-{uuid}.zip" - Body: Binary zip file content
Example
- REST API
- Python
- JavaScript
# Download build archive
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/660e8400.../download/ \
-o build-backup.zip
# Verify download
unzip -l build-backup.zip
import requests
response = requests.get(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/660e8400.../download/",
headers={"Authorization": "Bearer YOUR_TOKEN"}
)
with open("build-backup.zip", "wb") as f:
f.write(response.content)
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/660e8400.../download/",
{
headers: { "Authorization": "Bearer YOUR_TOKEN" }
}
);
const blob = await response.blob();
// Save or process the blob as needed
Error Responses
400 Bad Request - Build not completed:
{
"success": false,
"message": "Cannot download build with status \"pending\". Only completed builds can be downloaded.",
"status_code": 400
}
404 Not Found - Archive doesn't exist:
{
"success": false,
"message": "This build does not have an archived file",
"status_code": 404
}
Delete Build
Delete a specific build and its archived file from storage. This is useful for:
- Cleaning up old builds to save storage space
- Removing failed or test builds
Endpoint
DELETE /api/frontend_workers/{slug}/builds/{build_uuid}/
URL Parameters
slug(required): The immutable slug identifier of the workerbuild_uuid(required): The UUID of the build
Important Restrictions
- Cannot delete the active build - You must set a different build as active first
- This prevents accidental deletion of the currently deployed version
- Protects your ability to redeploy and change subdomains
What Gets Deleted
- The build record from the database
- The archived zip file from S3 storage
Response
{
"success": true,
"message": "Build 660e8400-e29b-41d4-a716-446655440001 and its archive deleted successfully",
"status_code": 204
}
Example
- REST API
- Python
- JavaScript
# Delete an old build (not active)
curl -X DELETE https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/660e8400.../ \
-H "Authorization: Bearer YOUR_TOKEN"
import requests
response = requests.delete(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/660e8400.../",
headers={"Authorization": "Bearer YOUR_TOKEN"}
)
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-react-app/builds/660e8400.../",
{
method: "DELETE",
headers: { "Authorization": "Bearer YOUR_TOKEN" }
}
);
Error Responses
400 Bad Request - Trying to delete active build:
{
"success": false,
"message": "Cannot delete the active build. Please set a different build as active first.",
"status_code": 400
}
404 Not Found - Build doesn't exist or belongs to different worker:
{
"success": false,
"message": "Not found.",
"status_code": 404
}
Safe Deletion Workflow
If you need to delete the active build:
- REST API
- Python
- JavaScript
# 1. List all builds to find another one
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-tenant.taruvi.app/api/frontend_workers/my-worker/builds/
# 2. Set a different build as active
curl -X PATCH https://your-tenant.taruvi.app/api/frontend_workers/my-worker/set-active-build/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"build_uuid": "new-active-build-uuid"}'
# 3. Now you can delete the old build
curl -X DELETE https://your-tenant.taruvi.app/api/frontend_workers/my-worker/builds/old-build-uuid/ \
-H "Authorization: Bearer YOUR_TOKEN"
import requests
headers = {"Authorization": "Bearer YOUR_TOKEN"}
# 1. List all builds to find another one
builds = requests.get(
"https://your-tenant.taruvi.app/api/frontend_workers/my-worker/builds/",
headers=headers
).json()
# 2. Set a different build as active
requests.patch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-worker/set-active-build/",
headers={**headers, "Content-Type": "application/json"},
json={"build_uuid": "new-active-build-uuid"}
)
# 3. Now you can delete the old build
requests.delete(
"https://your-tenant.taruvi.app/api/frontend_workers/my-worker/builds/old-build-uuid/",
headers=headers
)
const headers = { "Authorization": "Bearer YOUR_TOKEN" };
// 1. List all builds to find another one
const builds = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-worker/builds/",
{ headers }
).then(r => r.json());
// 2. Set a different build as active
await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-worker/set-active-build/",
{
method: "PATCH",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ build_uuid: "new-active-build-uuid" })
}
);
// 3. Now you can delete the old build
await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-worker/builds/old-build-uuid/",
{ method: "DELETE", headers }
);
Common Use Cases
Deploy Multiple Environments
Create separate workers for development, staging, and production:
- REST API
- Python
- JavaScript
# Development
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "name=App Dev" \
-F "subdomain_input=app-dev" \
-F "[email protected]"
# Staging
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "name=App Staging" \
-F "subdomain_input=app-staging" \
-F "[email protected]"
# Production
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "name=App Production" \
-F "subdomain_input=app" \
-F "[email protected]"
import requests
headers = {"Authorization": "Bearer YOUR_TOKEN"}
url = "https://your-tenant.taruvi.app/api/frontend_workers/"
environments = [
{"name": "App Dev", "subdomain_input": "app-dev", "file": "dev-build.zip"},
{"name": "App Staging", "subdomain_input": "app-staging", "file": "staging-build.zip"},
{"name": "App Production", "subdomain_input": "app", "file": "prod-build.zip"},
]
for env in environments:
response = requests.post(
url,
headers=headers,
data={"name": env["name"], "subdomain_input": env["subdomain_input"]},
files={"file": open(env["file"], "rb")}
)
print(f"{env['name']}: {response.json()['data']['web_url']}")
const headers = { "Authorization": "Bearer YOUR_TOKEN" };
const url = "https://your-tenant.taruvi.app/api/frontend_workers/";
const environments = [
{ name: "App Dev", subdomain: "app-dev", file: devBuildZip },
{ name: "App Staging", subdomain: "app-staging", file: stagingBuildZip },
{ name: "App Production", subdomain: "app", file: prodBuildZip },
];
for (const env of environments) {
const formData = new FormData();
formData.append("name", env.name);
formData.append("subdomain_input", env.subdomain);
formData.append("file", env.file);
const response = await fetch(url, {
method: "POST",
headers,
body: formData
});
const result = await response.json();
console.log(`${env.name}: ${result.data.web_url}`);
}
Your environments are now available at (with environment prefix if applicable):
- Dev:
https://dev-app-dev.taruvi.app/ - Staging:
https://dev-app-staging.taruvi.app/ - Production:
https://dev-app.taruvi.app/
Quick Rollback
If you discover issues with your latest deployment:
- REST API
- Python
- JavaScript
# 1. List all builds to find the previous version
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-tenant.taruvi.app/api/frontend_workers/my-app/builds/
# 2. Activate the previous build
curl -X PATCH https://your-tenant.taruvi.app/api/frontend_workers/my-app/set-active-build/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"build_uuid": "previous-build-uuid"}'
import requests
headers = {"Authorization": "Bearer YOUR_TOKEN"}
# 1. List all builds to find the previous version
builds = requests.get(
"https://your-tenant.taruvi.app/api/frontend_workers/my-app/builds/",
headers=headers
).json()
# 2. Activate the previous build
previous_uuid = builds["data"][1]["uuid"] # Second most recent
requests.patch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-app/set-active-build/",
headers={**headers, "Content-Type": "application/json"},
json={"build_uuid": previous_uuid}
)
const headers = { "Authorization": "Bearer YOUR_TOKEN" };
// 1. List all builds to find the previous version
const builds = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-app/builds/",
{ headers }
).then(r => r.json());
// 2. Activate the previous build
const previousUuid = builds.data[1].uuid; // Second most recent
await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-app/set-active-build/",
{
method: "PATCH",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ build_uuid: previousUuid })
}
);
Your application instantly reverts to the previous version.
Continuous Deployment
Integrate with your CI/CD pipeline:
#!/bin/bash
# deploy.sh
# Build the application
npm run build
# Create archive
cd build && zip -r ../release.zip . && cd ..
# Upload new build (use slug in URL)
curl -X PATCH https://your-tenant.taruvi.app/api/frontend_workers/${WORKER_SLUG}/ \
-H "Authorization: Bearer ${TARUVI_TOKEN}" \
-F "[email protected]"
# Get the new build UUID (from response)
NEW_BUILD_UUID="..." # Extract from response
# Activate the new build
curl -X PATCH https://your-tenant.taruvi.app/api/frontend_workers/${WORKER_SLUG}/set-active-build/ \
-H "Authorization: Bearer ${TARUVI_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"build_uuid\": \"${NEW_BUILD_UUID}\"}"
Change Subdomain
Update your worker's subdomain while preserving all files (slug remains the same):
- REST API
- Python
- JavaScript
# Update subdomain (use slug in URL, which doesn't change)
curl -X PATCH https://your-tenant.taruvi.app/api/frontend_workers/my-app/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"subdomain_input": "new-subdomain-name"}'
# Result:
# - slug: "my-app" (unchanged - still use this in API)
# - subdomain: "new-subdomain-name" (changed)
# - web_url: "https://dev-new-subdomain-name.taruvi.app/" (updated)
import requests
response = requests.patch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-app/",
headers={
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json"
},
json={"subdomain_input": "new-subdomain-name"}
)
result = response.json()
# slug: "my-app" (unchanged), subdomain: "new-subdomain-name" (changed)
const response = await fetch(
"https://your-tenant.taruvi.app/api/frontend_workers/my-app/",
{
method: "PATCH",
headers: {
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json"
},
body: JSON.stringify({ subdomain_input: "new-subdomain-name" })
}
);
const result = await response.json();
// slug: "my-app" (unchanged), subdomain: "new-subdomain-name" (changed)
All files are automatically migrated to the new subdomain path, and your application is immediately available at the new URL.
Build Management and Cleanup
Manage your build history to optimize storage:
- REST API
- Python
- JavaScript
# 1. List all builds to see storage usage
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-tenant.taruvi.app/api/frontend_workers/my-app/builds/
# 2. Download important builds for backup before cleanup
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-tenant.taruvi.app/api/frontend_workers/my-app/builds/{build_uuid}/download/ \
-o production-backup-v1.2.3.zip
# 3. Delete old builds (keeping recent ones and backups)
# Note: Cannot delete the active build
curl -X DELETE https://your-tenant.taruvi.app/api/frontend_workers/my-app/builds/{old_build_uuid}/ \
-H "Authorization: Bearer YOUR_TOKEN"
import requests
headers = {"Authorization": "Bearer YOUR_TOKEN"}
base = "https://your-tenant.taruvi.app/api/frontend_workers/my-app"
# 1. List all builds
builds = requests.get(f"{base}/builds/", headers=headers).json()
# 2. Download important builds for backup
for build in builds["data"][:2]: # Keep latest 2
resp = requests.get(f"{base}/builds/{build['uuid']}/download/", headers=headers)
with open(f"backup-{build['uuid']}.zip", "wb") as f:
f.write(resp.content)
# 3. Delete old builds (cannot delete active build)
for build in builds["data"][3:]: # Delete older builds
requests.delete(f"{base}/builds/{build['uuid']}/", headers=headers)
const headers = { "Authorization": "Bearer YOUR_TOKEN" };
const base = "https://your-tenant.taruvi.app/api/frontend_workers/my-app";
// 1. List all builds
const builds = await fetch(`${base}/builds/`, { headers }).then(r => r.json());
// 2. Download important builds for backup
for (const build of builds.data.slice(0, 2)) {
const resp = await fetch(`${base}/builds/${build.uuid}/download/`, { headers });
const blob = await resp.blob();
// Save blob as needed
}
// 3. Delete old builds (cannot delete active build)
for (const build of builds.data.slice(3)) {
await fetch(`${base}/builds/${build.uuid}/`, { method: "DELETE", headers });
}
This keeps your storage clean while maintaining backups of critical versions.
White-Labeled Multi-Tenant Deployments
Deploy the same application to multiple client domains with their own branding:
Use Case: As a SaaS agency, you want to deploy a "Customer Portal" application to multiple clients (Walmart, Target, Best Buy), where each client sees their own domain.
- REST API
- Python
- JavaScript
# Deploy to Walmart's domain
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "name=Walmart Customer Portal" \
-F "subdomain_input=portal" \
-F "is_internal=false" \
-F "domain=walmart.com" \
-F "[email protected]"
# Live at: https://dev-portal.walmart.com/ (if dev environment)
# Deploy same app to Target's domain
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "name=Target Customer Portal" \
-F "subdomain_input=portal" \
-F "is_internal=false" \
-F "domain=target.com" \
-F "[email protected]"
# Live at: https://dev-portal.target.com/
# Deploy same app to Best Buy's domain
curl -X POST https://your-tenant.taruvi.app/api/frontend_workers/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "name=Best Buy Support" \
-F "subdomain_input=support" \
-F "is_internal=false" \
-F "domain=bestbuy.com" \
-F "[email protected]"
# Live at: https://dev-support.bestbuy.com/
import requests
headers = {"Authorization": "Bearer YOUR_TOKEN"}
url = "https://your-tenant.taruvi.app/api/frontend_workers/"
clients = [
{"name": "Walmart Customer Portal", "subdomain": "portal", "domain": "walmart.com"},
{"name": "Target Customer Portal", "subdomain": "portal", "domain": "target.com"},
{"name": "Best Buy Support", "subdomain": "support", "domain": "bestbuy.com"},
]
for client in clients:
response = requests.post(
url,
headers=headers,
data={
"name": client["name"],
"subdomain_input": client["subdomain"],
"is_internal": "false",
"domain": client["domain"]
},
files={"file": open("customer-portal.zip", "rb")}
)
result = response.json()
print(f"{client['name']}: {result['data']['web_url']}")
const headers = { "Authorization": "Bearer YOUR_TOKEN" };
const url = "https://your-tenant.taruvi.app/api/frontend_workers/";
const clients = [
{ name: "Walmart Customer Portal", subdomain: "portal", domain: "walmart.com" },
{ name: "Target Customer Portal", subdomain: "portal", domain: "target.com" },
{ name: "Best Buy Support", subdomain: "support", domain: "bestbuy.com" },
];
for (const client of clients) {
const formData = new FormData();
formData.append("name", client.name);
formData.append("subdomain_input", client.subdomain);
formData.append("is_internal", "false");
formData.append("domain", client.domain);
formData.append("file", customerPortalZip);
const response = await fetch(url, { method: "POST", headers, body: formData });
const result = await response.json();
console.log(`${client.name}: ${result.data.web_url}`);
}
Key Benefits:
- Same codebase: Deploy identical applications across multiple client domains
- No subdomain conflicts: The same subdomain (e.g., "portal") can be used on different domains
- Unique slugs: Each worker gets a unique slug for API access (auto-incremented if needed)
- Client branding: Each client sees their own domain, not your company's
- Isolated deployments: Each deployment is independent - you can update Walmart without affecting Target
- Centralized management: Manage all client deployments from a single tenant
Architecture:
- Each worker has its own builds and deployment history
- Workers can share the same name (e.g., "Customer Portal") but have unique
(subdomain, domain)combinations - Configure your backend to serve tenant-specific data based on the domain
- DNS must be configured by each client to point their domain to your Taruvi instance
Configuration
DNS Requirements
For frontend workers to be accessible, your Taruvi instance administrator must configure:
-
Base Domain: Set via environment variable
FRONTEND_WORKER_BASE_DOMAIN(e.g.,taruvi.app) -
Wildcard DNS Entry: Create a wildcard DNS record pointing to your Taruvi instance:
*.taruvi.app A <IP_ADDRESS>
*.taruvi.app CNAME <CLOUDFRONT_OR_S3_ENDPOINT>
This allows any subdomain (e.g., my-app.taruvi.app, dashboard.taruvi.app) to route correctly.
Storage
Frontend workers use cloud storage (S3-compatible) for:
- Deployment files: Live application files served to users
- Build archives: Compressed archives for version history and quick rollback
All storage is managed automatically by the platform.
Best Practices
Build Preparation
- Optimize for Production: Always use production builds (
npm run build,yarn build) - Test Locally: Test your build locally before deploying (use
serveor similar) - Check File Paths: Ensure all assets use relative paths, not absolute paths
- Environment Variables: Bake environment-specific values into your build
Archive Structure
Your archive should contain your build files at the root or in a single root folder:
Good structure:
my-app.zip
├── index.html
├── static/
│ ├── css/
│ └── js/
└── assets/
Also acceptable (automatically handled):
my-app.zip
└── build/
├── index.html
├── static/
└── assets/
Avoid nested structures:
my-app.zip
└── project/
└── build/
└── index.html
Version Management
- Keep History: Don't delete old builds immediately - they enable quick rollback
- Test Before Activation: Upload new builds and test before setting them active
- Gradual Rollout: Use separate workers for staged rollouts (dev -> staging -> prod)
Security
- Don't Commit Secrets: Never include API keys or secrets in your frontend build
- Use Environment Variables: Configure backend URLs and public keys via build-time variables
- Regular Updates: Keep your dependencies updated and rebuild regularly
Troubleshooting
Build Status is "Failed"
Possible Causes:
- Archive is corrupted or invalid format
- Archive exceeds maximum size (10MB)
- Unsupported file format
Solution:
- Verify your archive can be extracted locally
- Check archive format is one of:
.zip,.tar,.tar.gz,.tgz - Reduce build size by optimizing assets
Application Not Loading
Possible Causes:
- Index.html not at the root of the archive
- Assets using absolute paths instead of relative
- CORS issues with backend API
Solution:
- Ensure
index.htmlis at the archive root (or single root folder) - Configure your build tool to use relative paths
- Configure your backend API to allow requests from your frontend worker domain
Subdomain Already Taken
Error: "Subdomain 'portal' is already in use for domain 'taruvi.app'. Please choose a different subdomain."
Cause: You explicitly provided a subdomain that is already in use for the same domain.
Solution:
- Use the availability checker: Check subdomain availability before submitting
- Choose a different subdomain: The
(subdomain, domain)combination must be unique - Let the system auto-generate: If you don't provide a subdomain, it will be auto-generated from the name and a numeric suffix will be added if needed (e.g.,
portal-1,portal-2) - Use a different domain: For external workers, you can use the same subdomain on different domains (e.g.,
portal.walmart.comandportal.target.comcan coexist)
Slug is Immutable Error
Error: "Slug cannot be changed after creation. It is immutable."
Cause: You attempted to change the slug of an existing worker.
Solution:
- The slug is a permanent identifier and cannot be changed
- If you need a different API identifier, create a new worker
- You can change the subdomain (which updates the web URL) without affecting the slug
- Use the subdomain update feature to change where your app is hosted
Cannot Set Active Build
Error: "Build must have status 'completed'"
Solution:
- Wait for the build upload to complete
- If build is marked as "failed", upload a new build
- Verify the build UUID is correct and belongs to this worker
Cannot Delete Build
Error: "Cannot delete the active build. Please set a different build as active first."
Reason: The active build's archive is needed for:
- Redeploying when changing subdomains
- Quick rollback via the set-active-build endpoint
- Disaster recovery if deployment files are corrupted
Solution:
- Set a different build as active first using the Set Active Build endpoint
- Then delete the old build
- See the Safe Deletion Workflow for step-by-step instructions
Limits and Quotas
| Resource | Limit |
|---|---|
| Maximum archive size | 10MB |
| Subdomain length | 3-100 characters |
| Supported formats | .zip, .tar, .tar.gz, .tgz |
| Builds per worker | Unlimited |
| Workers per site | Unlimited |
API Response Codes
All responses include a standardized format with success, message, and status_code fields.
| Code | Description | Success Field |
|---|---|---|
| 200 | OK - Request successful | true |
| 201 | Created - Worker created successfully | true |
| 204 | No Content - Deletion successful | true |
| 400 | Bad Request - Invalid data or file | false |
| 401 | Unauthorized - Authentication required | false |
| 403 | Forbidden - Permission denied | false |
| 404 | Not Found - Worker or build not found | false |
| 409 | Conflict - Subdomain already exists | false |
| 413 | Payload Too Large - File exceeds 10MB | false |
| 500 | Internal Server Error - Server error occurred | false |
Example Error Response:
{
"success": false,
"message": "Failed to update frontend worker",
"status_code": 500,
"data": {
"details": "Database connection timeout"
}
}
Next Steps
- Explore the API Overview for authentication details
- Check out User Management for managing access
- Learn about Organizations and Sites for multi-tenancy
Need help? Check the interactive API documentation at /api/docs/ or contact support.