This document provides purely technical reference documentation for the Sandboxes feature within the Valstorm platform. It is intended for internal developers and external integrators working with the Valstorm REST API, SDKs, and command-line interfaces.
Valstorm utilizes a secure multi-tenant architecture where each tenant (organization) has an isolated database. Dynamic resolution occurs within the data-access layer by mapping requests to a database whose name matches the user's active organization_id:
def get_db(self, user: User = None, db_name: str = None): # Dynamically maps database context to user.organization_id target_db = user.organization_id if user else db_name return self.client[target_db]
By leveraging this model, Sandboxes are provisioned as completely separate, isolated databases. No changes to query routing or database access layers are needed.
A sandbox's ID (which acts as its organization_id and database name) is deterministically generated as:
{parent_organization_id}_s_{api_name}
{parent_organization_id}: The standard UUID4 of the parent production organization.{api_name}: The sanitized lowercase alphanumeric label assigned to the sandbox (max 12 characters, e.g., dev, staging, feat_billing).Example Database Name: obj_lIuXffMgd3VtLM5m_s_dev
When a sandbox is created, the system copies configuration and metadata collections from the parent production database to seed the sandbox with an identical operating environment:
schemas: Custom entity and field definitions.app and app_page: AppBuilder workspaces, layouts, and page templates.automation: Visual flow builder configurations.permission and role: Access levels and group configurations.record_trigger & function: Custom serverless Python triggers, schedules, and scripts.template: Mail, document, and notification templates.Note: Transactional data (such as leads, contacts, invoices, and messaging logs) is not copied, ensuring sandboxes remain cleanly separated and unpolluted.
The frontend's built-in organization selector dynamically pulls accessible environments from the active user's organizations list:
"organizations": [ {"id": "obj_lIuXffMgd3VtLM5m", "name": "Acme Corp"}, {"id": "obj_lIuXffMgd3VtLM5m_s_dev", "name": "Acme Corp (Sandbox: dev)", "description": "Markdown text description"} ]
Creating, refreshing, or deleting sandboxes modifies this list on the user records in both the production tenant database and the central base database. Consequently, newly provisioned sandboxes instantly appear in the workspace UI organization switcher with zero client-side changes or UI refactoring.
To prevent sandboxes from accidentally charging customers or contacting real users, the following safety overrides are programmatically applied during provisioning:
stripe_customer_id and related billing keys to a non-billing free tier.auth_credential collection) are completely deleted during the cloning step to prevent sandboxes from accessing real API channels.All sandbox lifecycle interactions must go through the dedicated /v1/sandbox endpoints.
🚫 Crucial Security Rule: Direct manipulation of sandbox records via standard
/v1/object/sandboxendpoints is strictly blocked by theSandboxSystemHandlerwith a400 Bad Requestto guarantee database-level provisioning and integrity.
Provisions an isolated database, seeds configuration collections, clones active production administrators, and appends discoverability metadata.
POST/v1/sandboxAuthorization: Bearer <access_token>Content-Type: application/jsonname (string, Required): Lowercase alphanumeric name (e.g., "dev").description (string, Optional): Markdown text detailing the sandbox's purpose.id (string): Generated sandbox organization ID.name (string): Display name.is_sandbox (boolean): true.parent_organization_id (string): Parent UUID.sandbox_name (string): Lowercase identifier.description (string | null): Markdown description.curl -X POST "https://api.valstorm.com/v1/sandbox" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "staging", "description": "### Staging Sandbox\n\nUsed for testing **visual automations** and custom scripts before release." }'
{ "id": "sandbox_staging_obj_lIuXffMgd3VtLM5m", "name": "Acme Corp (Sandbox: staging)", "is_sandbox": true, "parent_organization_id": "obj_lIuXffMgd3VtLM5m", "sandbox_name": "staging", "description": "### Staging Sandbox\n\nUsed for testing **visual automations** and custom scripts before release." }
Lists all sandbox environments associated with the active production organization.
GET/v1/sandboxAuthorization: Bearer <access_token>List[SandboxResponse]curl -X GET "https://api.valstorm.com/v1/sandbox" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
[ { "id": "sandbox_staging_obj_lIuXffMgd3VtLM5m", "name": "Acme Corp (Sandbox: staging)", "is_sandbox": true, "parent_organization_id": "obj_lIuXffMgd3VtLM5m", "sandbox_name": "staging", "description": "### Staging Sandbox\n\nUsed for testing **visual automations** and custom scripts before release." } ]
Wipes the sandbox database entirely and re-clones configuration collections and users from the parent production database. The custom Markdown description of the sandbox is automatically preserved across refreshes.
POST/v1/sandbox/{name}/refreshAuthorization: Bearer <access_token>curl -X POST "https://api.valstorm.com/v1/sandbox/staging/refresh" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{ "id": "sandbox_staging_obj_lIuXffMgd3VtLM5m", "name": "Acme Corp (Sandbox: staging)", "is_sandbox": true, "parent_organization_id": "obj_lIuXffMgd3VtLM5m", "sandbox_name": "staging", "description": "### Staging Sandbox\n\nUsed for testing **visual automations** and custom scripts before release." }
Permanently drops the isolated database, removes central base database organization registry documents, and pulls sandbox references from all production user records.
DELETE/v1/sandbox/{name}Authorization: Bearer <access_token>curl -X DELETE "https://api.valstorm.com/v1/sandbox/staging" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{ "detail": "Sandbox 'staging' successfully deleted." }
Admin users can programmatically manage user access to sandboxes. When a user is added, their production record is cloned to sandbox_db['user'] and the sandbox reference is appended to their organizations switcher map in base_db and prod_db. Conversely, removing a user deletes their profile in the sandbox database and strips the organizations switcher map reference.
The /users endpoints support extremely flexible request schemas for users selection. You can specify a single user, a list of users, or user dictionaries, mixing and matching selectors:
"obj_rDJ5n1okQ3rwcdiq" (User ID)"[email protected]" (User Email - resolved automatically via @){"id": "obj_rDJ5n1okQ3rwcdiq"}{"user_id": "obj_rDJ5n1okQ3rwcdiq"}{"email": "[email protected]"}["[email protected]", "obj_rDJ5n1okQ3rwcdiq"][{"email": "[email protected]"}, {"id": "obj_rDJ5n1okQ3rwcdiq"}]POST/v1/sandbox/{name}/usersAuthorization: Bearer <access_token>Content-Type: application/jsonusers (Union[str, Dict, List[str], List[Dict]], Required): Flexible selector format.sandbox_name (string): Identifer label.added_users (List[str]): List of emails of successfully added users.curl -X POST "https://api.valstorm.com/v1/sandbox/staging/users" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "users": [ "[email protected]", {"id": "aut_XMDs1orfKCzI6ASV"} ] }'
{ "sandbox_name": "staging", "added_users": [ "[email protected]", "[email protected]" ] }
DELETE/v1/sandbox/{name}/users/{userIds}Authorization: Bearer <access_token>Content-Type: application/jsonuserIds (string, Required): Comma-separated list of user IDs to remove.204 OK: Users were successfully removed. No response body is returned.curl -X DELETE "https://api.valstorm.com/v1/sandbox/staging/users/aut_XMDs1orfKCzI6ASV,674d8f305116-cb85-4e17-aa56-cbfed088" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json"
When developing locally, you can switch your active CLI workspace targeting to run commands directly against a specific sandbox.
This is managed entirely via your local valstorm.json workspace configuration, ensuring a deterministic developer environment.
To switch your active workspace target to a sandbox (e.g., staging):
valstorm sandbox use staging # or using the switch alias: valstorm sandbox switch staging
This updates your valstorm.json to include "sandbox": "staging". All subsequent command executions (such as valstorm record list or valstorm push) will target the staging sandbox database.
To switch your workspace target back to the parent production/dev organization:
valstorm sandbox use-parent # or using the switch-back alias: valstorm sandbox switch-back
This removes the "sandbox" key from your valstorm.json configuration, instantly restoring targeting to the parent environment.
All administrative commands (such as creating sandboxes, listing sandboxes, or deploying apps to sandboxes via valstorm deploy app sandbox) bypass sandbox targeting. They automatically run with parent-level production permissions to allow provisioning and pushing.