Sandbox

Sandbox lets you test the entire scan lifecycle — creation, status transitions, webhooks, and result retrieval — without consuming credits or calling the solver. Use a sandbox API key and every scan automatically runs in sandbox.


How it works

When you create a scan with a sandbox API key (prefixed zs_test_), the API:

  1. Validates your input — the same validation rules as production apply. Malformed DTOs, missing required fields, and invalid combinations are rejected with real error messages.
  2. Discards the validated payload — after validation passes, the constructed solver payload is thrown away.
  3. Selects a pre-computed fixture — from a library of 20 fixture scenarios, the API picks the one whose traits best match your input (see Fixture selection).
  4. Pre-stages the result — the fixture's payload and result are saved immediately, so the result is ready before status transitions even begin.
  5. Simulates status transitions — the scan progresses through pending → processing → completed on a fixed timeline (see Status simulation).

The response shape, webhook events, and scan lifecycle are identical to production. The only difference is the result data comes from a fixture rather than the solver.


Zero cost

Sandbox scans are completely free:

  • They don't consume credits — the cost field is null
  • They don't count towards your tier's scan limit — monthly usage meters exclude sandbox scans entirely
  • They don't trigger metered billing on Stripe
  • They bypass all limit checks — no overage flags, no hard caps

This makes sandbox ideal for integration testing, CI pipelines, and development workflows.


Studio scans not supported

Sandbox does not support studio scans. If your request includes customProfileId or sets inputMethod to "studio", the API will reject it with a 400 Bad Request error:

{
  "statusCode": 400,
  "message": "Sandbox does not support studio scans. Remove customProfileId and set inputMethod to 'known'."
}

Use inputMethod: "known" with yearlyKwh and loadProfile instead. This covers all hardware combinations (existing PV, new PV, battery) and both optimization goals.


Fixture selection

Instead of running the solver, sandbox selects a pre-computed fixture that best matches your input. The selection is based on four traits extracted from your CreateScan request:

  • Name
    hasExistingPv
    Type
    boolean
    Description

    Whether existingPv contains at least one entry.

  • Name
    hasNewPv
    Type
    boolean
    Description

    Whether newPv contains at least one entry.

  • Name
    hasBattery
    Type
    boolean
    Description

    Whether electricBattery is provided.

  • Name
    goal
    Type
    string
    Description

    The optimization goal: "cost-savings" or "self-sufficiency".

Each fixture is scored against your input: +1 for every matching trait, -1 for every mismatch. The highest-scoring fixture is selected.

Available fixtures

FixtureExisting PVNew PVBatteryGoal
Basic — cost savingscost-savings
Basic — self-sufficiencyself-sufficiency
Existing PVcost-savings
Existing PVself-sufficiency
Existing PV + exportcost-savings
New PVcost-savings
New PVself-sufficiency
Existing + New PVcost-savings
Existing + New PVself-sufficiency
Battery onlycost-savings
Battery onlyself-sufficiency
Existing PV + Batterycost-savings
Existing PV + Batteryself-sufficiency
New PV + Batterycost-savings
New PV + Batteryself-sufficiency
New PV + Fixed Batterycost-savings
New PV + Fixed Batteryself-sufficiency
Existing + New PV + Batterycost-savings
Existing + New PV + Batteryself-sufficiency
Existing + New PV + Battery + exportcost-savings

Status simulation

Sandbox scans progress through the same statuses as production scans, on a fixed timeline:

Elapsed timeStatusWebhook event
0 – 5 secondspendingscan.pending (fired on creation)
5 – 10 secondsprocessingscan.processing
10+ secondscompletedscan.completed

The status syncer checks every 2 seconds. After approximately 10 seconds your scan will be marked completed and the pre-staged result becomes available via GET /v1/scans/:id/result.


Webhooks require sandbox endpoints

Sandbox scans only dispatch events to sandbox webhook endpoints. Production webhook endpoints will not receive events from sandbox scans, and vice versa.

To receive webhook events during testing:

  1. Register a webhook endpoint in the dashboard under Organization → API
  2. Make sure it's set to sandbox (not live)
  3. Subscribe to the events you want (scan.pending, scan.processing, scan.completed)

The event payload, signing, and delivery behavior are identical to production. See [[Webhooks]] for full details on payload format and signature verification.


Identifying sandbox scans

Sandbox scans are scoped to the API key mode used to create them. When you list scans via GET /v1/scans with a sandbox API key, you only see sandbox scans. With a live key, you only see production scans.

From a behavioral perspective, sandbox scans are distinguishable by:

  • Completing in ~10 seconds — much faster than production scans
  • No credits consumed — your usage meters and billing are unaffected
  • Fixture-based results — the result data comes from pre-computed scenarios, not the solver

What's identical to production

Everything except the solver and billing:

  • Input validation (real 400 errors for bad input)
  • Webhook event flow (pending → processing → completed)
  • Webhook signing and delivery
  • API response shape and fields
  • Scan lifecycle (create → poll/webhook → retrieve result)
  • Access control and project scoping
  • Result retrieval via GET /v1/scans/:id/result

Was this page helpful?