Sandbox Lifecycle Management
SkillDev toolsLets your agent look up Salesforce sandbox details like status, license type, and activation state.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Sandbox Lifecycle Management skill
About this skill
MUST USE this skill for ANY sandbox request, including simply getting a sandbox's details, status, license type, or pending-activation state by name or ID. TRIGGER when the user: types \"sandbox -help\"/\"sandbox help\"; mentions a sandbox ID (07E prefix); asks to list or show sandboxes; asks for t
What this skill tells your AI
The instructions your AI receives, as published by forcedotcom/sf-skills in skills/platform-sandbox-configure/SKILL.md and read by ahel’s review.
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 sandboxId2. PATCH /sandbox/activate/{sandboxId} | Must be isPendingActivation: true; confirm with user first |
| "Discard the refresh on X" | 1. Resolve sandboxId2. DELETE /sandbox/discardsandbox/{sandboxId} | Must be isPendingActivation: true; confirm with user first |
| "Delete sandbox X" | 1. Resolve sandboxId2. 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 |
Signals
- GitHub stars
- 1k
- Forks
- 348
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
platform-sandbox-configure- Source
- github.com/forcedotcom/sf-skills