Create a test-only sandbox account for a person
curl --request POST \
--url https://api.gigstack.io/v2/auth/sandbox \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"email": "persona@empresa.mx",
"name": "Persona Ejemplo",
"agent": {
"client": "claude-code"
}
}
'const options = {
method: 'POST',
headers: {'Idempotency-Key': '<idempotency-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: 'persona@empresa.mx',
name: 'Persona Ejemplo',
agent: {client: 'claude-code'}
})
};
fetch('https://api.gigstack.io/v2/auth/sandbox', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.gigstack.io/v2/auth/sandbox"
payload = {
"email": "persona@empresa.mx",
"name": "Persona Ejemplo",
"agent": { "client": "claude-code" }
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"success": true,
"data": {
"team_id": "team_a1b2c3d4e5f6",
"billing_account_id": "ba_a1b2c3d4e5f6g7h",
"livemode": false,
"api_key": {
"test": null
},
"credential_delivery": "not_replayed",
"claim": {
"sent_to": "persona@empresa.mx",
"expires_at": 1767830400000
}
},
"message": "Sandbox already created; the test key is not returned again",
"timestamp": 1767225600000
}{
"success": true,
"data": {
"team_id": "team_a1b2c3d4e5f6",
"billing_account_id": "ba_a1b2c3d4e5f6g7h",
"livemode": false,
"api_key": {
"test": "eyJhbGciOiJIUzI1NiJ9.example.signature"
},
"credential_delivery": "first_response",
"claim": {
"sent_to": "persona@empresa.mx",
"expires_at": 1767830400000,
"delivery": "sent"
}
},
"message": "Sandbox created; the test key is returned only in this response",
"timestamp": 1767225600000
}{
"success": false,
"error": {
"code": "email_not_allowed",
"message": "Use a permanent email address the person reads; disposable inboxes are not accepted"
},
"timestamp": 1767225600000
}{
"success": false,
"error": {
"code": "not_found",
"message": "Not found"
},
"timestamp": 1767225600000
}{
"success": false,
"error": {
"code": "sandbox_pending_claim",
"message": "This email has an unclaimed sandbox; a new claim code was sent to it"
},
"data": {
"team_id": "team_a1b2c3d4e5f6",
"claim": {
"sent_to": "persona@empresa.mx",
"expires_at": 1767830400000,
"delivery": "sent"
}
},
"timestamp": 1767225600000
}{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Too many sandbox requests. Retry in 1800 seconds."
},
"timestamp": 1767225600000
}{
"success": false,
"error": {
"code": "sandbox_state_unavailable",
"message": "Retry the same request later"
},
"timestamp": 1767225600000
}Auth
Create a test-only sandbox account for a person
Lets an agent create a gigstack account for a person without a partner secret. The account
is a pricing v3 account with one test API key (sk_test_, livemode: false), so it
cannot stamp CFDI before the SAT or charge anything. Going live needs a person: they claim the
sandbox with the code emailed to them, set a password and go live from app.gigstack.pro.
- Off by default. The route answers
404 not_founduntil the operator enables it. - Idempotent.
Idempotency-Keyis required. Replaying it with the same body returns the sameteam_idandbilling_account_idwith200, without new effects; the test key is returned only in the first201response (api_key.test: null,credential_delivery: not_replayedon replay). The same key with a different body is409 idempotency_conflict. - One open sandbox per email. If the email already has an unclaimed sandbox, a fresh claim
code is sent to it and the answer is
409 sandbox_pending_claimwith theteam_idand claim details, never keys. Any other existing account is409 email_already_exists. - Abuse controls. Per caller IP (3 an hour, 10 a day), per company email domain (50 a
day; large consumer mailbox providers are exempt), up to 3 claim codes an hour per email,
and disposable inbox domains are refused with
400 email_not_allowed. - Test data only until claimed. The test key can use clients, services, payments,
receipts, invoices, catalogs, SAT lists, retentions and
GET /teams/{id}. Everything else (users, login links, API keys, team members, billing, go-live, portals, webhooks) answers403 sandbox_unclaimeduntil the person claims the sandbox. - An unclaimed sandbox expires 7 days after creation: its keys are revoked and, when the person has no other gigstack account, the login is removed so the email can be used again. Team data is never deleted.
The claim code is never returned or logged. Hand team_id to the person, or ask them for the
code and call POST /auth/sandbox/claim.
POST
/
auth
/
sandbox
Create a test-only sandbox account for a person
curl --request POST \
--url https://api.gigstack.io/v2/auth/sandbox \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"email": "persona@empresa.mx",
"name": "Persona Ejemplo",
"agent": {
"client": "claude-code"
}
}
'const options = {
method: 'POST',
headers: {'Idempotency-Key': '<idempotency-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: 'persona@empresa.mx',
name: 'Persona Ejemplo',
agent: {client: 'claude-code'}
})
};
fetch('https://api.gigstack.io/v2/auth/sandbox', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.gigstack.io/v2/auth/sandbox"
payload = {
"email": "persona@empresa.mx",
"name": "Persona Ejemplo",
"agent": { "client": "claude-code" }
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"success": true,
"data": {
"team_id": "team_a1b2c3d4e5f6",
"billing_account_id": "ba_a1b2c3d4e5f6g7h",
"livemode": false,
"api_key": {
"test": null
},
"credential_delivery": "not_replayed",
"claim": {
"sent_to": "persona@empresa.mx",
"expires_at": 1767830400000
}
},
"message": "Sandbox already created; the test key is not returned again",
"timestamp": 1767225600000
}{
"success": true,
"data": {
"team_id": "team_a1b2c3d4e5f6",
"billing_account_id": "ba_a1b2c3d4e5f6g7h",
"livemode": false,
"api_key": {
"test": "eyJhbGciOiJIUzI1NiJ9.example.signature"
},
"credential_delivery": "first_response",
"claim": {
"sent_to": "persona@empresa.mx",
"expires_at": 1767830400000,
"delivery": "sent"
}
},
"message": "Sandbox created; the test key is returned only in this response",
"timestamp": 1767225600000
}{
"success": false,
"error": {
"code": "email_not_allowed",
"message": "Use a permanent email address the person reads; disposable inboxes are not accepted"
},
"timestamp": 1767225600000
}{
"success": false,
"error": {
"code": "not_found",
"message": "Not found"
},
"timestamp": 1767225600000
}{
"success": false,
"error": {
"code": "sandbox_pending_claim",
"message": "This email has an unclaimed sandbox; a new claim code was sent to it"
},
"data": {
"team_id": "team_a1b2c3d4e5f6",
"claim": {
"sent_to": "persona@empresa.mx",
"expires_at": 1767830400000,
"delivery": "sent"
}
},
"timestamp": 1767225600000
}{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Too many sandbox requests. Retry in 1800 seconds."
},
"timestamp": 1767225600000
}{
"success": false,
"error": {
"code": "sandbox_state_unavailable",
"message": "Retry the same request later"
},
"timestamp": 1767225600000
}Headers
Your identifier for this request, 8-128 characters of A-Z a-z 0-9 . _ : -.
Required string length:
8 - 128Pattern:
^[A-Za-z0-9._:-]{8,128}$Body
application/json