Skip to main content

ring-sandbox

Typed Python client and offline emulator for the Ring Partner API (api.amazonvision.com).

Ring ships no SDK and no local simulator. Testing a partner integration today means a real device, a 30-minute Playground token, or hand-rolled mocks. ring-sandbox gives you:

  • RingClient – a small, typed, synchronous client over httpx covering users, device discovery (with ?include= side-loading), status, capabilities, configurations, location, event history (auto-pagination), image snapshots (303 redirect flow), media clips (200/206/416 semantics), and chime playback.
  • Webhook helpers – HMAC-SHA256 X-Signature signing/verification over raw bytes, v1.1 payload construction, and parsing into a WebhookEvent.
  • Emulator – a FastAPI app that speaks the same JSON:API shapes at /v1/..., plus a /_sandbox control plane to inject events, register webhook targets (the emulator signs and delivers them), add devices (doorbells, cameras, chimes, Early Access sensors), and reset.
  • Scenarios – scripted event sequences (delivery, home_aide_visit, short_visit, no_show, device_flap, or your own YAML), replayable in real time, time-compressed, or back-dated into history.
  • pytest pluginring_client, ring_control, ring_world fixtures that run the emulator in-process with no sockets.
  • Recorderring-sandbox record --token ... snapshots real Playground/production responses into JSON fixtures.

Install

pip install "ring-sandbox[server]"      # client + emulator + CLI
pip install ring-sandbox                # client only

# development: pinned, CI-tested dependency set
pip install -r requirements-dev.txt -e ".[dev]"

Client

from ring_sandbox import RingClient

with RingClient(token) as ring:                       # production by default
    for d in ring.devices(include=["status", "capabilities"]):
        print(d.name, d.online, d.capabilities.is_camera)

    cam = next(d for d in ring.devices(include=["capabilities"]) if d.capabilities.is_camera)
    for ev in ring.events(cam.id, event_types=["motion.human", "ding"]):
        print(ev.attributes.started_at, ev.attributes.event_type)

    snap = ring.snapshot_latest(cam.id, start=some_datetime)   # follows the 303 to the pre-signed URL
    clip = ring.clip(cam.id, timestamp=some_datetime, duration_ms=10_000)
    if clip.partial: print("only", clip.actual_length_ms, "ms available")

Media endpoints 303-redirect to a pre-signed URL on another host. Redirects are followed manually: bearer credentials and cookies are never forwarded, the response body is capped by max_media_bytes, and off-origin targets must be allowlisted — pass media_origins=["https://media-host.example", "*.amazonaws.com"] when talking to real Ring. JSON endpoints never follow redirects; device ids are treated as opaque single path segments.

Point it at the emulator with RingClient(token, base_url="http://127.0.0.1:8787").

Emulator

ring-sandbox serve --port 8787
ring-sandbox webhook http://localhost:8000/webhooks/ring --key my-hmac-key
ring-sandbox play delivery --speed 5           # courier: vehicle -> human -> ding -> package -> vehicle
ring-sandbox play home_aide_visit --backdate   # 90-minute visit written straight into history
ring-sandbox play examples/late_arrival.yml    # your own scenario: name, description, steps
ring-sandbox inject --type motion_detected --sub-type human

Custom scenarios are plain YAML — steps entries take offset_s, type, optional sub_type, device (id or name), and duration_ms. See examples/ for documented files: late_arrival.yml (aide shows 25 min late), partial_blackout.yml (camera dies mid-visit — departure unobserved), visitor_not_worker.yml (courier activity in the aide's window).

Interactive docs at http://127.0.0.1:8787/_sandbox/docs. Drop real default.jpg / default.mp4 (or <device_id>.jpg) in a folder and pass --media-dir to serve real media instead of placeholders.

Fidelity notes

The emulator reproduces the parts of the API that bite integrators:

Behaviour Emulated
JSON:API compound documents via ?include= yes
History newest-first, page[key] cursor, dotted event_types filters yes
is_third_party_reviewed flips after media access yes
Snapshot 303 See Other to a pre-signed URL yes
Clip 206 Partial + X-Media-Length, 416 TIMESTAMP_NOT_FOUND when idle yes
Chime playback restricted to the app's two audio slots yes
Sensor faulted semantics, 255 battery sentinel on mains devices yes
Webhook v1.1 payloads with sub_type, component_ids, HMAC X-Signature yes
OAuth / account linking / nonce flow no (use any bearer token, or --token to pin one)
WHEP / RTSP live video no

Webhooks

from ring_sandbox import webhooks

@app.post("/webhooks/ring")
async def ring_hook(request: Request):
    raw = await request.body()                                    # raw bytes, never re-serialized JSON
    ev = webhooks.parse(raw, signing_key=KEY, signature=request.headers.get("X-Signature"))
    if ev.event_type == "motion_detected" and ev.sub_type == "human": ...

Chaos fault injection

ring-sandbox serve --chaos storm runs the emulator with a fault-injection profile: webhook deliveries can be duplicated, dropped, or delayed with jitter, and history/media endpoints can return transient 500s. Presets: delivery (dup/drop/delay only), flaky (endpoint failures only), storm (both). A custom profile is a key=value list: --chaos drop=0.2,duplicate=0.4,jitter_ms=1500. --chaos-seed N makes the fault stream deterministic for reproducible runs.

Every injected fault is recorded — GET /_sandbox/chaos returns the active profile plus the actions taken so far (webhook.dropped, webhook.duplicated, webhook.delayed with the applied ms). POST /_sandbox/chaos adjusts rates live ({"drop": 0.5}) without restarting. The point is proving the receiver: a correct consumer dedupes re-delivered request_ids, tolerates out-of-order arrival, and keeps working through flaky polls.

pytest

def test_visit_detection(ring_client, ring_control):
    ring_control.post("/_sandbox/events", json={"type": "motion_detected", "sub_type": "human"})
    cam = ring_client.devices()[0]
    assert next(ring_client.events(cam.id)).attributes.event_type == "motion"

Status

Built during the Amazon Developer Hackathon 2026. Response shapes follow the public documentation; where the docs are ambiguous the emulator follows what the Playground returns (see fixtures/). Sensors and chimes are Early Access upstream and may change.

MIT licensed.

Release files for ring-sandbox 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ring-sandbox 0.1.0
File Size Uploaded
ring_sandbox-0.1.0.tar.gz 37.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ring-sandbox 0.1.0
File Interpreter ABI Platform
ring_sandbox-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 70.0 kB

Release files / ring_sandbox-0.1.0.tar.gz

Download URL ring_sandbox-0.1.0.tar.gz
Size 37.0 kB
Tags Source
SHA-256 checksum
How to use checksums
8a0738b6f9cb42de7be17bbedb9f876fa1ae8dc9971d3025a8fb6919fc308f1b
BLAKE2b-256 checksum
How to use checksums
af970eec5a9f1002919781d4442d6bc4a2b28be07c9ff5fec283547133a538df
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release files / ring_sandbox-0.1.0-py3-none-any.whl

Download URL ring_sandbox-0.1.0-py3-none-any.whl
Size 33.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
134d9e00c9e07b71de08d52df40e182f02100f7f3ed914afc466a09a484407e0
BLAKE2b-256 checksum
How to use checksums
b254c861495eb4b5ef8d352f8bf31e3176fe28cb116ab16ebf34254be6ac0db4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page