Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5512f9b
docs(scenarios): add the Scenarios section, starting with rotating OA…
rejifald Aug 4, 2026
1e42923
docs(scenarios): cost-based rate limits — the quota is in the body, n…
rejifald Aug 4, 2026
141ab49
docs(scenarios): batch writes where the retry unit is smaller than th…
rejifald Aug 4, 2026
5a18ab1
docs(scenarios): the async job triangle — submit, poll, download
rejifald Aug 4, 2026
0f9cc3e
docs(scenarios): a stream that fails after 800 tokens
rejifald Aug 4, 2026
f3015fd
docs(scenarios): the free poll — ETag revalidation and the bodyless 304
rejifald Aug 5, 2026
202edd6
docs(scenarios): the upload you must clean up after
rejifald Aug 5, 2026
0341f7b
docs(scenarios): receiving a signed webhook — where the boundary actu…
rejifald Aug 5, 2026
d466b03
docs(scenarios): one customer's revoked token, everyone's outage
rejifald Aug 5, 2026
b7e0ef4
docs(scenarios): failing over to the backup provider
rejifald Aug 5, 2026
4e480fc
docs(scenarios): the page that moved while you were reading it
rejifald Aug 5, 2026
77e11f2
docs(scenarios): the vendor changed the shape for 5% of responses
rejifald Aug 5, 2026
aa90c11
docs(scenarios): the export that eats the heap
rejifald Aug 5, 2026
39e9b68
docs(scenarios): the signature that expired in your own queue
rejifald Aug 5, 2026
bc23cf6
docs(scenarios): the charge you can't confirm
rejifald Aug 5, 2026
a2fc68c
docs(scenarios): one list, a hundred follow-up calls
rejifald Aug 5, 2026
cbb6a63
docs(scenarios): the vendor told you for six months, in a header
rejifald Aug 5, 2026
f5b6d54
docs(scenarios): register the Scenarios section in the content manifest
rejifald Aug 5, 2026
60ef3ed
docs(scenarios): file the top three drafts as GitHub issues
rejifald Aug 5, 2026
fc491b6
docs(scenarios): file three more drafts — the credential broadcast an…
rejifald Aug 5, 2026
75aeebe
docs(scenarios): the agent picks the arguments — MCP boundary, measured
rejifald Aug 5, 2026
822eadf
docs(scenarios): file input-schema filtering issue as #648
rejifald Aug 5, 2026
5290764
docs(scenarios): the mock that passed for six months — the clock table
rejifald Aug 5, 2026
89b2288
docs(scenarios): file testing-kit clock gaps as #650
rejifald Aug 5, 2026
b13adfa
docs(scenarios): file ten non-security issue drafts as #651-#660
rejifald Aug 5, 2026
f5e0b68
docs(scenarios): the ID that changed on the way in
rejifald Aug 5, 2026
7963985
docs(scenarios): finding 2 of #651 is fixed — `.safe()` no longer dow…
rejifald Aug 5, 2026
d490c5b
docs(scenarios): the customer data you didn't mean to log
rejifald Aug 5, 2026
e595f9c
docs(scenarios): the migration you have to run twice
rejifald Aug 5, 2026
fd81a04
docs(scenarios): register the five unlisted scenario pages in the man…
rejifald Aug 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
164 changes: 164 additions & 0 deletions apps/docs/content.manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export const sections: Section[] = [
},
{ path: 'getting-started', title: 'Getting started', icon: 'Rocket' },
{ path: 'recipes', title: 'Recipes', icon: 'ChefHat' },
{ path: 'scenarios', title: 'Scenarios', icon: 'Map' },
{ path: 'concepts', title: 'Concepts', icon: 'Lightbulb' },
{ path: 'guides', title: 'Guides', icon: 'BookOpen' },
{ path: 'guides/authoring', title: 'Authoring & composition' },
Expand Down Expand Up @@ -261,6 +262,169 @@ export const pages: Page[] = [
kind: 'guide',
},

// ── Scenarios ───────────────────────────────────────────────────────────
{
path: 'scenarios/index',
title: 'Scenarios',
description:
'Real integration problems that have no one-line answer anywhere — what the usual fixes cost, what StitchAPI changes, and what it leaves to you.',
kind: 'landing',
},
{
path: 'scenarios/oauth2-refresh-token-rotation',
title: 'OAuth2 refresh tokens that rotate',
description:
'A single-use refresh token plus two concurrent workers revokes the whole account. What the usual fixes cost, and what a custom auth strategy buys you.',
kind: 'guide',
},
{
path: 'scenarios/cost-based-rate-limits',
title: 'Rate limits priced in query cost',
description:
'Shopify bills per query cost, answers 200 OK when you overspend, and puts the wait in the body. Why status-code retry and rate-per-second both miss, and what does work.',
kind: 'guide',
},
{
path: 'scenarios/batch-partial-failure',
title: 'Batch writes that fail one item at a time',
description:
'A bulk endpoint returns 200 and reports that 7 of your 100 items did not land. Retrying the request re-writes the 93 that did — so the retry unit has to be the body, not the call.',
kind: 'guide',
},
{
path: 'scenarios/async-job-polling',
title: 'Submit, poll, download — the async job triangle',
description:
'A 202 with a Location header, a status endpoint that reports failure at HTTP 200, and a single-use result URL. Three endpoints and a loop, and you have to pick which guarantee you keep.',
kind: 'guide',
},
{
path: 'scenarios/mid-stream-failure',
title: 'A stream that fails after 800 tokens',
description:
'The 200 was spent on the first token, so the failure arrives in-band or not at all. When a stream can be resumed this is one flag; when it cannot — every LLM API — that same flag replays the whole answer.',
kind: 'guide',
},
{
path: 'scenarios/conditional-requests-304',
title: 'The free poll — ETag revalidation and the bodyless 304',
description:
'A 304 means "use what you have", carries no body, and is not a 2xx. Turning it back into the resource takes one seam — and the cache primitive cannot help.',
kind: 'guide',
},
{
path: 'scenarios/multipart-upload',
title: 'The upload you must clean up after',
description:
'Multipart upload is four steps, and the fourth — abort on failure — is the one no HTTP client models. Skip it and the parts bill forever, invisibly.',
kind: 'guide',
},
{
path: 'scenarios/webhook-receipt',
title: 'Receiving a signed webhook',
description:
'StitchAPI does not receive webhooks — that is your server. Here is exactly where the line falls, measured, and what the library does own on the far side of it.',
kind: 'guide',
},
{
path: 'scenarios/multi-tenant-blast-radius',
title: "One customer's revoked token, everyone's outage",
description:
'Tokens and caches isolate per tenant automatically. Rate budgets and circuit breakers do not — they isolate only by a string you have to remember to write.',
kind: 'guide',
},
{
path: 'scenarios/provider-failover',
title: 'Failing over to the backup provider',
description:
'Everything per-provider is free and declarative. The routing between them is entirely yours — and the combinator named for this job bills you twice on every successful call.',
kind: 'guide',
},
{
path: 'scenarios/unstable-pagination',
title: 'The page that moved while you were reading it',
description:
'Offset pagination over a live collection silently returns wrong lists. A client cannot fix that — but it should not report a clean run over data it lost.',
kind: 'guide',
},
{
path: 'scenarios/intermittent-drift',
title: 'The vendor changed the shape for 5% of responses',
description:
'Leveled drift catches a canary rollout precisely and refuses to invent a value. What it cannot do is tell a harmless coercion from a destructive one.',
kind: 'guide',
},
{
path: 'scenarios/large-response-memory',
title: 'The export that eats the heap',
description:
'The NDJSON decoder is genuinely O(1) — and the engine retains every chunk one line later, so neither await nor .stream() is memory-bounded.',
kind: 'guide',
},
{
path: 'scenarios/expiring-signatures',
title: 'The signature that expired in your own queue',
description:
'A rate-limited queue cannot age a SigV4 signature here — the wait happens before signing, by construction. Clock drift still needs 26 lines.',
kind: 'guide',
},
{
path: 'scenarios/unconfirmed-write',
title: "The charge you can't confirm",
description:
'A timeout tells you nothing about the server. idempotency.keyOf fixes the restart and the race in configuration alone — the default key does not, and it double-charged a re-driven job.',
kind: 'guide',
},
{
path: 'scenarios/n-plus-one-fanout',
title: 'One list, a hundred follow-up calls',
description:
'cache.coalesce collapses in-flight duplicates — 100 concurrent calls over 30 ids made 30 requests. A coalesced failure is not shared, and that is where it loses.',
kind: 'guide',
},
{
path: 'scenarios/deprecation-headers',
title: 'The vendor told you for six months, in a header',
description:
'Deprecation and Sunset arrive on responses that succeeded, so nothing fails and nothing retries. Response headers are reachable in exactly three places — here is the table.',
kind: 'guide',
},
{
path: 'scenarios/agent-holds-the-tool',
title: 'The agent picks the arguments',
description:
'Exposing a vendor API to an LLM over MCP. The credential boundary held under 30 payload scans — the argument boundary is yours, and a query parameter pinned in the path is only a default.',
kind: 'guide',
},
{
path: 'scenarios/stale-fixture',
title: 'The mock that passed for six months',
description:
'Your fake goes stale and the suite keeps saying green. Resilience and streams test perfectly offline — here is the definitive table of which time-driven features manualClock actually drives.',
kind: 'guide',
},
{
path: 'scenarios/precision-loss',
title: 'The ID that changed on the way in',
description:
'JSON.parse turns a 64-bit snowflake into a different number, silently. wire.response text plus transform recovers the exact digits in 16 lines — and a bigint in params vanishes.',
kind: 'guide',
},
{
path: 'scenarios/pii-in-the-logs',
title: "The customer data you didn't mean to log",
description:
'Response bodies reach 13 destinations and metadata reaches 11 — with nothing in between. An output allowlist takes it to zero; sensitive: true does not, and only gates the cache.',
kind: 'guide',
},
{
path: 'scenarios/dual-run-migration',
title: 'The migration you have to run twice',
description:
'Dual-running a vendor v1 and v2 when you own neither endpoint. One of five isolation channels is safe by default, and the combinator that looks built for this broadcasts one input to both.',
kind: 'guide',
},

// ── Concepts ────────────────────────────────────────────────────────────
{
path: 'concepts/the-stitch',
Expand Down
1 change: 1 addition & 0 deletions apps/docs/content/docs/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"pages": [
"getting-started",
"recipes",
"scenarios",
"concepts",
"guides",
"surfaces",
Expand Down
Loading
Loading