Skip to content

Sandbox ​

The sandbox is a test environment where you can try the whole integration on your own, without a contract and without any effect on production. You register with an invite code from ChronosHub, point the sandbox at your webhook, check payloads against the event contracts, send and trigger events, and see a readiness report that tells you what still has to work before go-live.

You can use the sandbox through the REST API (the examples below use curl) or through an MCP server, so that an AI assistant such as Claude Code can do the setup for you. Both do the same things.

Replace <host> in every example with the host of the test environment, which ChronosHub gives you together with your invite.

Events you send as a sandbox partner are validated and stored, but they are not forwarded to ChronosHub services. Outbound events you trigger are delivered to your own webhook only.

The flow at a glance ​

  1. Register with your invite code and collect your API key and webhook secret from two single-use links.
  2. Set your webhook.
  3. Subscribe to the outbound event types you want.
  4. Look at the contracts, or create your own draft contracts.
  5. Validate payloads against the contracts.
  6. Send inbound events and trigger outbound events to your webhook.
  7. Use files and download links in test payloads.
  8. Check readiness.

1. Register ​

ChronosHub gives you a one-time invite code, issued in your organisation's name. It expires, by default 14 days after it is issued.

bash
curl -X POST https://<host>/sandbox/register \
  -H "Content-Type: application/json" \
  -d '{
    "inviteCode": "<invite-code>",
    "webhookUrl": "https://partner.example.com/webhook"
  }'

webhookUrl is optional here; you can set it later. The response has status 201 Created:

json
{
  "partnerId": "0d3f2f0e-1111-2222-3333-444455556666",
  "partnerName": "sandbox-example-organisation",
  "apiKeyLink": { "url": "https://<host>/sandbox/secret#<token>", "expiresAt": "2026-11-11T13:00:00Z" },
  "webhookSecretLink": { "url": "https://<host>/sandbox/secret#<token>", "expiresAt": "2026-11-11T13:00:00Z" }
}
  • Your partner name is the organisation name on your invite, stored as a sandbox- prefixed slug.
  • Your credentials are not in the response. Open each link in a browser and click Reveal to see the secret, then store it securely. This is how ChronosHub hands over staging and production credentials too.
    • The API key goes in the X-Api-Key header on every later call.
    • The webhook secret is what ChronosHub signs your webhook deliveries with. Put it in your webhook receiver's configuration; you need it to verify webhook signatures.
  • Each link works once and expires at the expiresAt time in the response, normally 60 minutes after it was issued. Opening the page doesn't use the link up; only clicking Reveal does, and the secret is shown just that one time.
  • If a link expired or you lost it before revealing your API key, register again with the same invite code while it is still valid. You get two new links, and the old ones stop working. Once your API key has been revealed, the invite code can't be used again. A webhookUrl sent with the repeated registration replaces the one you set before.
  • To replace a secret later, ask for a new link with your API key: POST /sandbox/me/api-key/rotate or POST /sandbox/me/webhook-secret/rotate. Both return { "url": "...", "expiresAt": "..." }. Your current key or secret keeps working until you click Reveal on the new link, and is replaced at that moment.
  • These endpoints need a working API key. If you revealed your API key and then lost it, you can't replace it yourself: ask your ChronosHub contact for a new key.

Registration is limited per client address (see Limits). A wrong, used or expired code returns Invite code is invalid or expired, and a used, expired or replaced link returns Link is invalid or has expired; see Errors.

2. Set your webhook ​

bash
curl -X PUT https://<host>/sandbox/me/webhook \
  -H "X-Api-Key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{ "webhookUrl": "https://partner.example.com/webhook" }'

Deliveries are signed with the webhook secret from your webhook secret link. To replace the secret, use POST /sandbox/me/webhook-secret/rotate (see Register).

For sandbox partners the webhook has extra rules, because the sandbox is open to outside developers. These rules apply to sandbox partners only.

  • The URL must be an absolute https URL.
  • Hosts that resolve to private, loopback, link-local, CGNAT, unique-local or multicast addresses are rejected when you set the webhook, and fail at delivery. To receive webhooks on your development machine, use a public HTTPS tunnel.
  • Only 307 and 308 redirects are followed, at most 5 hops, and only to HTTPS locations. Every hop is checked. A 301, 302 or 303 response fails the delivery.

Check what is stored (the secret itself is never shown again):

bash
curl https://<host>/sandbox/me -H "X-Api-Key: <api-key>"

How to receive and verify the requests is described in Receiving Webhooks.

3. Subscribe to event types ​

bash
curl -X PUT https://<host>/sandbox/me/subscriptions \
  -H "X-Api-Key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{ "eventTypes": ["manuscript.uploaded"] }'

This replaces your whole list. An empty list unsubscribes you from everything.

4. Event contracts ​

An event contract is the JSON Schema a payload must match.

Sandbox contracts are for testing only. The contracts you use and the drafts you create here let you test your flow. The contracts for staging and production are agreed together by you and ChronosHub; your drafts are a good starting point for that conversation, not the final version.

List what is available to you:

bash
curl "https://<host>/sandbox/event-contracts?direction=Outbound" -H "X-Api-Key: <api-key>"

Each entry has id, eventType, direction (Inbound or Outbound), version, status and isOwned. Fetch one with GET /sandbox/event-contracts/{id}. The shared contracts are read-only. The payload of each is described on its event page, for example Manuscript Uploaded.

If the event you need is not there yet, create a draft contract of your own to test with. The contract is the JSON Schema, passed as a string. An inbound contract must set "additionalProperties": false at its root, so that ChronosHub only accepts fields the contract names:

bash
curl -X POST https://<host>/sandbox/event-contracts \
  -H "X-Api-Key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "eventType": "invoice.paid",
    "direction": "Outbound",
    "contract": "{\"type\":\"object\",\"required\":[\"invoiceId\"],\"properties\":{\"invoiceId\":{\"type\":\"string\"}}}",
    "changeSummary": "First draft"
  }'

The response is 201 Created with a Location header pointing to the new contract. Change a draft with PUT /sandbox/event-contracts/{id} and the body { "contract": "...", "changeSummary": "..." }. Your drafts are visible to you only. Events that match your own draft count towards readiness.

5. Validate a payload ​

Check a payload against the applicable contract without sending anything:

bash
curl -X POST https://<host>/sandbox/validate \
  -H "X-Api-Key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "eventType": "manuscript.uploaded",
    "direction": "Outbound",
    "payload": { "fileUrl": "https://example.invalid/file.pdf", "correlationId": "check-123" }
  }'

The report has isValid, matchedContractId, matchedVersion, matchedOwnedDraft and a list of errors. The payload must be a JSON object.

6. Send and trigger events ​

Inbound (you send to ChronosHub). Use the normal endpoint, described in Inbound Events. It returns 202 Accepted with an eventId. In the sandbox the event is validated and stored, and not forwarded.

bash
curl -X POST https://<host>/inbound/manuscript.status.updated \
  -H "X-Api-Key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{ "manuscriptId": "MS-2026-001", "status": "accepted" }'

Outbound (ChronosHub sends to you). Make ChronosHub deliver an event to your webhook, so you can test your receiver. This needs a webhook and a secret, and an outbound contract for the event type. The payload must be a JSON object and is validated against the contract first; a payload that does not match is stored as a rejected event and answered with 400.

bash
curl -X POST https://<host>/sandbox/outbound-events \
  -H "X-Api-Key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{ "eventType": "manuscript.uploaded", "payload": { "fileUrl": "<download-url>", "correlationId": "check-123" } }'

The response is 202 Accepted with the event id and eventId. Delivery happens in the background, with the same retries as real deliveries.

Follow what happened. The list requests take the optional filters eventType, status (Pending, Processing, Processed, Failed or Rejected), from, to, page and pageSize (at most 100).

RequestPurpose
GET /sandbox/inbound-events and GET /sandbox/inbound-events/{id}Events you sent.
GET /sandbox/outbound-events and GET /sandbox/outbound-events/{id}Events ChronosHub sent to your webhook, with delivery status.
POST /sandbox/outbound-events/replaySend failed outbound events again. Body: { "from": "...", "to": "..." } (ISO 8601; the window has a maximum length).

Event payloads such as Manuscript Uploaded carry a link to a file. The sandbox gives you sample files from ChronosHub and lets you upload your own.

bash
# List the sample files and your own files
curl https://<host>/sandbox/files -H "X-Api-Key: <api-key>"

# Upload a file (multipart/form-data, field name "file")
curl -X POST https://<host>/sandbox/files \
  -H "X-Api-Key: <api-key>" \
  -F "file=@manuscript.pdf"

# Create a download link for a file
curl -X POST https://<host>/sandbox/files/<file-id>/link -H "X-Api-Key: <api-key>"

The link response has a url and expiresAt. Use the url exactly as returned, because the signature in it is checked. Links last 2 hours. After that, create a new one. A link is a plain GET and needs no X-Api-Key header, so treat it like a secret while it is valid. Uploaded files are kept for 30 days by default.

8. Check readiness ​

bash
curl https://<host>/sandbox/readiness -H "X-Api-Key: <api-key>"

The report has isReady and, for each event type and direction, an item with the contract and version used, successCount, lastSuccessAt, failedOrRejectedSinceLastSuccess and a status of NotStarted, InProgress (the latest event is still waiting to be processed or delivered), Failing or Passing. You are ready when every direction you have tested has worked at least once: if you triggered outbound events, at least one was delivered to your webhook, and if you sent inbound events, at least one was accepted and processed. Events checked against your own draft contracts count, because the final schemas are agreed with ChronosHub before staging and production anyway. Later failures and subscribed event types you never triggered don't make you not ready, but the items still show them.

Use the sandbox from an AI assistant (MCP) ​

The sandbox also runs an MCP server at https://<host>/mcp. With it, an AI assistant can register you, configure the webhook, validate payloads and read these docs by itself. The server serves these documentation pages through the list_docs and read_doc tools, which need no API key, so you can ask the assistant about limits, errors or payloads at any time. The same pages are also published as resources (docs://guide/..., docs://outbound-schemas/...). To start, ask the assistant to onboard you, or run the server's onboard_me prompt. The assistant first checks that you know the basics (inbound and outbound events, webhooks and webhook signatures) and explains any you don't. Then it registers you and works out with you whether your flow is outbound (ChronosHub sends you events), inbound (you send ChronosHub events) or both. Only those directions are covered from then on: choose existing event contracts or draft your own, set up your webhook if you receive events, then test for real by receiving an outbound event, sending an inbound event, or both (outbound first). For inbound, the assistant can first send a test event for you, as a quick check of your contract, and then help you send one from your own system, which is what proves the integration. Verifying webhook signatures is up to your receiver; the assistant explains how but doesn't do it for you. You end up with a working test integration. Moving to staging and production then mostly means agreeing the final event schemas with ChronosHub and repeating the same setup with new credentials.

Registration needs no key, so connect first without one and start onboarding. When it registers you, the assistant gives you the two single-use links; open them in your browser yourself (the assistant never opens them). Keep the webhook secret out of the chat: it only belongs in your webhook receiver. For the API key, choose one of two ways to add it to your MCP client configuration:

  • Add it yourself. The assistant shows you where it goes for your setup (the examples below), so the key never passes through the chat.
  • Paste it in the chat and let the assistant add it. The key then stays in your chat history, which is acceptable for the sandbox; you can replace the key with request_api_key_link while it still works.

You then reconnect the server once, and the assistant continues where it left off. The configuration file now contains your API key, so don't commit it to source control.

Claude Code ​

Add the server to your project, so that it is stored in .mcp.json in the project folder:

bash
claude mcp add --transport http --scope project chronoshub-sandbox https://<host>/mcp

Start Claude Code, approve the server when asked, and type "onboard me" (or run the prompt with /mcp__chronoshub-sandbox__onboard_me). After registration, add the key to .mcp.json, or paste it in the chat and let the assistant add it. Then reconnect: run /mcp, select chronoshub-sandbox and choose Reconnect.

To add the key yourself, put it in .mcp.json:

json
{
  "mcpServers": {
    "chronoshub-sandbox": {
      "type": "http",
      "url": "https://<host>/mcp",
      "headers": { "X-Api-Key": "<api-key>" }
    }
  }
}

Or, for a server added without --scope project, replace it from a terminal:

bash
claude mcp remove chronoshub-sandbox
claude mcp add --transport http chronoshub-sandbox https://<host>/mcp --header "X-Api-Key: <api-key>"

Cursor ​

Add the server to .cursor/mcp.json (or the global ~/.cursor/mcp.json):

json
{
  "mcpServers": {
    "chronoshub-sandbox": {
      "url": "https://<host>/mcp",
      "headers": { "X-Api-Key": "<api-key>" }
    }
  }
}

Leave out headers until you have registered. The assistant can add them for you afterwards.

Claude Desktop ​

Claude Desktop starts local servers from its config file, so use the mcp-remote bridge (needs Node.js). In claude_desktop_config.json:

json
{
  "mcpServers": {
    "chronoshub-sandbox": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://<host>/mcp", "--header", "X-Api-Key:${CHRONOSHUB_API_KEY}"],
      "env": { "CHRONOSHUB_API_KEY": "<api-key>" }
    }
  }
}

Restart Claude Desktop after changing the file.

Tools ​

ToolWhat it does
register_sandbox_partnerRegisters you with an invite code and returns the single-use links for your API key and webhook secret. Works without a key.
request_api_key_linkReturns a single-use link to a new API key; the current key works until you reveal it.
request_webhook_secret_linkReturns a single-use link to a new webhook secret; the current secret is used until you reveal it.
get_my_partnerShows your partner and its webhook URL.
update_webhookSets the webhook URL.
set_subscriptionsReplaces your outbound event subscriptions.
list_event_contractsLists contracts, optionally filtered by Inbound or Outbound.
get_event_contractReturns one contract with its schema.
create_draft_contractCreates your own draft contract, for testing.
update_draft_contractChanges one of your draft contracts.
validate_payloadChecks a payload against a contract without sending it.
send_inbound_eventSends an inbound event, like POST /inbound/{eventType}.
trigger_outbound_eventMakes ChronosHub deliver an outbound event to your webhook.
list_eventsLists inbound or outbound events, with filters.
get_eventShows one event and its delivery status.
replay_failed_outboundSends failed outbound events again for a time window.
list_filesLists sample files and your uploads.
upload_fileUploads a small file (up to 10 MB, base64) for use in test events.
create_file_linkCreates a download link for a file; it lasts 2 hours.
get_readinessReturns the readiness report.

Tool errors are written in plain language; the Errors page explains each one.

Limits ​

These are the current defaults. The test environment can be configured differently.

WhatLimit
Requests per sandbox partner to /sandbox/* and the MCP server, together60 per minute
Inbound events per sandbox partner (POST /inbound/{eventType})60 per minute
Registrations per client address10 per hour
File uploads per partner20 per hour
Download requests per client address120 per minute
Draft contracts you can own50
Stored files per partner10
Upload size20 MB with POST /sandbox/files, 10 MB with the MCP upload_file tool
Allowed file types.pdf, .docx, .doc, .tex, .zip, .xlsx
Download link lifetime2 hours
Uploaded file retention30 days

Going over a rate limit returns 429 Too Many Requests with a Retry-After header giving the seconds to wait. See Errors.