> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pikopod.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Import a spec, make it fail, gate a build, and reproduce a production failure in ten minutes.

This walk-through uses a fictional provider, `examplepay`, so every command is safe to copy. Every block of output came from running the real binary.

## 1. Install and run the demo

```bash theme={null}
go install github.com/pikopod/pikopod/cmd/pikopod@latest
pikopod demo
```

`demo` needs no config. It stands up a fake provider, sends traffic through the agent, changes the provider's responses behind your back, and prints the alerts. See [Installation](/getting-started/installation) for Homebrew, release binaries and the container image.

## 2. Scaffold a config

```bash theme={null}
pikopod init
```

```text theme={null}
wrote pikopod.yaml — edit the upstreams block, then: pikopod doctor && pikopod up
```

`pikopod.yaml` is read from the working directory unless `--config` says otherwise. Unknown keys are startup errors, so a typo can never silently do nothing. The full key list is in the [configuration reference](/operations/configuration).

## 3. Import a spec into a sandbox

`--spec` takes a local file or an `http(s)` URL: OpenAPI 3.x, Swagger 2.0, a Postman collection, a GraphQL schema, or a documentation page. Use your provider's spec URL, or download the small example spec these docs use to follow along exactly:

```bash theme={null}
curl -fsSL -o examplepay.spec.json https://raw.githubusercontent.com/pikopod/pikopod/main/docs/demo/examplepay.spec.json
pikopod import examplepay --spec ./examplepay.spec.json
```

```text theme={null}
sandbox examplepay registered (sbx_41d959476a09e5f9, 4 endpoints)
serve it with `pikopod up` → http://127.0.0.1:4600/examplepay/...
test credential (send it the way the spec's auth scheme expects, e.g. the Authorization header):
  pikopod_sbx_test_8eb1bc48ceb2fcd6ab7ba33c12f314189b3e51502ea32156
```

The sandbox is deterministic: same seed, same bytes, every run. Pin one with `--seed` when you want reproducible transcripts. The credential is what the sandbox expects on every request, sent the way the spec's auth scheme says.

<Tip>
  No spec? Point `--spec` at the provider's documentation page. pikopod looks for a linked or embedded spec first, then the site's well-known spec paths, and only falls back to extracting one with a model if you configured your own key. See [Importing from a docs URL](/sandbox/docs-url-import).
</Tip>

## 4. See which failures already bound to your API

There is no binding step. The moment a spec is imported, pikopod's eleven failure stories attach themselves to the operations it declares. `scenario list` shows what attached and, for anything that did not, why.

```bash theme={null}
pikopod scenario list examplepay
```

```text theme={null}
archetypes vs examplepay (4 endpoints):
  ✓ happy_path                 Happy path  (1 candidate binding(s))
  ✓ unauthorized               Unauthorized  (4 candidate binding(s))
  ✓ invalid_request            Invalid request  (1 candidate binding(s))
  ✗ duplicate_delivery         Duplicate delivery
      no webhookEvent matching {} for role 'emittedEvent'
  ✓ rate_limit_backoff         Rate limit and backoff  (4 candidate binding(s))
  ✓ state_transition_sequence  State transition sequence  (1 candidate binding(s))
  ✓ retry_storm                Retry storm with recovery  (1 candidate binding(s))
  ✓ declines                   Declines  (1 candidate binding(s))
  ✓ timeouts                   Timeouts  (1 candidate binding(s))
  ✓ partial_failure            Partial failure  (1 candidate binding(s))
  ✓ downtime_recovery          Downtime and recovery  (1 candidate binding(s))

run one: pikopod scenario run examplepay <name> [<name>...]
```

Eleven failure stories bind themselves to the operations your spec declares. The one that cannot bind says why: the spec declares no webhook event, so there is nothing to deliver twice. pikopod never guesses a test into existence. See [Archetypes](/scenarios/archetypes).

## 5. Run two of them

```bash theme={null}
pikopod scenario run examplepay declines retry_storm
```

```text theme={null}
✓ declines — PASSED (4 assertion(s) passed; 0 not evaluated)
    NOT_EVALUATED  arm-decline      armed error on POST /charges
    PASSED         declined         POST /charges → 400
    NOT_EVALUATED  clear            cleared matching faults
    PASSED         recovered        POST /charges → 201
✓ retry_storm — PASSED (4 assertion(s) passed; 0 not evaluated)
    NOT_EVALUATED  arm              armed error on POST /charges
    PASSED         attempt1         POST /charges → 503
    PASSED         attempt2         POST /charges → 503
    PASSED         attempt3         POST /charges → 201
```

Runs are ephemeral by default: each one drives its own requests at a throwaway copy of the sandbox, so nothing you serve is touched. Exit `0` when everything passes, `1` when an assertion fails, `2` on a tool error. See [Running scenarios](/scenarios/running).

## 6. Serve it and make your own tests meet the failure

```bash theme={null}
pikopod up
```

```text theme={null}
pikopod agent on http://127.0.0.1:4700  (upstreams: examplepay)
point your app's provider base URL at http://127.0.0.1:4700/<upstream>; /healthz shows progress
pikopod sandbox on http://127.0.0.1:4600  (sandboxes: examplepay)
```

Two listeners start, and which one you point at depends on what you are pointing:

| What                                                            | Point it at                                      | Credentials                               | What happens                                                 |
| --------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------------- | ------------------------------------------------------------ |
| Your tests, Postman, or a staging app that should meet failures | `http://127.0.0.1:4600/examplepay` (the sandbox) | The test credential printed at import     | pikopod answers from the spec, and fails when you tell it to |
| Your real app, in staging or production                         | `http://127.0.0.1:4700/examplepay` (the agent)   | Your real provider credentials, unchanged | pikopod forwards to the real provider untouched and observes |

For this step, point your tests at the sandbox. Then, in another terminal, put it into a scenario's standing state so your own tests, Postman, or plain curl meet the failure:

```bash theme={null}
pikopod mode set examplepay timeouts
```

```text theme={null}
mode: timeouts (from archetype or pack timeouts)
  armed   latency on GET /charges
point your app at the sandbox and run your own tests; clear it with `pikopod mode clear examplepay`
```

Or arm one fault directly:

```bash theme={null}
pikopod chaos examplepay --kind error --status 503 --method POST --path /charges
```

See [Modes](/sandbox/modes) and [Faults](/sandbox/faults).

## 7. Gate a build on a breaking spec change

This needs no sandbox, no proxy and no config file. It reads straight from git with no checkout:

```bash theme={null}
pikopod spec-diff git:origin/main:openapi.yaml openapi.yaml --fail-on ERR
```

```text theme={null}
1 change(s): 1 ERR, 0 WARN, 0 INFO

ERR  GET    /charges/{id}                            endpoint-removed
     endpoint removed from the spec  [fp_bcc85ba9a094]

breaking declared drift at/above ERR — failing the gate (exit 1)
```

Add `--format githubactions` in CI and every finding lands inline on the pull request diff. See [Spec diff](/gate/spec-diff) and [CI integration](/gate/ci-integration).

## 8. Watch a real provider

Tell the agent where the real provider is. `pikopod init` left a placeholder; replace it with the provider's real base URL:

```yaml theme={null}
upstreams:
  examplepay:
    target: https://api.examplepay.com
```

Restart `pikopod up`, then point your app's provider base URL at `http://127.0.0.1:4700/examplepay`, keeping your real provider credentials. The agent forwards everything untouched and observes afterwards.

What to expect, and when:

* **Incidents fire from the first request.** A 5xx, a 429, or an unreachable provider is a fact about one request and needs no history.
* **Drift stays quiet for 50 samples and 48 hours per endpoint.** A shape change is a claim about what is normal, and a reference built from five responses has not seen your optional fields yet. This is on purpose, and it is why nothing about drift appears on day one.
* **`pikopod status` shows progress**: every endpoint the agent has seen, its sample count, and whether it is warmed up.

For a same-day evaluation, shorten the gates and reset them later:

```yaml theme={null}
warmup:
  min_samples: 5
  min_hours: 0
```

```bash theme={null}
pikopod incidents
```

```text theme={null}
[] incident upstream_error         POST /charges (examplepay) · 4 occurrence(s) · last 2026-09-19T10:00:00Z
  fp_14835fa32dfb
  reproduce: pikopod scenario reproduce fp_14835fa32dfb
  export: pikopod incidents export fp_14835fa32dfb
```

See [Observe](/observe/overview).

## 9. Reproduce the failure on your laptop

```bash theme={null}
pikopod scenario reproduce fp_14835fa32dfb
```

```text theme={null}
reproduced fp_14835fa32dfb (examplepay answered 503 on POST /charges) as pikopod-data/scenarios/incident-14835fa32dfb.yaml
PASSED — 1 assertion(s) passed; 0 not evaluated
the failure now happens locally — fix it, then re-run: pikopod scenario run examplepay incident-14835fa32dfb
```

The generated pack is an ordinary scenario. Commit it and it guards that path forever. When the agent runs on another host, `pikopod incidents export <fp>` writes a bundle that `reproduce` accepts in place of the fingerprint. See [Reproduce](/reproduce/reproduce).

## 10. Regress forever

```bash theme={null}
pikopod replay --ci
```

```text theme={null}
examplepay: 37 recordings gated (1 pre-warmup skipped) — 0 finding(s)
clean — no drift against frozen baselines
```

Replay runs offline against recorded traffic and frozen baselines. See [Replay gate](/observe/replay-gate).

## What just happened

* `import` normalized the spec into pikopod's internal representation, where every field carries provenance, and registered a sandbox under `data_dir`.
* `scenario list` bound eleven provider-agnostic archetypes to the operations the spec declares, refusing the one it could not ground.
* `scenario run` expanded two archetypes into concrete steps and ran them against an ephemeral engine.
* `up` started two listeners in one process: the sandbox on `:4600` and the observing agent on `:4700`.
* `spec-diff` normalized two spec versions and derived a severity for every change by one fixed rule.
* `incidents` read the event log, and `reproduce` turned one event plus its redacted recording into a runnable pack.

## Adopt it in this order

1. **Day one: the sandbox and the CI gate.** Import the spec, run the archetypes in CI with a pinned seed, and add `spec-diff` to the pull request pipeline. Nothing is installed in your request path.
2. **Then: the agent in staging.** Point the staging app at the agent and let it warm up somewhere low-stakes. Incidents are useful immediately; drift becomes useful after two days.
3. **Then: the agent in production.** One agent as a shared service, its `data_dir` on a persistent volume. The first thing production sees is a tool that has already been quiet for two days. See [Deployment](/operations/deployment).

## Next steps

<CardGroup cols={2}>
  <Card title="The loop" icon="arrows-rotate" href="/getting-started/the-loop">
    Why the sandbox and the observer are one tool.
  </Card>

  <Card title="Use it from a coding agent" icon="robot" href="/getting-started/coding-agents">
    The same checks over MCP, with honest verdicts.
  </Card>

  <Card title="Webhooks" icon="bolt" href="/sandbox/webhooks">
    Deliveries wrapped and signed the way the provider documents.
  </Card>

  <Card title="Deployment" icon="server" href="/operations/deployment">
    Where the agent runs, and where its state lives.
  </Card>
</CardGroup>
