Contract
- shape
- entity
- authority
- act
- evidence
- none
- subscription
- false
- permission
- any one of: staff.provision (the staff role staff_provision held in the Internal org, tradehq_role.staff_role); a deactivated caller is refused first
- idempotency
- keyed
- emits
- sandbox.converted, account.created
- MCP tool
- convert_sandbox
- decision_set
- approve, reject, edit, request_info
- REST
- POST https://api.containertradehq.com/v1/cap/convert_sandbox
Through MCP this call returns an approval request first; see Approvals.
Idempotency: keyed by the caller's Idempotency-Key, which is required: the same key and inputs replay the first result, the same key with other inputs is 409 idempotency_key_reused
Inputs
| name | type | required | validation |
|---|---|---|---|
| org_id | org_id | required | tradehq-org-id; 1 to 20 characters The sandbox: its org, or its test twin, answered the same. |
| account_id | account_id | optional | tradehq-account-id An existing account the customer org joins. Send this or account_name, not both. |
| account_name | text | optional | 1 to 200 characters A new account to make for the customer: the commercial relationship's name. Send this or account_id, not both. |
| org_name | text | required | 1 to 120 characters The customer org's name, the legal entity's. Its test twin becomes "{org_name} (test)". |
| admin_email | text | required | 3 to 254 characters The first admin's email address. They are invited, and sign in with it by magic link or passkey. |
| admin_name | text | optional | 1 to 200 characters The first admin's name, for the welcome mail. |
| plan | one_of | optional | one of base The plan the customer org is on: base (the one tier today, and the default). |
Response
org_id· string · The customer org: the sandbox's own id, unchanged, now of type customer.twin_org_id· string · Its test twin, which kept the sandbox's data and test key.account_id· string · The account the customer org is in.account_created· boolean · Whether this call made the account.provisioning_id· string · The onboarding's ledger row, which get_onboarding_status reads.plan· string · The plan the customer org is on.key_id· string · The sandbox's test API key, kept, which no longer expires. Never the secret.converted_at· string · When the sandbox was converted.
Full JSON Schema
{
"type": "object",
"properties": {
"org_id": {
"type": "string",
"minLength": 1,
"maxLength": 20,
"pattern": "^[A-Za-z0-9_-]{1,20}$",
"format": "tradehq-org-id",
"description": "The customer org: the sandbox's own id, unchanged, now of type customer.",
"x-data-class": "operational"
},
"twin_org_id": {
"type": "string",
"minLength": 1,
"maxLength": 20,
"pattern": "^[A-Za-z0-9_-]{1,20}$",
"format": "tradehq-org-id",
"description": "Its test twin, which kept the sandbox's data and test key.",
"x-data-class": "operational"
},
"account_id": {
"type": "string",
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
"format": "tradehq-account-id",
"description": "The account the customer org is in.",
"x-data-class": "operational"
},
"account_created": {
"type": "boolean",
"description": "Whether this call made the account.",
"x-data-class": "operational"
},
"provisioning_id": {
"type": "string",
"pattern": "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$",
"format": "uuid",
"description": "The onboarding's ledger row, which get_onboarding_status reads.",
"x-data-class": "operational"
},
"plan": {
"type": "string",
"enum": [
"base"
],
"description": "The plan the customer org is on.",
"x-data-class": "operational"
},
"key_id": {
"type": "string",
"description": "The sandbox's test API key, kept, which no longer expires. Never the secret.",
"x-data-class": "operational"
},
"converted_at": {
"type": "string",
"format": "date-time",
"description": "When the sandbox was converted.",
"x-data-class": "operational"
}
},
"required": [
"org_id",
"twin_org_id",
"account_id",
"account_created",
"provisioning_id",
"plan",
"key_id",
"converted_at"
],
"additionalProperties": false
} Refusals
not_a_sandbox· 409 · the org is not a public sandbox, so there is nothing to convertsandbox_expired· 409 · the sandbox is past its expiry, or purged, and is being deletedsandbox_already_converted· 409 · the sandbox was already converted, under another key; nothing was changedaccount_choice· 422 · the call names both an existing account and a new one, or neither, or an account that does not existadmin_email_invalid· 422 · the first admin's address is not an email address a person signs in withidempotency_key_reused· 409 · this Idempotency-Key already converted a sandbox with other inputsadmin_already_provisioned· 409 · the existing account already has a provisioning for that admin emailretry_key_mismatch· 409 · the Idempotency-Key was already used with a different requestretry_key_in_flight· 409 · a request with this Idempotency-Key is still runningretry_key_lost· 409 · another request took over this Idempotency-Key first; nothing was applied
The platform refusals apply to every call: version, authentication, authority, validation and rate limits, each in the same envelope.
Surfaces
- Mobile: degraded: composition
- MCP: approval required
- Voice: degraded: composition
- OneBox: no document originates a sandbox
- Outbound: crosses to no counterparty
Events
sandbox.convertedaudit_onlyaccount.createdaudit_only