All docs
SDKs

Build the stack, not the plumbing

Drop a Coday SDK into any project and skip the auth boilerplate, the S3 IAM dance, the WebSocket reconnect logic, and the cron job container — Coday provisions the backend on your first deploy.

00Install

One install for everything

All Coday SDKs share a tiny core (@codayinc/core) for env reading, token storage, and HTTP. Install just the ones you use.

Install
pnpm add @codayinc/auth @codayinc/storage @codayinc/realtime @codayinc/worker

Push to GitHub. Coday detects the imports in package.json and, on first deploy:

  • Provisions a per-project Aurora database.
  • Runs idempotent _coday_* table migrations.
  • Allocates a per-project S3 prefix and Redis namespace.
  • Injects COD_* env vars into your ECS task.
01@codayinc/auth

Auth — Coday-managed OAuth + magic link

One OAuth flow. No client_ids. No callback registration. Sign in with GitHub, Google, magic link, email + password, or TOTP — all routed through Coday's master OAuth apps and stored in your per-project database.

Client (browser)

sign-in.tsx
import { signIn, signOut, getSession, consumeOauthCallback } from "@codayinc/auth"

// Call once on app boot — consumes #coday_token=... after OAuth redirect.
consumeOauthCallback()

await signIn("github")              // → 302 to Coday → 302 to GitHub
await signIn("email", { email, password })
await signIn("magic-link", { email })
const session = await getSession()
await signOut()

React hooks

useUser.tsx
import { useAuth, useUser } from "@codayinc/auth/react"

function Header() {
  const { user, status, signOut } = useAuth()
  if (status === "loading") return <Spinner />
  if (!user) return <SignInButton />
  return <UserMenu user={user} onSignOut={signOut} />
}

Server (Next.js App Router)

server.tsx
import { auth, requireAuth, verifyToken } from "@codayinc/auth/server"

export async function GET() {
  const session = await auth()
  if (!session) return new Response("Unauthorized", { status: 401 })
  return Response.json({ user: session.user })
}
02@codayinc/storage

Storage — S3 without the IAM

Upload files from the browser through Coday's API. No CORS configuration, no presigned-URL boilerplate, no S3 SDK in the bundle. Each project gets a private prefix; metadata mirrors into your project DB so you can SELECT directly.

upload.tsx
import { storage } from "@codayinc/storage"

// Direct upload (≤ 50 MB)
const meta = await storage.upload("avatars/me.jpg", file, { visibility: "public" })

// Larger files — presigned PUT
const { url, headers } = await storage.getSignedUrl("video.mp4", { method: "PUT", expiresInSec: 600 })
await fetch(url, { method: "PUT", body: file, headers })

const list = await storage.list({ prefix: "avatars/", limit: 50 })
await storage.delete("avatars/old.jpg")
03@codayinc/realtime

Realtime — channels + presence with auto-reconnect

WebSocket channels with exponential-backoff reconnect, presence sets, and server-side publish for cron and webhooks. Wire protocol is JSON-over-WS. Cross-instance fan-out runs over Redis pub/sub.

chat.tsx
import { realtime } from "@codayinc/realtime"
import { useChannel, usePresence } from "@codayinc/realtime/react"

function ChatRoom({ id }: { id: string }) {
  const { messages, publish } = useChannel<{ text: string }>(`room:${id}`)
  const { members } = usePresence(`room:${id}`)
  return (
    <>
      <Online members={members} />
      <Log messages={messages} />
      <Composer onSend={(text) => publish("say", { text })} />
    </>
  )
}

// Server-side (cron, webhook, etc.)
import { publishFromServer } from "@codayinc/realtime/server"
await publishFromServer(`room:${id}`, "system", { text: "Daily digest ready" })

Note: the WebSocket gateway service is on the Sprint S8 rollout path. SDK is shipped today; the realtime.coday.io ALB listener follows in S8.

04@codayinc/worker

Worker — background queues + cron

defineWorker + queue wrap BullMQ on Coday's managed Redis (one namespace per project). On every deploy, Coday AST-scans your repo for cron schedules and provisions an EventBridge rule — no separate Vercel cron config or Kubernetes CronJob.

workers/email.ts
import { defineWorker, queue } from "@codayinc/worker"

defineWorker<{ to: string; body: string }>({
  queue: "email",
  concurrency: 10,
  async handler({ to, body }) {
    await sendMail(to, body)
  },
})

// Enqueue from anywhere in your code
await queue<{ to: string; body: string }>("email").add({
  to: "[email protected]",
  body: "Welcome",
})

// Or define a periodic job (Coday provisions the EventBridge rule)
defineWorker({
  queue: "daily-rollup",
  cron: "0 6 * * *",
  async handler() {
    await rollup()
  },
})

Note: cron AST-scan is shipped today; EventBridge auto-provision lands in Sprint S8. Until then, the SDK still runs your queues; periodic schedules just need a manual rule.

05Env reference

Coday-injected env vars

Coday injects these into your ECS task on every deploy. Don't set them manually in local dev — point COD_*_URL to a Coday test deployment instead.

  • COD_PROJECT_ID — Coday project ID
  • COD_AUTH_URL, COD_AUTH_SECRET
  • COD_STORAGE_URL, COD_STORAGE_CDN, COD_STORAGE_TOKEN
  • COD_REALTIME_URL, COD_REALTIME_TOKEN
  • COD_WORKER_REDIS_URL, COD_WORKER_TOKEN
  • DATABASE_URL — per-project Postgres (also used by your own Prisma/Drizzle)
06Migrating from

From Supabase or Vercel + Supabase

The bigger surface (auth + storage + realtime + DB + deploy) collapses into a single workspace. Migration roughly looks like:

  1. Replace @supabase/supabase-js auth calls with @codayinc/auth.
  2. Move Supabase Storage buckets → @codayinc/storage (the API surface lines up almost 1:1).
  3. Replace Realtime channels with @codayinc/realtime.
  4. Migrate your Postgres schema; Coday provisions the DB on first deploy.
  5. Move Vercel cron config into defineWorker({ cron: ... }).

A step-by-step migration playbook with full code mappings is in flight — see [email protected] if you want a walkthrough for your project.