Sandbox Lifecycle Management
Manage Salesforce sandbox environments through Connect REST API — list inventory, activate or discard completed refreshes, create and refresh sandboxes, and permanently delete sandboxes.
When This Skill Owns the Task
Use platform-sandbox-configure when the work involves:
- Listing or retrieving all sandboxes (GET /sandbox/reports)
- Getting details or status of a specific sandbox by name or by ID (07E prefix)
- Checking license usage and remaining capacity by license type (GET /sandbox/licenses)
- Activating a sandbox after a refresh completes (applying the refresh)
- Discarding a completed refresh (keeping existing sandbox data unchanged)
- Permanently deleting a sandbox to free up licenses
- Verifying activation or deletion completed
- Creating a new sandbox (Developer, Developer Pro, Partial Copy, or Full)
- Refreshing an existing sandbox with latest production data
Delegate elsewhere when the user is:
- Cloning a sandbox → Tooling API (
SandboxInfosObject) - Generating a post-copy automation JSON config from an SOP →
automation-sandbox-post-copy-config-generate - Applying/running a post-copy automation JSON config against a sandbox →
automation-sandbox-post-copy-configure
Help (Interactive Menu)
When the user types sandbox -help or sandbox help, respond with ONLY a text message showing the numbered operation list below. Do NOT call any API or tool — just display this menu and wait for the user to reply with a number.
The agent MUST respond with this exact markdown (not in a code block — render it directly as a bullet list):
Sandbox Lifecycle Management
1. Inventory & Details
- a. List all sandboxes — Names, types, statuses, IDs
- b. Get details (by name) — Status, license, config
- c. Get details (by ID) — Provide a 07E ID directly
- d. Check license usage — Available vs. used counts by license type
2. Create & Refresh
- a. Create a new sandbox — Dev, Dev Pro, Partial, Full
- b. Refresh a sandbox — Latest production data
3. Activate, Discard & Delete
- a. Activate a sandbox — Apply a completed refresh
- b. Discard a refresh — Reject, keep existing data
- c. Delete a sandbox — Permanent removal
4. Verify & Monitor
- a. Verify activation status — Check if activate completed
- b. Verify deletion status — Check if delete completed
5. Post-Copy Automation
- a. Create Post Copy Automation JSON Configs — Generate a config from an SOP
- b. Run Post Copy Automation — Apply a config JSON to a sandbox
Reply with a code (e.g. "3a") or describe what you need.
After the user replies, ask for the required input:
| Selection | Follow-up question |
|---|---|
| 1a | No input needed — proceed immediately |
| 1b | "What's the sandbox name?" |
| 1c | "What's the sandbox ID? (starts with 07E)" |
| 1d | No input needed — proceed immediately |
| 2a | First call GET /sandbox/licenses (Section 9) and show available/used counts; then ask for name + license type (Developer, Developer_Pro, Partial_Copy, Full) |
| 2b | First call GET /sandbox/reports (Section 1) and list refresh-eligible sandboxes (isPendingActivation: false); then ask which to refresh |
| 3a | "Which sandbox? Provide a name or 07E ID." |
| 3b | "Which sandbox? Provide a name or 07E ID." |
| 3c | "Which sandbox? Provide a name or 07E ID." |
| 4a | "Which sandbox did you activate? Provide a name or 07E ID." |
| 4b | "Which sandbox did you delete? Provide a name or 07E ID." |
| 5a | "Delegating to the post-copy config generator — please share the SOP (file, text, or screenshot)." |
| 5b | "Delegating to the post-copy config runner — please share the config JSON file and the target sandbox." |
Execute the corresponding operation below. Exception: 5a/5b delegate — 5a → automation-sandbox-post-copy-config-generate; 5b → automation-sandbox-post-copy-configure. This skill does not implement post-copy automation; do not generate or apply a config directly.
API Base
CRITICAL: For sandbox operations in this skill, use the Connect REST API. Two discovery paths exist:
- By name: Call
GET /sandbox/reportsto list all sandboxes and find the matching one bysandboxName. - By ID (07E prefix): Call
GET /sandbox/sandboxes/{sandboxId}directly — do NOT call/sandbox/reports.
Both paths return sandbox records with sandboxId (prefix 07E) which is required for all lifecycle mutation operations.
sf api request rest "/services/data/v66.0/sandbox/reports" --method GET
See references/api-response-shapes.md for the full response shape.
IMPORTANT: Do NOT use Tooling API (SandboxInfo/SandboxProcess) for discovery — mutation endpoints require the Connect REST API's sandboxId (07E), NOT SandboxInfo.Id (0GQ) or SandboxProcess.Id (0GR).
NEVER use SOQL / run_soql_query / sf data query for lifecycle reads (status, inventory, details, license, pending-activation). This data lives ONLY in the Connect REST API — no SObject returns it. (sf data query --use-tooling-api on SandboxInfo is still valid for Create/Refresh mutations, not a lifecycle read.)
Report API results exactly as they come back — never invent an error or a cause. count: 0 is a successful result (sandbox absent) — record not_found, don't reinterpret as a failure or retry via SOQL. If the API errors, capture the error body verbatim — do NOT speculate why (e.g. "must be a scratch org"); this endpoint doesn't report org edition/type, so any such guess is a fabrication.
Required permission: ManageSandboxes
Irreversible Actions — Always Confirm First
| Action | Why irreversible |
|---|---|
| Create | Consumes a license of the selected type |
| Activate | Overwrites sandbox with refreshed data |
| Discard | Refresh data is lost |
| Delete | Sandbox permanently removed |
Refresh with AutoActivate=true | Auto-applies on completion — same effect as Activate |
Operations
1. List Sandbox Inventory
Endpoint: GET /services/data/v66.0/sandbox/reports
Returns a list of all sandboxes with their IDs, names, statuses, and license types.
sf api request rest "/services/data/v66.0/sandbox/reports" --method GET
See references/api-response-shapes.md for the full response shape.
Use when: User asks "show me all sandboxes", "how many sandboxes do I have", "what's the status of my sandboxes"
Key response fields:
sandbox.sandboxId(07E prefix) — Required for all mutation operationssandbox.sandboxName— The sandbox name (top-level field)sandbox.license— Developer, Developer Pro, Partial Copy, Fullsandbox.isPendingActivation— true if refresh is pending activationsandbox.canActivate/canDelete/canDiscard— Permission flags
2. Get Sandbox Details
Endpoint: GET /services/data/v66.0/sandbox/sandboxes/{sandboxId}
Returns detailed info for a specific sandbox.
Use when: User asks about a specific sandbox's status, configuration, or metadata.
Key response fields:
status— Active, Pending Activation, Activating, Completed, etc.isPendingActivation— true if a refresh completed and awaits user decisionsandboxType— Developer, DeveloperPro, PartialCopy, FullsourceId— ID of the source org
3. Activate Sandbox (Apply Refresh)
Endpoint: PATCH /services/data/v66.0/sandbox/activate/{sandboxId}
CRITICAL DOMAIN RULE: This operation ONLY applies to sandboxes with a completed refresh in "Pending Activation" state. It applies the refreshed data to the sandbox. It does NOT "bring an inactive sandbox online" or "start" a sandbox.
Pre-conditions:
- Sandbox must be in
Pending Activationstatus - A refresh must have completed successfully
- User must have
ManageSandboxespermission
Before calling PATCH /activate:
- Confirmed sandbox is in
Pending Activationstatus via GET/sandbox/sandboxes/{id}(isPendingActivation: true) - Confirmed a refresh has completed successfully
- Received explicit user confirmation — this overwrites the sandbox with the refreshed data and cannot be undone
Use when: User says "activate it", "apply the refresh", "use the latest data"
After activation: The sandbox runs with the newly refreshed production data.
4. Verify Activation
Endpoint: GET /services/data/v66.0/sandbox/sandboxes/{sandboxId}
Poll this endpoint after activation to confirm status changed to Active. This is a verification step, not a standalone user action.
Use when: Agent needs to confirm activation completed (called automatically after activate).
5. Discard Sandbox (Reject Refresh)
Endpoint: DELETE /services/data/v66.0/sandbox/discardsandbox/{sandboxId}
CRITICAL DOMAIN RULE: This operation ONLY applies to sandboxes with a completed refresh in "Pending Activation" state. It rejects the refresh — the existing sandbox continues running with its current data unchanged. It does NOT:
- Free up licenses
- Soft-delete or hide the sandbox
- Reset the sandbox to match production
Pre-conditions:
- Sandbox must be in
Pending Activationstatus - A refresh must have completed
Before calling DELETE /discardsandbox:
- Confirmed sandbox is in
Pending Activationstatus via GET/sandbox/sandboxes/{id}(isPendingActivation: true) - Confirmed this is a discard (reject refresh), NOT a delete (permanent removal)
- Received explicit user confirmation — the refresh data will be permanently lost and cannot be undone
Use when: User says "discard the refresh", "keep existing data", "don't apply the refresh", "reject the refresh"
WARNING: Discard is not reversible. The user will need to trigger a new refresh if they want fresh production data later.
6. Delete Sandbox (Permanent)
Endpoint: DELETE /services/data/v66.0/sandbox/deletesandbox/{sandboxId}
Permanently removes a sandbox and frees the license.
Pre-conditions:
- Sandbox must exist
- User must have
ManageSandboxespermission
Before calling DELETE /deletesandbox:
- Surfaced sandbox details (name, license, status) with user and received explicit delete approval
Use when: User says "delete this sandbox", "remove it permanently", "free up the license"
WARNING: This is irreversible. Always confirm with the user before executing. Surface the sandbox name, license, and status as a safety check.
If asked to restore a deleted sandbox: No recovery path is documented today — escalate to Support rather than guessing at one.
7. Create a New Sandbox
Creates a new sandbox from scratch. Two approaches are supported — pick based on the user's preference; default to Approach A unless the user asks for a definition file or a repeatable DX blueprint.
Pre-conditions:
- Available license of the requested type must exist in the org
- Sandbox name must be unique and not already in use
- User must have
ManageSandboxespermission
Before creating — always confirm first:
-
Collected Name/License; asked about that license's optional inputs (
references/definition-file-approach.md) -
Shown this confirmation summary — common fields plus the license's own fields:
Creating a new sandbox — please confirm:
- Name:
<SandboxName> - Description:
<Description or "(none)"> - Create From: Production
- License:
<Developer | Developer Pro | Partial Copy | Full> - (plus this license's fields — see
references/definition-file-approach.md)
Feel free to change any of these before I proceed.
- Name:
-
Received explicit confirmation — this consumes a license of the selected type
Approach A — Tooling API record (direct)
API: Tooling API — SandboxInfo sObject
Required inputs:
SandboxName— Name for the new sandbox (alphanumeric, max 10 chars)LicenseType— One of:Developer,Developer_Pro,Partial_Copy,Full
Optional inputs:
Description— sandbox purposeFeatures— storage upgrade:Developer→400 MB,Developer_Pro→2 GB (irreversible); not forPartial_Copy/FullApexClassId— Apex class implementingSandboxPostCopy(runs post-creation)ActivationUserGroupId— Access group (default: All Active Users)TemplateId/HistoryDays/CopyChatter/CopyArchivedActivities—Partial_Copy/Full-only; per-license table inreferences/definition-file-approach.md
# Create a Developer sandbox
sf data create record --sobject SandboxInfo --use-tooling-api --values "SandboxName='mybox' LicenseType='Developer'"
Approach B — Sandbox definition file (Salesforce CLI)
DX-native path: write a JSON definition file, then sf org create sandbox --definition-file <file> --alias <name> --target-org <org>. Prefer for a checked-in, repeatable config or name-based Apex/group references. See references/definition-file-approach.md for the JSON example, command, and field table.
After creation: A SandboxProcess record is created with Status = Processing. The sandbox copy begins immediately.
Wrong type created: Delete and recreate with the correct type.
Renaming: Not a standalone action — only takes effect via a Refresh's SandboxName input.
8. Refresh an Existing Sandbox
Refreshes a sandbox with the latest production data. Two approaches are supported — pick based on the user's preference; default to Approach A unless the user asks for a definition file.
Approach A — Tooling API record (direct)
API: Tooling API — SandboxInfo sObject (PATCH)
Refreshes by updating the existing SandboxInfo record.
Required inputs:
- Sandbox name — to look up the
SandboxInforecord ID (0GQ prefix)
Optional inputs:
SandboxName— New name for the refreshed sandbox (if user wants to rename it; alphanumeric, max 10 chars)Description— New or updated description for the sandboxAutoActivate—trueto auto-activate when refresh completes (default: false)Features— storage upgrade:Developer→400 MB,Developer_Pro→2 GB (irreversible); not forPartial_Copy/FullActivationUserGroupId— Access group (default: All Active Users)TemplateId/HistoryDays/CopyChatter—Partial_Copy/Full-only; per-license table inreferences/definition-file-approach.md
Before triggering refresh — always confirm first:
-
Collected the sandbox name; asked about its license's optional inputs (
references/definition-file-approach.md) -
Shown this confirmation summary:
Refreshing sandbox
<name>— please confirm:- Rename to:
<SandboxName or "(no change)"> - Description:
<Description or "(no change)"> - Auto-Activate:
<Yes | No (default)> - Sandbox Access:
<ActivationUserGroupId or "All Active Users"> - (plus this license's fields — see
references/definition-file-approach.md)
Feel free to change any of these before I proceed.
- Rename to:
-
Received explicit confirmation — refresh overwrites the sandbox with production data; Auto-Activate=Yes applies automatically (equivalent to Activate)
Steps:
# 1. Look up SandboxInfo record Id by name
sf data query --query "SELECT Id, SandboxName, LicenseType, Description FROM SandboxInfo WHERE SandboxName = '<name>'" --use-tooling-api --json
# 2. PATCH to trigger refresh; include SandboxName/Description only if the user changed them
# Description is free text — escape any embedded single quotes (' -> \') before interpolating
sf data update record --sobject SandboxInfo --use-tooling-api --record-id <0GQ-id> --values "AutoActivate=true SandboxName='<newName>' Description='<description>'"
Approach B — Sandbox definition file (Salesforce CLI)
Refresh from the same JSON definition-file blueprint used for create, using sf org refresh sandbox --name <name> --definition-file <file> --target-org <org>. See references/definition-file-approach.md for the JSON example, command, and field table.
Pre-conditions:
- Sandbox must exist and be in a refreshable state
- Refresh interval must have elapsed (Developer = 1 day, Dev Pro = 1 day, Partial = 5 days, Full = 29 days)
- User must have
ManageSandboxespermission
After refresh: A new SandboxProcess record is created with Status = Processing. If AutoActivate=true, the sandbox activates automatically when done. Otherwise it enters Pending Activation state.
Refresh interval not met: State the last-refreshed date and eligible date. For a fresher copy sooner, a clone (delegated) works if a spare license exists.
9. Check License Usage
Endpoint: GET /services/data/v66.0/sandbox/licenses
Returns license capacity, usage, and remaining counts per license type — no sandbox name or ID needed.
sf api request rest "/services/data/v66.0/sandbox/licenses" --method GET
See references/api-response-shapes.md for the full response shape.
Use when: User asks "how many sandbox licenses do I have left", "what's my license usage", "can I create another Full sandbox", or before creating/refreshing a sandbox to confirm capacity exists for that licenseType.
Key response fields:
licenseType—DEVELOPER,DEVELOPER_PRO,PARTIAL,FULLlimit— Total licenses of this typeused— Currently allocatedavailable— Remaining (i.e.limit - used)
Create/Refresh blocked by a license limit: offer to pick an available type, free one up by deleting a stale sandbox, ask the admin for a license increase, or — if it's an expired Courtesy Full Copy — purchase/convert it.
Decision Guide for Agents
Resolve sandboxId first: by name, call GET /sandbox/reports and match sandboxName; by ID (07E prefix), call GET /sandbox/sandboxes/{sandboxId} directly — don't call /sandbox/reports.
| User says... | Operation | Key check |
|---|---|---|
| "Show all my sandboxes" | GET /sandbox/reports | — |
| "What's the status of X?" | GET /sandbox/reports (by name) or GET /sandbox/sandboxes/{id} (by ID) | — |
| "Activate sandbox X" | 1. Resolve sandboxId<br>2. PATCH /sandbox/activate/{sandboxId} | Must be isPendingActivation: true; confirm with user first |
| "Discard the refresh on X" | 1. Resolve sandboxId<br>2. DELETE /sandbox/discardsandbox/{sandboxId} | Must be isPendingActivation: true; confirm with user first |
| "Delete sandbox X" | 1. Resolve sandboxId<br>2. DELETE /sandbox/deletesandbox/{sandboxId} | Confirm with user first |
Common Mistakes to Avoid
| Mistake | Correct understanding |
|---|---|
| Using activate to "start" any sandbox | Activate ONLY applies completed refreshes |
| Using discard to "hide" or "soft-delete" | Discard ONLY rejects a pending refresh |
| Activating without checking status first | Always verify isPendingActivation = true |
Skipping confirmation on Create, Activate, Discard, Delete, or Refresh with AutoActivate=true | See Irreversible Actions above — always confirm first |
Offering AutoActivate during Create | Refresh-only field |
Offering ApexClassId during Refresh | Create-only field |