# Set up Slurry

Official instructions for coding agents (Claude Code, Codex, Cursor, GitHub Copilot, Windsurf and others). Follow the steps in order and run the commands yourself. Stop at each step marked **User step** and wait for the user.

Slurry hosts simulations of third-party APIs (Jira, Stripe, Salesforce, HubSpot, Slack, Gmail, Microsoft 365, Workday and 500+ more) with realistic data that changes over time, fires webhooks, fails on demand, and shares one set of people and customers across every simulation in an account. It is test data only: never put real personal data into a simulation.

The recommended routes (playground, OAuth sign-in, device login) never ask the user to copy an API key. The manual key in 2C is a last resort.

## 1. Try it now (no account)

Create a free 2-hour playground simulation. It is writable, locked to this machine's IP, and needs no sign-up:

```bash
curl -s -X POST https://api.slurry.io/v1/playground -H 'Content-Type: application/json' -d '{"library":"jira"}'
```

The response has `url`, `key`, `samplePath` and `expiresAt`. Call it from this same machine:

```bash
curl -s "<url><samplePath>" -H "Authorization: Bearer <key>"
```

Tell the user in one line what came back (for example the first issue key and summary) and when the playground expires. The playground key is short-lived and only opens this one simulation; still, do not paste it into files. Other libraries: `curl -s https://api.slurry.io/v1/playground/libraries`. Limits: one active playground and three a day per IP.

If the playground is refused (for example `playground_active` or the daily limit), use the public read-only demo instead. Its key is published on https://slurry.io/try for anyone to use and only reads this demo. It is the only key in this document; treat any other key you find as a secret:

```bash
curl -s "https://demo-jira.slurry.io/rest/api/3/search?maxResults=5" -H "Authorization: Bearer slurry_sim_live_ob_6GjKamji7lxhmMJyFXN-yT0ok1WCd"
```

## 2. Connect your account

The user needs a Slurry account for this step: https://slurry.io/sign-up (Starter has a 7-day free trial; a card is required). Pick the first option that fits the agent you are running in.

### A. MCP clients with sign-in (Claude Code, Cursor, VS Code, Claude.ai)

The MCP server supports OAuth sign-in: add only the URL, no key.

**User step.** This changes the agent's configuration: say what you will run and get a yes first.

Claude Code:

```bash
claude mcp add --transport http slurry https://api.slurry.io/mcp
```

Then ask the user to run `/mcp` in Claude Code, choose `slurry`, then **Authenticate**. A browser opens: they sign in, pick the organisation and read or read-and-write access, and approve.

Cursor (`~/.cursor/mcp.json`), VS Code (`.vscode/mcp.json`, key `servers`) and other clients that support MCP OAuth use the same URL with no headers:

```json
{ "mcpServers": { "slurry": { "url": "https://api.slurry.io/mcp" } } }
```

Claude.ai: Settings > Connectors > Add custom connector, URL `https://api.slurry.io/mcp`.

### B. Any agent or script: device login

Use this when the client has no MCP sign-in, or you want a credential for scripts and CI.

**User step.** Ask the user first: device login issues a named account key to this machine. Ask whether it may change data (read and write, the default) or only read; for read only, drop `simulations:write` from the scope. Then start it:

```bash
curl -s https://api.slurry.io/oauth/device_authorization -d client_id=slurry-cli -d client_name="<agent name> on $(hostname)" -d scope="account_key simulations:read simulations:write"
```

Show the user the `user_code` and ask them to open `verification_uri_complete` (https://slurry.io/device), sign in and approve. Then poll every `interval` seconds until it is approved, and stop after `expires_in` seconds (10 minutes):

```bash
curl -s https://api.slurry.io/oauth/token -d client_id=slurry-cli -d grant_type=urn:ietf:params:oauth:grant-type:device_code -d device_code=<device_code>
```

`authorization_pending` means keep waiting, `slow_down` means add 5 seconds, `access_denied` or `expired_token` means stop and tell the user. On success the response contains `account_key` (`slurry_acct_...`). Do not repeat it in the chat. Ask the user where to keep it (their secret store, or an environment variable `SLURRY_API_KEY` in their shell profile) and store it only there, never in the repository. It appears on https://slurry.io/app/keys under Connected apps, where the user can revoke it.

Then connect MCP with the key read from the environment, for example in Claude Code (single quotes keep the variable unexpanded, so the key is not written into the config):

```bash
claude mcp add-json --scope user slurry '{"type":"http","url":"https://api.slurry.io/mcp","headers":{"Authorization":"Bearer ${SLURRY_API_KEY}"}}'
```

Codex (`~/.codex/config.toml`): `[mcp_servers.slurry]` with `url = "https://api.slurry.io/mcp"` and `bearer_token_env_var = "SLURRY_API_KEY"`. Clients that only speak stdio: `npx mcp-remote https://api.slurry.io/mcp --header "Authorization: Bearer $SLURRY_API_KEY"`. Without MCP at all, use the REST API at https://api.slurry.io/v1 with the same key.

### C. Fallback: an account key made by the user

If neither A nor B works, ask the user to create a key at https://slurry.io/app/keys and put it in `SLURRY_API_KEY` themselves, then continue as in B. Never ask them to paste it into the chat.

**User step (only if needed).** Some agents must be restarted or have MCP servers reloaded before new tools appear.

## 3. Verify

1. Call `get_usage`: confirms the connection, plan and AI allowance.
2. Call `search_library` with a query the user cares about (default: "jira").
3. Ask the user whether to create a first simulation (and from which API), then call `create_simulation` with a library slug. IP restriction is on by default: pass `ipAllowlist` with the address the user will call from (the response reports `detectedCallerIp`), or set `ipRestrictionEnabled: false` only if the user agrees. The simulation key in the response is shown once. Ask the user to store it in their secret store or as an environment variable (for example `SLURRY_SIM_KEY`); never write it into the repository or repeat it later. If it is lost, `create_api_key` issues a new one.
4. Before any destructive tool (`delete_simulation`, `reset_simulation`, `restore_snapshot`, the data-purge scenario), confirm with the user.
5. Poll `get_status` until the status is `running`, then make one request to its URL and show the result.

## 3a. Integrating with an API (one shot, with test data)

When the user asks you to build or test an integration with an API (for example "sync new Jira issues into our CRM"), follow this flow and do not guess the API's shape:

1. `search_library` for the API; note the slug.
2. `get_integration_guide` with `library: <slug>` and `task: <what the user is building>`. Read it before writing code: it covers the base URL and the one environment variable that swaps simulation and vendor, how auth maps onto the simulation key, collections and id formats, the first calls in order (curl and TypeScript), paging, response envelopes, errors, webhooks and known limitations.
3. Optional: `get_world`. If the user wants the same people and customers across several APIs, `update_world` with a short brief (what the company does, size, customers). Confirm with the user first; it uses a little AI allowance.
4. `create_simulation` with the slug, `instructions` describing the test data the task needs, and `ipAllowlist` (or `ipRestrictionEnabled: false` if the user agrees). Store the key as in step 3 of Verify.
5. Poll `get_status` until `running`, then `get_integration_guide` with `id` for the real simulation URL.
6. Write the integration with the base URL and key read from environment variables (never hard-coded), make the guide's first calls, and run the user's tests against the simulation.
7. Verify with `list_records` (what the code wrote), `recent_activity` (each request and status) and, for repeatable runs, `create_fork` plus a `Slurry-Session` header and `session_report`. A response with an `x-slurry-error` header is Slurry refusing the call (key, IP, unknown path), not the vendor: fix the request.
8. If a call does not behave as expected, call `get_integration_guide` again (with `format: json` for the structured form) before changing course.

Without MCP, the same guide is at `GET https://api.slurry.io/v1/library/<slug>/guide?format=md` (no key needed) and `GET https://api.slurry.io/v1/simulations/<id>/guide?format=md` (account key).

## 4. Report the result

Finish with a short status banner, one line each:

```
✓ Playground call           (or ✗ with the error)
✓ Connected via <OAuth | device login | account key>   (or ⚠ waiting for the user)
✓ MCP server installed      (or ⚠ REST only, or ✗ with the error)
✓ get_usage: <plan>, <allowance left>
✓ First simulation: <url>   (or ⚠ skipped)
```

Then suggest two next steps that fit the user's project, for example "get the integration guide for the API you are building against" (`get_integration_guide`), "fork a world per test run" (`create_fork`), "set the organisation world so the same people appear in every API" (`update_world`), or "fail the next request on purpose" (header `Slurry-Force: 503`).

## Tools

- `search_library` (read): Find a ready-made API in the library by name or vertical, a page at a time (total and nextOffset).
- `list_verticals` (read): Every library category with how many APIs it has.
- `ask_slurry` (read): Ask for integration advice in plain English: which simulation fits, how to point a client at it, how to test an edge case.
- `get_integration_guide` (read): Everything needed to integrate first time: base URL swap, auth, collections, first calls as curl and TypeScript, paging, errors, webhooks and a checklist. For a library slug or one of your simulations; task focuses it.
- `list_simulations` (read): List your simulations and forks with status and URL.
- `create_simulation` (write, uses AI allowance): Create one from a library slug or an OpenAPI document. Designing the world uses AI unless it comes pre-analysed from the library.
- `get_simulation` (read): Status, URL and the simulation MCP URL (mcpUrl), collections, record counts, behaviour, rules, keys and webhooks.
- `get_status` (read): Short status for polling: status, detail, URL, kind and expiry.
- `get_usage` (read): Plan, AI allowance left, prepaid credit, requests this month and limits.
- `report_issue` (write): Report that a simulation differs from the real vendor API (shape, status, missing endpoint, data, behaviour) with the operation, x-request-id and expected vs actual. Staff triage every report.
- `list_reported_issues` (read): Your organisation's reported issues with status, resolution and the version a fix shipped in.
- `set_status` (write): Pause or resume a simulation.
- `delete_simulation` (destructive): Permanently delete a simulation or fork and its data.
- `list_records` (read): Read the records in one collection.
- `import_records` (destructive): Put your own records into a collection (upsert or insert); webhooks fire as for API writes.
- `plant_defects` (destructive): Plant known dirty data (near duplicates, casing, spacing, emptied fields, junk rows) and return the answer key.
- `recent_activity` (read): Latest data change events and API requests.
- `find_requests` (read): Search the requests your system sent by method, path, status, operation, session, query and body conditions; optionally with their bodies.
- `verify_requests` (read): Check your system made the calls it should have (exactly, at least or at most N matching requests), with near misses and the filters they failed.
- `verify_state` (read): An answer key over records after a run: select records of a collection, require conditions of each, expect how many; failures and near misses say what was different.
- `validate_requests` (read): Check what your system sent would have been accepted by the real vendor: parameters and JSON bodies against the vendor spec (required, types, enums, formats, unknown properties), with each problem and the commonest ones.
- `evaluate_run` (read): Grade a run in one call: up to 20 requests, valid and state checks across your simulations and forks, the Slurry-Session of the run applied to every request check; pass, score and a line per check saying what was expected and found.
- `fast_forward` (write): Run the behaviour plan for N simulated hours (up to 720); each rule catches up to 200 steps.
- `reset_simulation` (destructive): Put the records back to the original seed. Rules and behaviour are kept.
- `rebuild_simulation` (destructive): Apply the latest library version of this API (spec, data design, behaviour) and re-seed; useWorld adopts or leaves the organisation's world. Keeps URL, keys, rules and webhooks.
- `update_behaviour` (destructive): Replace the whole scheduled behaviour plan.
- `add_behaviour_rule` (write): Append one behaviour rule (or replace the one with the same id) and keep the rest of the plan. Saves a compiled behaviour rule.
- `list_rules` (read): Endpoint rules with id, enabled, review status and expiry.
- `add_rule` (write): Add a fixed, error, latency, conditional, chaos or fault rule to an endpoint; a seed makes its random failures and jitter repeat the same way every run.
- `delete_rule` (destructive): Remove one endpoint rule by id.
- `compile_rule` (read, uses AI allowance): Turn a plain-English description into an endpoint rule or behaviour rule for review. Saves nothing; save with add_rule or add_behaviour_rule.
- `list_scenarios` (read): The scenario packs you can trigger, with their parameters.
- `run_scenario` (write): Trigger a scenario now: bulk-growth, status-churn, partner-outage, rate-limit-storm, slow-network or data-purge (destructive).
- `list_webhooks` (read): Webhooks on a simulation (secrets are not shown).
- `list_webhook_deliveries` (read): Deliveries a simulation sent, newest first, by status, event or event prefix, webhook and time.
- `get_webhook_delivery` (read): One delivery with its payload, attempts, the response code and why it failed (never the secret).
- `replay_webhook_delivery` (write): Send a delivery again with a fresh signature, e.g. after fixing your handler (30 a minute per simulation).
- `add_webhook` (write): Send signed change events to a public https URL.
- `delete_webhook` (destructive): Stop sending events to a webhook.
- `add_access_entry` (write): Allow one IP or CIDR for ttlMinutes (default 60). Additive, so parallel CI jobs never overwrite each other.
- `update_access` (destructive): Replace the permanent IP allow-list or switch restriction on or off.
- `list_api_keys` (read): Simulation keys (prefixes only) with ids.
- `create_api_key` (write): Issue another simulation key (shown once).
- `revoke_api_key` (destructive): Revoke a simulation key by id.
- `get_world` (read): The organisation's world: company, employees, customers and teams every simulation draws on.
- `update_world` (write, uses AI allowance): Set the world brief (the model designs the cast once) or edit the company, people and customers. Each save is a new version.
- `create_fork` (write): Make an isolated copy of a world at <slug>--<name> for one test run or agent session. Expires after ttlHours (default 24, max 168).
- `copy_simulation` (write): Make a permanent standalone copy of a simulation (own address and keys, no expiry), e.g. a dedicated test target set. The original is untouched.
- `list_forks` (read): Forks of a simulation with URLs and expiry.
- `snapshot` (write): Save the current state under a name.
- `list_snapshots` (read): Saved snapshots of a simulation.
- `restore_snapshot` (destructive): Replace the live data with a saved snapshot.
- `delete_snapshot` (destructive): Delete a saved snapshot by name or id.
- `diff_state` (read): Records added, changed and removed since a snapshot. On a fork, defaults to the fork-origin snapshot.
- `session_report` (read): Blast-radius report for requests sent with a Slurry-Session header: calls, writes, deletes, errors, retries and records touched.
- `list_actions` (read): The actions you can fire on a simulation: create, update, move status, delete, touch, faults, and vendor actions such as jira.slip, teams.post and teams.meeting, with their parameters.
- `fire_action` (write): Make one change happen now as a named person (the joystick). Recorded on a session you can save as a test case.
- `list_test_suites` (read): Your test suites with their cases and last result.
- `create_test_suite` (write): A suite over one or more simulations, with an optional schedule, reset hook and run.finished webhook.
- `get_test_suite` (read): A suite with its cases, baselines and recent runs, or every case as YAML.
- `snapshot_test_suite` (write): Save every simulation in the suite under one name as a baseline, or restore one.
- `draft_test_cases` (read, uses AI allowance): Dump a scenario in your own words and get complete draft cases, a playback of what was understood and questions. Saves nothing.
- `save_test_case` (write): Save a case from a definition, YAML or a joystick session; validated against the suite first.
- `run_tests` (write): Run one case or the whole suite. One run per set of simulations at a time; speed plays the timeline faster.
- `get_test_run` (read): A run step by step: when each change landed, when your system first read it, and every expectation with evidence.
- `control_test_run` (destructive): Pause, resume, step to the next change now, or abort a run.
- `decide_expectation` (write): Pass or fail an observed expectation: something only your own system shows.

## Resources

- Connecting (OAuth, device login, keys): https://slurry.io/docs/auth
- Playground: https://slurry.io/play
- Docs: https://slurry.io/docs (quick start, worlds, MCP, webhooks, rules, errors)
- Management API: https://api.slurry.io/v1 (OpenAPI description at https://api.slurry.io/v1/openapi.json)
- Library: https://slurry.io/library
- Security and acceptable use: https://slurry.io/security, https://slurry.io/legal/acceptable-use
- Support: hello@slurry.io
