# How to wire Tidebase into a SvelteKit app

SvelteKit endpoints receive a web `Request`, which is exactly what Tidebase's recovery webhook handler takes — so the webhook mounts in one line, and your routes enqueue durable runs and read live run state for the UI. No adapters anywhere.

```typescript
// src/routes/api/tidebase/+server.ts — recovery webhook
import { Tidebase } from '@tidebase/sdk'
import { generateReport } from '$lib/workflows/generate-report'

const tide = new Tidebase()
tide.workflow('generate-report', generateReport)
const handler = tide.webhook()

export const POST = ({ request }) => handler(request)
```

```typescript
// src/routes/api/reports/+server.ts — enqueue, don't execute
import { json } from '@sveltejs/kit'
import { Tidebase } from '@tidebase/sdk'

const tide = new Tidebase()

export async function POST({ request }) {
  const { topic } = await request.json()
  const { run } = await tide.enqueue('generate-report', {
    input: { topic },
    dedupeKey: `report-${topic}`
  })
  return json({ runId: run.id })
}
```

```typescript
// src/routes/reports/[runId]/+page.server.ts — live progress for the page
import { Tidebase } from '@tidebase/sdk'

const tide = new Tidebase()

export async function load({ params }) {
  const detail = await tide.runs.get(params.runId)
  return { status: detail.run.status, state: detail.state, gates: detail.gates }
}
```

Tidebase is an open-source checkpoint layer for AI agents: wrap your steps, and failed runs resume from the last safe point — in your own Postgres, without moving execution into a new runtime.

## The shape

- **Enqueue from endpoints.** `tide.enqueue` writes a durable `queued` run (deduped against double submits) and returns the `runId` immediately — the request never carries agent work, which keeps you inside serverless limits on adapter-vercel/netlify deployments.
- **Execute in a worker.** The honest tradeoff, stated plainly: Tidebase does not execute your code. A long-lived Node process runs `tide.work({ queues: ['default'] })` with your workflows registered and executes jobs off the queue; retries, backoff, and lease-expiry requeues are Tidebase's job. On serverless SvelteKit deployments this worker is a small companion service.
- **Load run state in `load`.** Progress, step results, and pending gates come from `runs.get` — server-rendered, no client polling infrastructure required (poll or use SvelteKit's `invalidate` on whatever cadence suits the page).

## Approvals on your own pages

A workflow paused at `run.gate('approve-send', ...)` appears in the `load` data above as a pending gate. Render approve/reject buttons and resolve from a form action using the `resolveUrl` and `resolveToken` the gate's webhook channel delivers — durable, exactly-once, recorded with the actor. See [human approval gates](../human-approval-gates-for-ai-agents.md).

## Recovery semantics

When a non-queue run stalls or fails, Tidebase POSTs a signed `run.resume` payload to `/api/tidebase`; the SDK verifies the HMAC (`TIDEBASE_WEBHOOK_SECRET` on both sides) and re-invokes the workflow with the same `runId`. Completed steps replay from Postgres per [the replay contract](../replay-contract-is-it-safe-to-rerun.md). The webhook executes the remaining workflow inline, so on serverless it inherits the platform's time limits — for long tails, prefer queues, where the reconciler requeues without a webhook.

## What Tidebase does not do here

- **Alpha, opt-in auth.** Set `TIDEBASE_API_KEY` before exposing the Tidebase server beyond localhost.

Repo: <https://github.com/BlueprintLabIO/tidebase> · See also: [How to run durable AI workflows behind a Next.js route](nextjs.md)