api-debug · git:20260703.247aff5 · 2026-07-03 · sha256 9303ace4532004fe

api-debug git:20260703.247aff5A

Immutable. This exact content never changes and is served at /api/v1/blob/9303ace4532004fe.

---
name: api-debug
description: Debug REST, GraphQL, webhook, and OAuth/API-key integrations with layered request, auth, schema, and response checks.
metadata: {"cybara":{"requires":{"anyBins":["curl","python3"]}}}
---

# API Debug

Use this when an API returns the wrong status/body, auth fails, a webhook does not arrive, pagination is suspicious, GraphQL returns `errors`, or code works in one client but not another.

## Principle

Isolate the failing layer before changing code:

1. Connectivity
2. TLS and proxy behavior
3. Authentication
4. Request shape
5. Response parsing
6. Semantic correctness
7. Retry, timeout, and rate-limit behavior

## REST Checks

Use `curl` for the smallest repro. Keep tokens in environment variables and never paste full secrets into chat or logs.

```bash
curl -v --connect-timeout 5 https://api.example.com/health
curl -sS -D /tmp/headers.txt -o /tmp/body.json https://api.example.com/resource
python3 -m json.tool /tmp/body.json
```

For JSON POSTs:

```bash
curl -sS -v -X POST "$URL" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  --data '{"key":"value"}'
```

## GraphQL Checks

GraphQL failures often return HTTP 200. Always inspect `errors`.

```bash
curl -sS -X POST "$GRAPHQL_URL" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  --data '{"query":"{ viewer { id } }"}' | python3 -m json.tool
```

If `errors` exists, debug query shape and permissions even when the HTTP status is 200.

## Auth Checks

- Confirm scheme: `Bearer`, `Basic`, `Token`, `X-Api-Key`, signed request, OAuth cookie.
- Confirm environment: staging token against staging host.
- Confirm expiry for JWTs without printing the full token:

```bash
python3 - <<'PY'
import base64, json, os, time
tok = os.environ["TOKEN"]
payload = tok.split(".")[1]
payload += "=" * (-len(payload) % 4)
data = json.loads(base64.urlsafe_b64decode(payload))
print({k: data.get(k) for k in ("iss", "aud", "sub", "exp")})
print("expired:", data.get("exp", 0) < time.time())
PY
```

## Webhooks

- Capture raw request body and headers before parsing.
- Verify signature over the exact raw bytes, not a reserialized JSON object.
- Check timestamp skew and replay windows.
- Store a failing fixture and make a regression test for signature parsing.

## Fix Criteria

A fix is done only when:

- A minimal repro fails before and passes after.
- The code has bounded timeouts and safe retry behavior.
- Pagination and rate limits are handled explicitly.
- Error messages include status/request IDs without leaking secrets.