Sandbox Testing
Test the full incorporation flow without real payments or admin actions
The sandbox environment allows you to test the complete entity incorporation flow without processing real payments or waiting for admin actions. Use test API keys (sk_test_*) to access sandbox features.
Isolated Environment
Test data is separate from production
Simulate Payments
Mark entities as paid without real charges
Control Status
Transition through any status for testing
Test vs Live API Keys
| Key Type | Prefix | Use Case |
|---|---|---|
| Test Key | sk_test_ | Development, testing, sandbox endpoints |
| Live Key | sk_live_ | Production, real entities, actual payments |
Sandbox endpoints require test keys
The /sandbox/* endpoints will return a 403 error if called with a live API key. This prevents accidental use in production.
Complete Test Flow
Here's how to test the full incorporation lifecycle using sandbox endpoints:
Create entity
Use the standard POST /entities endpoint with a test key — returns a draft with no invoice yet
Add required data
Add parties, documents, and any required information
Issue invoice
Call POST /entities/{id}/invoice to create the incorporation invoice and get an invoiceId
Simulate payment
Call POST /sandbox/invoices/{invoiceId}/pay using the invoiceId from the invoice step
Submit for review
Call POST /entities/{id}/submit (or it auto-submits on payment if pending_submission was set)
Simulate admin actions
Use POST /entities/{id}/sandbox/set-status to transition through statuses
Test feedback loop
Set status to returned_for_changes, respond to feedback, resubmit
Complete incorporation
Set status to incorporated with company number and date
Sandbox Endpoints
Allowed Status Values
| Status | Description | Additional Fields |
|---|---|---|
submitted_for_review | Entity submitted and awaiting review | - |
in_review | Admin is reviewing the entity | - |
submitted_to_csp | Submitted to Corporate Service Provider | - |
returned_for_changes | Returned with feedback requiring response | feedback_message (optional) |
incorporated | Successfully incorporated | company_number, incorp_date |
Example: Full Test Flow
const API_KEY = 'sk_test_your_test_key';
const BASE_URL = 'https://app.entityengine.io/api/v1';
async function testIncorporationFlow() {
// 1. Create entity
const entity = await fetch(`${BASE_URL}/entities`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
jurisdiction_product_id: 'jp_abc123',
name: 'Test Company Ltd'
})
}).then(r => r.json());
const entityId = entity.data.id;
console.log('Created entity:', entityId, '— status:', entity.data.status); // "draft"
// 2. Add a director (simplified - add all required data in real tests)
await fetch(`${BASE_URL}/entities/${entityId}/parties`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
party_type: 'person',
first_name: 'John',
last_name: 'Smith',
roles: ['director', 'shareholder', 'ubo'],
date_of_birth: '1985-03-15',
nationality: 'US',
address: {
street1: '123 Test Street',
city: 'New York',
country: 'US',
postcode: '10001'
}
})
});
// 3. Issue the incorporation invoice (draft entities have no invoice until this call)
const invoiceResp = await fetch(`${BASE_URL}/entities/${entityId}/invoice`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({}) // omit payment_method — sandbox doesn't support hosted checkout
}).then(r => r.json());
const invoiceId = invoiceResp.data.invoice.id;
console.log('Invoice created:', invoiceId);
// 4. Simulate payment
const payment = await fetch(`${BASE_URL}/sandbox/invoices/${invoiceId}/pay`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}` }
}).then(r => r.json());
console.log('Payment simulated:', payment.data.payment_status);
// 5. Submit for review
const submission = await fetch(`${BASE_URL}/entities/${entityId}/submit`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}` }
}).then(r => r.json());
console.log('Submitted:', submission.data.status);
// 6. Simulate admin returning for changes
await fetch(`${BASE_URL}/entities/${entityId}/sandbox/set-status`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
status: 'returned_for_changes',
feedback_message: 'Please provide proof of address for the director.'
})
});
// 7. Get feedback and respond
const feedback = await fetch(`${BASE_URL}/entities/${entityId}/feedback`, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
}).then(r => r.json());
const feedbackId = feedback.data.feedback_items[0].id;
await fetch(`${BASE_URL}/entities/${entityId}/feedback/${feedbackId}/responses`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: 'I have uploaded the proof of address document.'
})
});
// 8. Resubmit
await fetch(`${BASE_URL}/entities/${entityId}/submit`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
// 9. Simulate incorporation
const incorporated = await fetch(`${BASE_URL}/entities/${entityId}/sandbox/set-status`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
status: 'incorporated',
company_number: 'TEST-12345678',
incorp_date: '2024-01-20'
})
}).then(r => r.json());
console.log('Incorporated!', incorporated.data);
}
testIncorporationFlow();Webhooks in Sandbox
Sandbox actions fire real webhooks to your registered endpoints. This allows you to test your webhook handlers with realistic payloads. Sandbox webhooks include a sandbox_simulated: true flag in the payload.
Test webhooks separately
Create separate webhook endpoints for test and live environments, or filter by the sandbox_simulated flag in your handler.