Template API
Complete API reference for managing environment templates. Templates define the base images and configurations used to create sandbox environments in ScaleBox.
Authentication Required: All API requests require authentication using the X-API-Key header with your API key.
Template Concepts
Templates in ScaleBox are pre-configured environment definitions that serve as the foundation for creating sandbox environments. They define the base image, default resource allocations, and metadata that will be used when users create new sandboxes.
Template Components
- Base Image - Harbor registry image with pre-installed software and configurations
- Resource Defaults - Default CPU, memory, and storage allocations for sandboxes
- Metadata - JSON metadata describing template capabilities and use cases
- Access Control - Public/private visibility and ownership permissions
Template Name Validation
Template names are validated strictly so they work as identifiers (e.g. in sandbox creation and image paths). The same rules apply when creating or updating a template and when providing new_name for share.
- Length: 4–32 characters
- Characters: Only lowercase letters (a–z), numbers (0–9), hyphens (-), and underscores (_)
- Start and end: Must start and end with an alphanumeric character (letter or digit)
- Cannot start with a number
- No consecutive separators: No
--,__,-_, or_- - Cannot start with
tpl-(reserved for system template IDs) - Reserved words (exact match, case-insensitive): admin, root, system, scalebox, harbor, docker, k8s, kubernetes, api, www, mail, ftp, ssh, public, private, test, dev, prod, staging, local, localhost, example, sample
Invalid examples: My Nginx (uppercase and space), nginx (too short), tpl-my-app (reserved prefix), my--app (consecutive hyphens)
Valid examples: my-nginx, python-data-science, node_web_app
Template Statuses
Templates progress through different statuses during their lifecycle:
| Status | Description |
|---|---|
pending | Template has been created but build process has not started |
building | Template image is being built from source |
pushing | Template image is being pushed to Harbor registry |
available | Template is ready for use in sandbox creation |
failed | Template build or push process failed |
Template Access Control
Template access is controlled by ownership and visibility settings:
| Type | Description |
|---|---|
| Public Templates | Accessible to all authenticated users, typically created by admin users |
| Private Templates | Accessible only to the owner and root users in the same account |
| Account Templates | Accessible to all users in the same account (root user templates) |
Response Conventions
Most JSON endpoints on this page return the standard envelope:
{
"success": true,
"data": {},
"timestamp": "2026-04-17T12:34:56Z"
}Some legacy handlers also place a message field inside data; that is part of the current backend contract.
Special cases:
GET /v1/templates/{template_id}/document?download=...returns a raw Markdown file, not the JSON envelope.POST /v1/templates/importandPOST /v1/templates/{template_id}/importreturn202 Acceptedimmediately; the import continues asynchronously.
API Endpoints
List Templates
GET /v1/templates
List all accessible templates for the authenticated user.
Request Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| page | integer | No | Page number, default 1 |
| limit | integer | No | Page size, default 20 (alias: page_size) |
| offset | integer | No | Offset, default 0 (alias: skip) |
| status | string | No | Filter templates by status (pending, building, pushing, available, failed) |
| visibility | string | No | Filter templates by visibility (private, account_shared, public) |
| name | string | No | Exact template name match (e.g. for resolving by name) |
| usable | string | No | Set to 'true' to return only templates usable for sandbox creation |
Each template object includes has_template_document (boolean): whether non-empty Markdown is stored. The template_document body itself is omitted from list responses to keep payloads small; use Get template or Get template document for full text.
Success response
200 OK in the standard envelope:
data.templates: array of template list objects. Each object includestemplate_id,name,description,default_cpu_count,default_memory_mb,visibility,template_source,custom_image_source,external_image_url,external_registry_username,status,owner_user_id,owner_account_id,harbor_image_url,harbor_project,harbor_repository,harbor_tag,image_size_bytes,metadata,ports,custom_command,ready_command,build_started_at,push_started_at,push_completed_at,build_error_message,last_storage_sync_at,created_at,updated_at, andhas_template_document.data.templates[].owner: optional object withuser_id,username, anddisplay_namewhen owner information is exposed.data.pagination: object withpage,limit,total,total_pages, and optionaloffset.
Example
curl -X GET https://api.scalebox.dev/v1/templates \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678"Get Template
GET /v1/templates/{template_id}
Get detailed information about a specific template.
Example
curl -X GET https://api.scalebox.dev/v1/templates/tpl-abc123def456789 \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678"template_document field: The full template response may include template_document (string or null) — long-form Markdown user documentation (getting started, ports, examples). It is separate from the short description. List templates responses omit this field to keep payloads small; use GET /v1/templates/{template_id} or the dedicated document endpoint below.
Success response
200 OK in the standard envelope with data set to one template object. It contains the same fields as List Templates, plus parent_template_id, root_template_id, and template_document.
Template documentation (Markdown)
Templates can store an optional Markdown document (max 65,535 bytes, MySQL TEXT). Any user who can access the template can read the document; create, update, and delete require the same permissions as editing the template (owner, account root for account members, or admin for public templates).
Get template document (JSON)
GET /v1/templates/{template_id}/document
Returns 200 OK in the standard envelope:
{
"success": true,
"data": {
"template_id": "tpl-abc123def456789",
"template_document": "# Getting started\n\n..."
},
"timestamp": "2026-04-17T12:34:56Z"
}template_document is omitted or null when no document is stored.
curl -X GET https://api.scalebox.dev/v1/templates/tpl-abc123def456789/document \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678"Download template document (raw Markdown)
GET /v1/templates/{template_id}/document?download=1
Same access rules as the JSON endpoint. On success it returns 200 OK with a raw Markdown body, Content-Type: text/markdown; charset=utf-8, and Content-Disposition: attachment for a .md file. Returns 400 if there is no document or the stored content is empty/whitespace-only (avoids useless downloads).
Accepted query values include download=true, download=1, or download=yes.
curl -L -o guide.md "https://api.scalebox.dev/v1/templates/tpl-abc123def456789/document?download=1" \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678"Set template document
PUT /v1/templates/{template_id}/document
Request body (JSON):
| Name | Type | Required | Description |
|---|---|---|---|
| template_document | string | Yes | Markdown body. Empty or whitespace-only clears the document |
Success response
200 OK in the standard envelope with:
data.template_iddata.template_document- stored Markdown string, ornullif the submitted content was empty or whitespace-only
curl -X PUT https://api.scalebox.dev/v1/templates/tpl-abc123def456789/document \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678" \
-H "Content-Type: application/json" \
-d '{"template_document": "# Guide\n\nHello."}'Delete template document
DELETE /v1/templates/{template_id}/document
Clears stored Markdown. Same edit permissions as PUT.
Success response
200 OK in the standard envelope with top-level message: "Template document removed" and:
data.template_iddata.template_document- alwaysnull
curl -X DELETE https://api.scalebox.dev/v1/templates/tpl-abc123def456789/document \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678"Create Template
POST /v1/templates
Create a new template (ScaleBox-family or custom). Full validation applies: name uniqueness, budget, subscription limits, and for custom external images pre-validation.
Request Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| name | string | Yes | - | Template name. Must follow Template Name Validation rules |
| description | string | No | - | Detailed description of the template |
| default_cpu_count | integer | No | 2 | Default CPU cores for sandboxes using this template |
| default_memory_mb | integer | No | 512 | Default memory in MB for sandboxes using this template |
| visibility | string | No | private | Template visibility: 'private', 'account_shared', or 'public' (admin only) |
| template_source | string | No | scalebox_family | 'scalebox_family' (default) or 'custom' |
| custom_image_source | string | No | - | Required if template_source is 'custom': 'external' or 'imported' |
| external_image_url | string | No | - | Required for custom external templates. Full image URL |
| external_registry_username | string | No | - | Optional registry username for private external images |
| external_registry_password | string | No | - | Optional registry password for private external images |
| harbor_image_url | string | No | - | Full Harbor image URL |
| harbor_project | string | No | Auto-generated | Harbor project name |
| harbor_repository | string | No | - | Harbor repository name |
| harbor_tag | string | No | latest | Harbor image tag |
| metadata | string | No | JSON metadata for the template | |
| base_template_id | string | No | - | Template ID to inherit from (same template_source only) |
| ports | string | No | [] | JSON array of port configurations |
| reset_ports | boolean | No | false | If true, remove unprotected ports inherited from base |
| custom_command | string | No | - | Custom startup command for the container |
| ready_command | string | No | - | Readiness probe: JSON with type (exec/httpGet/tcpSocket) |
Success response
201 Created in the standard envelope. data includes:
template_id,name,description,default_cpu_count,default_memory_mb,visibilitytemplate_source,custom_image_source,external_image_url,external_registry_usernamestatus,harbor_image_url,harbor_project,harbor_repository,harbor_tag,image_size_bytesmetadata,ports,build_started_at,push_started_at,push_completed_at,build_error_message,last_storage_sync_atowner_id,created_at,updated_at
Note the create response uses owner_id as the field name here.
Example
curl -X POST https://api.scalebox.dev/v1/templates \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678" \
-H "Content-Type: application/json" \
-d '{
"name": "python-data-science",
"description": "Python environment with data science libraries",
"default_cpu_count": 2,
"default_memory_mb": 4096,
"harbor_project": "scalebox-public",
"harbor_repository": "python-data-science",
"harbor_tag": "latest",
"metadata": {
"category": "data-science",
"languages": ["python"],
"frameworks": ["pandas", "numpy", "scikit-learn"]
}
}'Update Template
PUT /v1/templates/{template_id}
Update template configuration and Harbor information.
Success response
200 OK with top-level message: "Template updated successfully". data.template includes:
template_id,name,description,template_documentdefault_cpu_count,default_memory_mb,visibility,statusharbor_image_url,harbor_project,harbor_repository,harbor_tag,image_size_bytesmetadata,ports,build_started_at,push_started_at,push_completed_at,build_error_message,last_storage_sync_atowner_id,created_at,updated_at
Example
curl -X PUT https://api.scalebox.dev/v1/templates/tpl-abc123def456789 \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678" \
-H "Content-Type: application/json" \
-d '{
"description": "Updated Python environment with latest libraries",
"default_memory_mb": 8192
}'Delete Template
DELETE /v1/templates/{template_id}
Delete a template and its Harbor image.
Success response
200 OK with top-level message: "Template deleted successfully". No data object is returned on success.
Example
curl -X DELETE https://api.scalebox.dev/v1/templates/tpl-abc123def456789 \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678"Legacy POST /v1/templates/register removed: That endpoint is not in the API anymore. If an image already exists in Harbor, create the template with POST /v1/templates and set Harbor fields (harbor_image_url, harbor_project, harbor_repository, harbor_tag, etc.) per Create Template. To copy an image from an external registry into Harbor, use POST /v1/templates/import (direct import) or POST /v1/templates/{template_id}/import (existing external template), documented below.
Direct import template (external → Harbor)
POST /v1/templates/import
Creates a custom template from an external OCI/Docker image reference and queues a Skopeo copy into ScaleBox Harbor in one step. Responds with 202 Accepted and data containing template_id, optional job_id, status, and image URLs when the import job is created.
Harbor project / repository / tag are derived by the server (repository aligns with the template name, tag latest); clients do not send Harbor destination fields for this path.
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Template name (same validation as Create Template) |
| external_image_url | string | Yes | Full upstream image reference (e.g. ghcr.io/org/image:tag) |
| description | string | No | Template description |
| external_registry_username | string | No | Registry username for private sources |
| external_registry_password | string | No | Registry password or token |
| default_cpu_count | integer | No | Sandbox defaults |
| default_memory_mb | integer | No | Sandbox defaults |
| visibility | string | No | private (default for most users), account_shared (account root only), or public (admin only) |
| ports | string | No | JSON port list (same shape as Create Template) |
| custom_command | string | No | Optional startup command |
| ready_command | string | No | Optional readiness probe JSON |
Success response
202 Accepted. The import continues asynchronously; poll GET /v1/templates/{template_id}/import.
data.template_iddata.namedata.visibilitydata.job_id,data.status,data.external_image_url,data.harbor_image_url,data.created_atwhen the job is createddata.message- eitherTemplate created and import startedorTemplate created but import failed to start. Retry via POST /v1/templates/:template_id/import
Example
curl -X POST https://api.scalebox.dev/v1/templates/import \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678" \
-H "Content-Type: application/json" \
-d '{
"name": "my-upstream-tool",
"description": "Copied from upstream registry",
"external_image_url": "docker.io/library/alpine:latest",
"default_cpu_count": 2,
"default_memory_mb": 1024,
"visibility": "private"
}'Start import for existing external template
POST /v1/templates/{template_id}/import
Starts Harbor import for a template row that already exists with custom_image_source: external. Returns 202 Accepted with job_id and status. Typical errors: template not external, already imported, import already running (409), permission denied, or no healthy cluster / Harbor (503).
No JSON body is required.
Success response
202 Accepted with:
data.job_iddata.template_iddata.statusdata.external_image_urldata.harbor_image_urldata.created_atdata.message-Import started
curl -X POST https://api.scalebox.dev/v1/templates/tpl-abc123def456789/import \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678"Get template import status
GET /v1/templates/{template_id}/import
Returns the active import job for the template, or the most recent completed / failed / cancelled job. Template owner only (same user as owner_user_id).
Response includes: job_id, template_id, status, progress_percentage, external_image_url, harbor_image_url, timestamps, last_log_message, error_message, skopeo_logs.
Success response
200 OK in the standard envelope with data containing:
job_id,template_id,status,progress_percentageexternal_image_url,harbor_image_urlcreated_at,started_at,completed_atlast_log_message,error_message,skopeo_logs
Cancel template import (by template)
DELETE /v1/templates/{template_id}/import
Cancels the active import job for this template. Owner only. 404 if there is no active job.
Success response
200 OK in the standard envelope with:
data.job_iddata.status- alwayscancelleddata.message-Import job cancelled
curl -X DELETE https://api.scalebox.dev/v1/templates/tpl-abc123def456789/import \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678"Update Template Status
PUT /v1/templates/{template_id}/status
Update template build status and tracking information.
Request Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| status | string | Yes | Template status (pending, building, pushing, available, failed) |
| error_message | string | No | Error message if status is failed |
| push_logs | string | No | Build and push logs |
| harbor_image_url | string | No | Updated Harbor image URL |
Success response
200 OK in the standard envelope with:
data.message-Template status updated successfullydata.template_iddata.build_status
Validate Template
POST /v1/templates/{template_id}/validate
Validate and activate a template with Harbor image.
Request Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| harbor_image_url | string | Yes | Harbor image URL to validate |
Success response
200 OK in the standard envelope with:
data.message-Template validated successfullydata.template_iddata.build_statusdata.harbor_image_url
Get Storage Usage
GET /v1/templates/storage-usage
Get private template storage usage statistics.
Success response
200 OK in the standard envelope with:
data.used_gbdata.limit_gbdata.percentage
Example
curl -X GET https://api.scalebox.dev/v1/templates/storage-usage \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678"Data example
{
"success": true,
"data": {
"used_gb": 2.5,
"limit_gb": 5.0,
"percentage": 50.0
},
"timestamp": "2026-04-17T12:34:56Z"
}List Import Jobs
GET /v1/import-jobs
List template import jobs for the current user.
Request Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| status | string | No | Filter: ongoing (default), pending, running, completed, failed, cancelled |
| template_id | string | No | Filter jobs for this template ID |
| page | integer | No | Page number, default 1 |
| limit | integer | No | Max jobs to return (1-100). Alias: page_size |
| offset | integer | No | Pagination offset (alias: skip) |
Success response
200 OK in the standard envelope with:
data.jobs: array of import job summaries withjob_id,template_id,status,progress_percentage,external_image_url,harbor_image_url,created_at,started_at,completed_at, anderror_messagedata.pagination: object withpage,limit,total,total_pages, and optionaloffset
Get Import Job
GET /v1/import-jobs/{job_id}
Get a single import job by job ID.
Success response
200 OK in the standard envelope with:
data.job_id,data.template_id,data.status,data.progress_percentagedata.external_image_url,data.harbor_image_urldata.created_at,data.started_at,data.completed_at,data.updated_atdata.last_log_message,data.error_message,data.skopeo_logsdata.retry_count,data.max_retries
Cancel Import Job
DELETE /v1/import-jobs/{job_id}
Cancel an import job by job ID (same ownership rules as listing jobs).
Success response
200 OK in the standard envelope with:
data.job_iddata.status- alwayscancelleddata.message-Import job cancelled
curl -X DELETE https://api.scalebox.dev/v1/import-jobs/job-xyz789 \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678"You can also cancel via DELETE /v1/templates/{template_id}/import when you only know the template ID.
Share Template
POST /v1/templates/{template_id}/share
Share a private template with your account (visibility → account_shared). Only template owner or admin.
Request Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| new_name | string | No | Optional new name when sharing |
Success response
200 OK in the standard envelope with:
data.template_iddata.visibility-account_shareddata.harbor_projectdata.harbor_image_urldata.message-Template successfully shared with account
Example
curl -X POST https://api.scalebox.dev/v1/templates/tpl-abc123def456789/share \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678" \
-H "Content-Type: application/json" \
-d '{}'Unshare Template
POST /v1/templates/{template_id}/unshare
Make an account-shared template private again. Only template owner or admin.
Success response
200 OK in the standard envelope with:
data.template_iddata.visibility-privatedata.harbor_projectdata.harbor_image_urldata.message-Template successfully unshared (now private)
Example
curl -X POST https://api.scalebox.dev/v1/templates/tpl-abc123def456789/unshare \
-H "X-API-Key: sk-1234567890abcdef1234567890abcdef12345678" \
-H "Content-Type: application/json" \
-d '{}'Create vs direct import
| Aspect | POST /v1/templates | POST /v1/templates/import |
|---|---|---|
| Purpose | Create or derive a template (base_template_id), or create with Harbor / custom fields per API rules | Create template and queue external → Harbor copy |
| Image | You supply Harbor and/or external fields according to template type | Requires external_image_url; Harbor destination is server-derived |
| Response | 201 (typical create) | 202 (async import job) |
| When to use | Normal lifecycle, derivatives, images already addressable in Harbor in the create payload | Onboarding an upstream registry image in one API call |
Harbor Integration
Templates are tightly integrated with Harbor container registry for image storage and management:
Harbor Project Structure
- Public Templates: Stored in
scalebox-publicproject - Private Templates: Stored in user-specific projects like
scalebox-{user_id} - Admin Templates: Can be stored in any project, including
scalebox-public
Image Management
- Automatic Size Detection: Template image sizes are automatically retrieved from Harbor
- Storage Tracking: Private template storage usage is tracked and limited
- Cleanup: Deleting templates automatically removes Harbor images and repositories
Error Handling
| Scenario | Description |
|---|---|
| Template Name Validation | Template names must follow the Template Name Validation rules |
| Access Control | Users can only access public templates and their own private templates |
| Template Usage | Templates cannot be deleted if they are being used by active sandboxes |
Best Practices
| Practice | Description |
|---|---|
| Template Naming | Use names that pass validation: lowercase, 4–32 characters |
| Resource Defaults | Set appropriate default CPU and memory based on typical usage patterns |
| Metadata Usage | Include rich metadata to help users discover and understand template capabilities |
HTTP Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 429 | Too Many Requests |
| 500 | Internal Server Error |