Sandbox — a throwaway account for testing
One unauthenticated call gives you a complete, bookable business with its own keys. Point the widget, the public API, the management API or the MCP server at it, make real bookings, read the mails that would have been sent, pay with a fake checkout, and throw it away. Nothing leaves the sandbox: no mail is delivered and no money moves.
curl -s -X POST https://app.nisebookings.com/api/sandbox
{
"tenantId": "6f1c…",
"name": "Sandbox 3fa2",
"publicKey": "9c2e41…",
"apiKey": "nk_…",
"scopes": ["catalog:read", "catalog:write", "settings:read", "settings:write", "customers:read",
"customers:write", "bookings:read", "bookings:write", "payments:read", "payments:write"],
"expiresAt": "2026-10-13T09:12:44.000Z",
"endpoints": {
"publicApi": "https://app.nisebookings.com/api/public/9c2e41…",
"managementApi": "https://app.nisebookings.com/api/v1",
"mcp": "https://app.nisebookings.com/api/mcp",
"widget": "https://app.nisebookings.com/widget.js"
}
}
The apiKey is a normal management-API key with every scope. It is shown once — keep the
response. The publicKey is the sandbox account's public key, exactly as on a real account.
What is in it
| Business | Sandbox <4 hex>, time zone Europe/Amsterdam, EUR, default mail templates, gateways local and mollie both enabled |
| Staff | Anna and Ben, Monday–Saturday 09:00–17:00 (opening hours the same) |
| Services (category "Demo") | Knippen 30 min €25 (Anna and Ben) · Kleuren 90 min €65 with a €20 deposit rule (Anna; whether it applies follows the deposit mode in the settings) · Groepsles group class, 6 seats, 60 min €15 (Ben) |
| Events | one event a week ahead with one ticket type |
The data is fictional. Change anything you like through the management API or MCP — it is your account until it expires. There is no admin login for a sandbox: use the API, MCP and the widget.
Limits
| Limit | Value |
|---|---|
| Lifetime | 24 hours from creation (expiresAt), then the account and everything in it is deleted |
| Creation rate | 5 sandboxes per hour per client address (the rightmost x-forwarded-for hop our edge appends, else x-real-ip) → 429 {"error":{"code":"rate_limited"}} with Retry-After |
| Capacity | a global cap on live sandboxes → 429 {"error":{"code":"capacity"}} with Retry-After |
never delivered; every mail is recorded with status suppressed (see below) |
|
| Payments | never real; the Mollie gateway goes to a fake checkout page (see below) |
Connect
eval "$(curl -s -X POST https://app.nisebookings.com/api/sandbox |
jq -r '"export PK=\(.publicKey) NISE_API_KEY=\(.apiKey)"')"
Widget — embed it on any page (a sandbox has no allowed-sites list, so every origin works):
<script src="https://app.nisebookings.com/widget.js" defer></script>
<nise-booking data-key="9c2e41…"></nise-booking>
Public API — examples/headless.mjs runs against it unchanged:
KEY=$PK node headless.mjs
Management API — Authorization: Bearer $NISE_API_KEY on /api/v1:
curl -s -H "Authorization: Bearer $NISE_API_KEY" https://app.nisebookings.com/api/v1/me
MCP — connect your coding agent to the hosted MCP server with the same key:
claude mcp add --transport http nise-sandbox https://app.nisebookings.com/api/mcp \
--header "Authorization: Bearer $NISE_API_KEY"
Verify mails: the outbox
Every mail the sandbox would have sent — confirmations, staff notices, cancellations — is kept with
status suppressed. Read them with GET /api/v1/outbox (or the list_outbox MCP tool), newest first:
curl -s -H "Authorization: Bearer $NISE_API_KEY" \
"https://app.nisebookings.com/api/v1/outbox?to=ada@example.com"
{
"data": [
{ "id": "…", "status": "suppressed", "toEmail": "ada@example.com", "toName": "Ada Jansen",
"subject": "Je afspraak is bevestigd", "text": "…", "html": "…",
"createdAt": "2026-10-12T09:14:02.000Z", "sentAt": null, "lastError": null }
],
"total": 1
}
Filters: status (pending, sending, sent, failed, suppressed), to (exact address,
case-insensitive); pagination limit (≤ 100) and offset. Mails are written by the background
worker, so a row can appear a minute after the booking that caused it; poll rather than assert once.
The endpoint exists on real accounts too (scope settings:read), where it shows what was actually sent.
Fake payments
Book with gateway: "mollie" and a returnUrl exactly as on a real account. The response's
payment.checkoutUrl points to a nise test page, https://app.nisebookings.com/sandbox/pay/<token>,
instead of Mollie. It shows the amount and description with two buttons:
- Betalen — the payment becomes
completed, the bookings are confirmed and the confirmation mail is recorded. - Mislukt — the payment becomes
rejectedand so do its bookings, as with a failed Mollie payment.
Both redirect to your returnUrl with ?nb_pay=<token>, so your return page and its
GET /api/public/<key>/payments/<token> polling work exactly as in production. Refunds
(POST /api/v1/payments/{id}/refund, scope payments:write) succeed locally without contacting Mollie.
Delete it
A sandbox deletes itself after 24 hours. To delete it now, with its own key:
curl -s -X DELETE -H "Authorization: Bearer $NISE_API_KEY" https://app.nisebookings.com/api/v1/sandbox
204 on success; the keys stop working immediately (401). On a real account this endpoint answers
404 — it can never delete a business.