Skip to content

The sandbox

Every API key carries an environmentlive or sandbox — fixed at issuance. An event inherits the environment of the credential that published it, and nothing in the payload can override that: a publisher that could name its own environment could write into the live network from a test key, which is the failure the field exists to prevent.

Every local credential is sandbox, and sandbox is what to pick when you issue your first key. What that buys you, what it costs you and where the boundary bites are all on this page.

Your sandbox is not empty

This is the headline, not a footnote: a sandbox key reads the live network too. From your first request you are developing against real traffic — the ingested French national feed and every partner's live reports — with your own test events layered on top. There is nothing to populate, no fixture to load, no waiting for data.

The rule is one function with three callers (GET /v1/events, GET /v1/events/{id}, and the stream's subscription matcher — they must agree exactly, and do):

CallerIs served
live keylive events only
sandbox keylive and sandbox events

The asymmetry is the design. The closed direction — a live consumer never sees a sandbox event — is the poisoning one: our own landing page publishes fake events from an anonymous form at real coordinates on the A7 and the Paris périphérique, and without this boundary they would be indistinguishable from a partner's report. The open direction exists because a sandbox with nothing in it is one nobody can test a filter against — an integrator wiring up needs real traffic under its test events to know its filters work at all.

What the asymmetry is, precisely

A sandbox key can READ live data, so this is not confidentiality. Isolation is a write boundary: it keeps sandbox events away from live consumers and sandbox credentials away from the trust model. It does not hide live data from anyone holding any key. And a sandbox subscriber's world is not reproducible — live traffic moves under it.

One shared space, not yours

The sandbox is shared. Every sandbox consumer is served every sandbox event: other integrators' tests, and the fake events our landing-page demo publishes — at real coordinates, on real roads. Isolation is from the live network, never between sandbox users. Two consequences for your client:

  • Read environment off the event. It is on every event we serve, and it is the only thing that tells your test traffic apart from strangers' fiction — and both apart from the real incidents underneath.
  • Do not assert on what the sandbox contains. You control what you publish, not what the space holds.

The three errors at the boundary

Each verified against the running API; the responses below are real.

A sandbox key signalling a live event gets 403. Reading a live incident is the designed asymmetry; voting on one is the hole the asymmetry would otherwise open — a test key must never reach the trust model. Distinct from the 409 a dead event gets, because the answer is different: nothing about the event is wrong, and no amount of retrying changes the outcome.

{"detail": "event 0a38b381… belongs to the live network and a sandbox credential cannot signal it"}

A live key signalling a sandbox event gets 404. That event is not on its network — and a live consumer must not be able to probe for sandbox events by id, so "not found" is what that event is to it.

The same client_event_id in both environments is refused with 409. The event id is derived as uuid5(namespace, org_id + client_event_id) and the namespace is frozen forever, so it cannot take the environment as a third input. Without the refusal, an organisation that tested with trip-1 on its sandbox key and then went live with the same ids would have its live publish silently swallowed — a 200 holding the id of a sandbox event the live network never received. Prefix or namespace the ids your sandbox integration sends (test-trip-1), and going live costs nothing.

{"detail": "client_event_id 'trip-1' already identifies a sandbox event; the id is derived from it, so the same value cannot be reused across environments. Prefix or namespace the ids your sandbox integration sends."}

Traffic you control

The live traffic under your sandbox is real, which means you do not control it. For testing your consumer against events whose timing and position you chose, publish them yourself — it is a loop, not a product. Stdlib only, using the same two variables as every other page:

#!/usr/bin/env python3
"""Publish a handful of staggered test events along a road. Stdlib only.
 
Run it with a stream open on the same area and watch your own `created`
deltas arrive between the real ones.
"""
import json
import os
import time
import urllib.request
 
RELAY = os.environ["RELAY"]
KEY = os.environ["RELAY_KEY"]
 
# New idempotency keys per run — a rerun should publish, not replay.
RUN = time.strftime("%Y%m%dT%H%M%S")
 
# Five points strung south along the A7 below Lyon, ~550 m apart.
POINTS = [(45.700 - i * 0.005, 4.830) for i in range(5)]
 
for i, (lat, lon) in enumerate(POINTS):
    body = {
        "type": "object_on_road",
        "lat": lat,
        "lon": lon,
        "client_event_id": f"loop-{RUN}-{i}",
    }
    req = urllib.request.Request(
        f"{RELAY}/v1/events",
        data=json.dumps(body).encode(),
        headers={
            "Authorization": f"Bearer {KEY}",
            "Content-Type": "application/json",
        },
    )
    with urllib.request.urlopen(req) as resp:
        event = json.load(resp)
        print(resp.status, event["event_id"], event["environment"],
              "expires", event["expires_at"])
    time.sleep(2)  # staggered, so the deltas land one by one

Open the stream on the same disc first — lat=45.69&lon=4.83&radius=5000 covers all five points — then run the script and watch each created delta land. Every event it publishes carries "environment": "sandbox", expires on its type's normal TTL, and is never served to a live consumer.

What this deliberately is not: a fleet simulator with realistic vehicle behaviour. Simulated vehicles do not make a real network any more real. The sandbox's value is that the traffic under your tests is genuine; the part you need to control is the part you publish, and that is a script you own and adapt.