Sandbox Test Scenarios

Concrete, copy-pasteable ways to exercise your integration against the seeded sandbox fixture. Every scenario uses a sk_test_ key against the real deterministic ids (all ≥ 900000000). See Sandbox & Testing for the full fixture reference.

Seed first

Every scenario below assumes the fixture is loaded. Run POST /v1/sandbox/seed once (it's idempotent) before you start, and POST /v1/sandbox/reset when you want a clean slate.

Scenario: seed and read inventory

The end-to-end happy path — no OAuth, no live channel. Seed the fixture, list your listings, and read one back by id with content and details expanded.

# Seed the deterministic fixture (idempotent)
curl -X POST https://api.repull.dev/v1/sandbox/seed \
  -H "Authorization: Bearer sk_test_YOUR_KEY"
# → { "data": { "listings": [900000101, 900000102, 900000103], ... } }

# List all three seeded listings
curl https://api.repull.dev/v1/listings \
  -H "Authorization: Bearer sk_test_YOUR_KEY"

# Read one by id, with content + details
curl "https://api.repull.dev/v1/listings/900000101?include=content,details" \
  -H "Authorization: Bearer sk_test_YOUR_KEY"

Scenario: walk the reservation lifecycle

The five seeded reservations (930000101930000105) cover created, confirmed, cancelled, modified, and pending. Drive your status-handling and reconciliation logic against them without waiting for a real booking.

# Every seeded reservation, across the full lifecycle
curl https://api.repull.dev/v1/reservations \
  -H "Authorization: Bearer sk_test_YOUR_KEY"

# A single one by id — 930000104 is the "modified" case
curl https://api.repull.dev/v1/reservations/930000104 \
  -H "Authorization: Bearer sk_test_YOUR_KEY"

Every reservation carries an updatedAt. Amended reservations (modified / date-changed) have an updatedAt strictly after their createdAt, so a “what changed since my last sync?” filter has real data to catch.

Scenario: prove tenant isolation

If you build a multi-tenant PMS, you need to prove reads never leak across connected accounts. The two seeded accounts — 960000101 (Airbnb) and 960000102 (Booking.com) — let you test scoping end-to-end with the X-Account-Id header.

# Airbnb account → Ocean View (900000101) + Cabin (900000102)
curl https://api.repull.dev/v1/listings \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -H "X-Account-Id: 960000101"

# Booking account → Ocean View (900000101) + Studio (900000103)
curl https://api.repull.dev/v1/listings \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -H "X-Account-Id: 960000102"

# Pinned to Airbnb, the Booking-only Studio is out of scope → 404
curl https://api.repull.dev/v1/listings/900000103 \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -H "X-Account-Id: 960000101"

No header returns the full workspace (all three listings). A by-id read for a fixture whose channel doesn't match the pinned account returns 404 not_found. See API Keys & Account Scope.

Scenario: write content and read it back

Content updates round-trip in the sandbox, so you can test your write-then-verify flow without touching a real channel. PUT /v1/listings/{id}/content updates the fixture and returns the fields it changed.

# Update the fixture
curl -X PUT https://api.repull.dev/v1/listings/900000102/content \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"description":"Updated cabin copy","maxOccupancy":4}'
# → { "data": { "id": 900000102, "changed": ["description", "maxOccupancy"], "deferred": [] } }

# Read it back — the change is live and updatedAt has moved
curl https://api.repull.dev/v1/listings/900000102 \
  -H "Authorization: Bearer sk_test_YOUR_KEY"

Scenario: read a deterministic calendar

GET /v1/availability/{propertyId} returns a deterministic, all-available calendar at the fixture's nightly price — ideal to assert on. It requires a valid from/to range; a missing or invalid range returns 422 invalid_params.

# A valid window
curl "https://api.repull.dev/v1/availability/900000101?from=2026-08-01&to=2026-08-07" \
  -H "Authorization: Bearer sk_test_YOUR_KEY"

# Missing range → 422 invalid_params
curl "https://api.repull.dev/v1/availability/900000101" \
  -H "Authorization: Bearer sk_test_YOUR_KEY"

Scenario: exercise error handling

Every error is a structured envelope with a stable code, a human message, a fix, and a docs_url. You can trigger each of these against the sandbox today — build your retry and surfacing logic against them. See Errors for the full catalog.

RequestResult
Omit or malform the Authorization header401 unauthorized
POST /v1/sandbox/seed with a sk_live_ key403 forbidden
GET /v1/listings/999999999 (unknown fixture id)404 not_found
GET /v1/listings/900000103 with X-Account-Id: 960000101 (channel mismatch)404 not_found
GET /v1/availability/900000101 (missing from/to)422 invalid_params

Not modeled yet

Deterministic failure fixtures — magic ids that force a 409, a 504, or a rate-limit response on demand, Stripe-test-card style — are not available yet; they are planned. Sandbox-aware channel writes beyond content (publish, messaging echo) are not modeled yet either, and there are no seeded guest, review, or conversation fixtures. Track progress on the changelog.
AI