Aller au contenu principal

Sandboxes

Every developer project gets a sandbox: a fully isolated environment with its own database schema and its own pre-seeded restaurant. You develop and test against it with the exact same API, headers, and base URL you will use in production.

There is no separate sandbox URL

Both sandbox and production traffic go to https://api.ssppos.com/api/plugin/v1. Your pik_ key alone decides which data plane you reach — so your plugin code never needs a "am I in sandbox?" branch.

Provisioning​

From your project page, choose Provision Sandbox (requires the owner or admin role). Provisioning returns two secrets, each displayed exactly once:

ValueFormatPurpose
API keypik_ + 64 hex characters (68 total)Authenticating Plugin Data API calls
Webhook secret64 charactersHMAC-SHA256 signing, in both directions

Store both immediately. Neither can be read back.

If a project has no plugin yet, provisioning also creates a scaffold plugin stub so the sandbox has something to attach its installation to. Scaffold plugins are inert in production but fully functional inside the sandbox — see Plugin Lifecycle.

What's in the box​

Sandbox data is deterministic, so you can write tests against known state:

ResourceSeeded content
OrganizationOne synthetic organization
Locations2 — Downtown Bistro (100 Main Street) and Uptown Grill (250 Park Avenue), both Montreal, QC
Menu categories4 — Appetizers, Mains, Drinks, Desserts
Menu items13 per location — 26 rows in total (e.g. Caesar Salad $12.99, Ribeye Steak $32.99, Espresso $3.99)
Orders6 — one in each status: CREATED, COOKING, SERVED, COMPLETED, PAID, CANCELLED
Customers3 per location
Inventory3 ingredients per location, with stock levels and reorder points
Tax15%, baked into the seeded order totals only — no tax rules are seeded, so orders you create yourself come back with zero tax

The orders are ordered so the plugin-reachable starting states come first: you always have a known open order (internal CREATED) and a known in_progress order (internal COOKING) to drive through the state machine.

Credentials​

Regenerating the API key​

Regenerate Key (owner/admin) mints a new pik_ and immediately revokes the previous one. There is no overlap window — deploy the new key before or immediately after regenerating.

Rotating the webhook secret​

Rotate Secret (owner/admin) returns a new 64-character secret in plaintext, once. The old secret is invalidated immediately, so any webhook already in flight signed with it will fail verification on your side.

A safe rotation:

  1. Rotate in the portal and copy the new plaintext.
  2. Deploy it to your environment and restart.
  3. Fire a test webhook from the portal and confirm your endpoint verifies it.

Resetting​

Reset Sandbox (owner, admin, or member) wipes the schema and re-seeds it from the template. What happens:

  • All sandbox data returns to the deterministic seed above.
  • Your pik_ API key is preserved — you do not need to redeploy.
  • Stop your plugin's traffic first. A portal reset goes straight from active into the wipe without passing through the resetting status, so concurrent pik_ calls meet a half-dropped schema and fail in unhelpful ways rather than receiving a clean retry signal. (The 409 sandbox_resetting response documented under statuses is reachable through the in-Manager training-mode reset, not this one.)
  • Any outstanding SSP Manager operator sessions are invalidated; exchange a fresh token to log back in.

Lifecycle and expiry​

PropertyValue
Idle TTL90 days for developer-portal sandboxes
Warningsandbox.expiry_warning webhook fires 7 days before expiry
Reset the clockOpening the sandbox in the portal, or resetting it. Plugin Data API traffic does not count — see below
ProtectionprotectSandbox pins a protection date, capped at 12 months
After expiryThe schema is dropped; the row is kept for a 30-day grace window
Plugin Data API calls do NOT reset the idle clock

This trips people up. The idle timer lives on the sandbox, and calls authenticated with your pik_ key update the installation's usage stamp instead — they leave the sandbox's last_accessed_at untouched.

So a sandbox your integration exercises every day is still deleted 90 days after its clock last moved. What does reset it: opening the sandbox in the Developer Portal, and resetting it.

If you are relying on a sandbox long-term, set a protection date with protectSandbox (up to 12 months) rather than assuming traffic keeps it alive. Watch for the sandbox.expiry_warning webhook 7 days out.

Protection auto-expires by design — once the date passes, normal idle cleanup resumes. Pass null to clear it early.

Sandbox statuses​

StatusMeaning
provisioningBeing created
activeReady for use
resettingMid-reset — API calls return 409
pausedTemporarily inactive
expiredSchema dropped by idle cleanup
failedProvisioning failed

Only active sandboxes authenticate. Everything else returns 401 sandbox_not_ready, except resetting, which returns 409.

Testing webhooks​

The portal's webhook tester (owner/admin) fires a real webhook at your plugin's configured webhook_endpoint, built from actual sandbox data:

  1. Pick an event type (e.g. order.created).
  2. Send. You receive a log ID immediately.
  3. Poll that log ID for the delivery outcome.
StatusMeaning
receivedQueued
processingA worker picked it up
successYour endpoint answered 2xx
failedYour endpoint answered non-2xx, or the request errored
skippedNot attempted — reason in error_message (no endpoint, no secret, event not subscribed, installation inactive)

The log row is created before dispatch and updated in place across the three delivery attempts, so polling the same ID always shows the current state.

Test webhooks need real data

The tester hard-fails with no_sample_data if your sandbox has no rows for the chosen event type. Reset the sandbox or create the relevant records first.

Opening SSP Manager against your sandbox​

You can log into the real SSP Manager UI pointed at your sandbox, as that sandbox's ORGANIZATION_ADMIN. This is how you see your plugin from the restaurant's side: install it, configure it, and drive it through a real service flow.

Back-office actions do not fire webhooks in a sandbox

Organic event fan-out only reaches plugins whose listing is is_active — which happens at publish. Sandbox and scaffold plugins are is_active = false by design, so creating an order in the sandbox Manager will not deliver order.created to your endpoint.

Use the portal's webhook tester to exercise your receiver; it targets your installation directly and bypasses that filter.

Use Open SSP Manager on the portal's sandbox page. Under the hood, the portal exchanges your session for a sandbox-operator JWT:

POST /api/developer/sandbox/{sandboxId}/jwt
Authorization: Bearer ssp_dev_YOUR_KEY # or your portal session
PropertyValue
Token lifetime24 hours
Rate limit30 requests/minute
Required roleOwner or admin on the project
RequiresSandbox active; developer agreements accepted

Failure modes are deliberately indistinguishable from each other: a sandbox that doesn't exist, one you're not a member of, and one you lack the role for all return the same 404 sandbox_not_found, so sandbox IDs can't be probed across tenants.

ErrorHTTPMeaning
authentication_failed401No developer credential, or two credentials resolving to different accounts
developer_agreement_required403Outstanding legal documents — see Developer Account
sandbox_not_found404Missing, not yours, or insufficient role
sandbox_not_ready409Not active, or provisioned before operator support
operator_unavailable403The operator identity is missing or suspended (e.g. the owning key was revoked)

Sandbox vs production​

SandboxProduction
Base URLSameSame
Key formatpik_pik_
DataSynthetic, resettableReal restaurant data
Plugin listing statusAny status works, including scaffoldOnly approved or active authenticate
Rate limit60/min per installation60/min per installation
Idle expiry90 daysNone
Suspension doesn't break your sandbox

If a listing is suspended or deprecated, its production installations stop authenticating immediately — but sandbox installations are exempt, so you can keep working on the fix.

Next steps​