Yettyبيتا مفتوحة
Developers · Yetty Apps

Yetty Apps developer docs

Run a real Next.js backend - API routes, a database, scheduled jobs - on Yetty. Build everything free with the starter template and the vendored SDK; pay only when you go live. And leave any time: portability here is a test that runs, not a promise.

What is Yetty Apps

Yetty runs two kinds of projects. A static site (plain HTML, or a Next.js static export with no server code) takes the free convert path - it becomes a managed, editable website with a CMS, leads, SEO and versioning. That story lives in the main docs.

A real backend - Next.js with API routes, server components, middleware, a database - is what Yetty Apps runs. We build it from your GitHub repo or a zip, deploy it to Cloudflare's edge, wire up a database and environment variables, give it signed scheduled jobs and deferred work, and keep a deploy history. You keep writing plain Next.js; there is no proprietary config in your repo.

When you send a project, an honest front door classifies it first and tells you the verdict before anything builds: static converts free, a supported backend asks you to confirm, and anything we cannot run yet is refused in plain words with the fix - never a failed deploy at minute nine.

Early access. Yetty Apps is rolling out gradually. Everything on this page is the real, current contract - it is what the platform enforces today.

Quick start

1

Download the template

create-yetty-app: a plain Next.js project with the SDK vendored in, pre-checked against intake.

2

Build locally

npm install, npm run dev, npm test. The smoke + eject suite runs before you deploy anything.

3

Send it to Yetty

Push to a public repo (or zip it), point the Apps panel at it, confirm the verdict, publish when ready.

1 · Get the template

The starter is create-yetty-app: a standard Next.js (App Router, TypeScript) project with lib/yetty.ts - the vendored SDK - plus two example job routes and a runnable test suite. Download it signed in from the Apps panel, or with your API key:

curl -H "Authorization: Bearer yk_your_key" \
  -o create-yetty-app.zip https://app.yetty.ai/api/v1/apps/template

Needs a free account (the key or a login) - the template itself costs nothing.

2 · Build and test locally

npm install
npm run dev        # http://localhost:3000
npm test           # the built-in smoke + eject suite

Verbatim from apps-sdk/scaffold/README.md

The scaffold is pre-checked against Yetty's intake rules - the platform's own test suite runs the real front-door detector over it and asserts zero refusals - so what you start from sails through detection with zero warnings.

3 · Send it to Yetty

Yetty never opens or downloads your code at intake - it only notes where the app lives; the build runs later in a locked-down sandbox. In the panel:

  1. Open AppsDeploy an app.
  2. Name it, then point at a public repo URL (GitHub, GitLab or Bitbucket) or paste the upload handle of a zip you uploaded.
  3. Optionally paste your package.json (and next.config) so the tier check runs before anything is queued.
  4. Check it & queue the build. A review card shows the verdict: the tier, the framework, how it will run, and what continuing means.
  5. Confirm Run this app. The build runs; nothing goes live until you publish it.

What runs here

Next.js 15 and 16 - App Router or pages/, the standard Next server, the default Node.js runtime - deployed to Cloudflare Workers. Server API routes, server components, middleware, server actions: all normal. The app bundle ceiling is 10 MB (host big media elsewhere).

And here is the part most platforms bury: what we refuse, stated up front. Workers are request-and-response; some things simply do not run there, and pretending otherwise would waste your afternoon. When intake sees one of these, this is - verbatim - what it tells you:

  • Websockets / live socketsThis app uses live socket connections (websockets), which Yetty Apps does not run yet - it serves request-and-response HTTP only.
  • Raw TCP socketsThis app opens raw network sockets, which Cloudflare Workers do not allow. Only HTTP requests are supported.
  • Always-on daemonsThis app expects an always-on server process. Yetty Apps runs request-scoped code (Next.js), not long-running daemons.
  • A custom Next.js serverThis app ships a custom Next.js server (server.js), which the Workers runtime cannot run. Use the standard Next.js server.
  • Cron / queue librariesThis app runs scheduled jobs or a queue, which are not available yet on Yetty Apps.
  • Custom Docker imagesThis project ships a custom Docker image. Yetty Apps runs Next.js on the Cloudflare Workers runtime, not arbitrary containers.
  • MonoreposThis looks like a monorepo (multiple packages). Point us at the single app to deploy, or split it out first.
  • The Next.js edge runtimeThis app uses the Next.js "edge" runtime, which is not supported. Use the default Node.js runtime.

Two of those have a happy ending here: scheduled jobs and queues are refused as libraries because the platform gives you both natively, signed and retried - see Jobs & background work. And a classic database client is not on this list at all: it routes through a secure pool - see Databases.

A refusal is never a dead-end: everything else about the app is kept, and fixing the one flagged thing gets you through. Other frameworks (Astro, SvelteKit, Remix backends) are on the roadmap; today Yetty Apps runs Next.js, and a static site of any framework converts free.

Databases

The front door reads your dependencies and routes the database automatically. Three good paths, one honest no - in the detector's own words:

Built-in D1 - free, instant, per app

No database dependency? You get Yetty's built-in database: D1, managed SQLite at the edge, provisioned per app with nothing to configure and nothing to pay for it.

no database dependency detected - Yetty D1 (managed SQLite) is offered

Bring your own serverless database - runs as-is

Apps on Neon, Supabase, PlanetScale, Turso, Upstash, Xata or Vercel Postgres deploy unchanged - serverless (HTTP) drivers run directly on Workers, and your data stays exactly where it is.

"@neondatabase/serverless" is a serverless (HTTP) driver - it runs directly on Cloudflare Workers, keeping the app's own database

Classic Postgres / MySQL - a secure pool, coming

A classic TCP client (pg, postgres, mysql2, Prisma on Postgres/MySQL, Sequelize, TypeORM, Knex, or just a DATABASE_URL) cannot open raw sockets from Workers - so Yetty connects it through a secure connection pool (Cloudflare Hyperdrive). You paste your database address once at deploy; the password is encrypted at rest and never shown again; your driver keeps working and you keep your database. This path is rolling out now.

"pg" is a classic TCP database client - Yetty connects it through Cloudflare Hyperdrive, a secure connection pool

What has no path (yet)

MongoDB and Redis clients need raw connections Cloudflare cannot make, and a local SQLite file has no server to pool. The refusal names the fix:

"mongodb" needs a raw-TCP or local database that Cloudflare Workers cannot reach; a serverless driver (Neon, Supabase, PlanetScale, Turso, Upstash) or Yetty D1 is required

The SDK

The SDK is one file, vendored into your repo: lib/yetty.ts. It is generated into the template - you own every byte, there is no npm package between you and your app, and nothing phones home. Phase 1 ships two surfaces: yetty.jobs and yetty.env.

Jobs in 30 seconds

import { yetty } from "@/lib/yetty";

export async function POST(req: Request) {
  const call = await yetty.jobs.verify(req);   // signed? fresh? typed.
  if (!call.ok) return call.response;          // a ready 401

  await doTheWork(call.payload);
  return yetty.jobs.ok();                      // 2xx = done; retry() = try me later
}

Verbatim from apps-sdk/scaffold/README.md

yetty.jobs.verify(req) checks the HMAC signature and the 5-minute replay window and hands you a typed call; enqueue({ path, payload, runAt, maxRetries }) queues work for later; ok() and retry() are the two answers a job route gives.

Typed env access with warm errors

yetty.env(NAME) returns the value or throws an error that tells you exactly where to set the missing variable, instead of failing mysteriously three calls later:

  env(name: string): string {
    const value = process.env[name];
    if (value === undefined || value === "") {
      throw new Error(`Missing environment variable ${name}. ${adapter().envHint(name)}`);
    }
    return value;
  },

Verbatim from apps-sdk/scaffold/lib/yetty.ts

How it is put together

Your app codes against interfaces (lib/yetty/types.ts). The Yetty platform lives in exactly one file - lib/yetty/adapter-yetty.ts - and the template ships a second, Yetty-free adapter beside it. That seam is the whole eject story.

app/                        your pages + API routes (plain Next.js)
  api/cron/daily/           example schedule route
  api/jobs/send-reminder/   example deferred-job route
lib/yetty.ts                the SDK facade (import this)
lib/yetty/types.ts          the interfaces your app codes against
lib/yetty/adapter-yetty.ts  the Yetty adapter (the ONE platform-specific file)
lib/yetty/adapter-local.ts  the eject/local adapter (proof of the seam)
lib/yetty/signing.ts        shared WebCrypto HMAC helpers
tests/                      smoke + eject suite (npm test)

Verbatim from apps-sdk/scaffold/README.md

Phase 2 of the SDK adds yetty.db (D1/SQLite), yetty.files (R2/S3) and yetty.cache (KV/Redis) behind the same interface + adapter seam.

Jobs & background work

Cloudflare Workers are request-response only: no cron triggers, no queues. Yetty gives every app both anyway - with zero extra Cloudflare dependency. Yetty's own runner calls your app's HTTPS routes on schedule, and calls back deferred jobs your app queued; every call is signed with a per-app HMAC so your route can prove the request came from Yetty.

Enable it once per app (your app → Jobs → Enable in the panel). Yetty mints a signing secret and a jobs token and stages them into your app env as YETTY_JOBS_SECRET and YETTY_JOBS_TOKEN, delivered on your next deploy.

Schedules

A schedule is a name, a path on your app (like /api/cron/daily), a cadence, an IANA timezone, and a method - POST (default, with a small JSON context body) or GET (the Vercel-cron style). The runner ticks every 5 minutes, so a run lands within about 5 minutes of its slot. Up to 5 schedules per app.

CadenceMeaning
hourlyevery hour at :00
hourly@:15every hour at :15
daily@07:30every day at 07:30 in the schedule's timezone
weekly@mon 09:00every Monday 09:00 in the schedule's timezone
*/15 * * * *every 15 minutes (minimum interval: 10 minutes)
0 4 * * *daily at 04:00

An unsupported cadence shape is refused up front - never run on a guessed cadence. Daily and weekly slots follow the schedule's timezone through DST changes.

Deferred jobs

Your app queues {path, payload, run_at, max_retries}; Yetty calls the path back at run_at with the payload as the JSON body. A delivery is a success iff it answers 2xx within 30 seconds; a failure retries on a bounded backoff ladder - 1m, 5m, 30m, then 1h - up to max_retries (0-5, default 3). Every invocation is a history row (status, HTTP code, duration) kept 30 days.

Caps: 5 schedules and 100 open jobs per app, 4 KB payload, run_at at most 30 days ahead, 30s per-invocation timeout. You only ever supply a path - the host is always your app's own hostname, derived server-side, HTTPS only.

What a signed call looks like

Content-Type: application/json
X-Yetty-Jobs-Timestamp: 1756700000
X-Yetty-Jobs-Signature: sha256=<hex hmac>
X-Yetty-Jobs-Kind: schedule | job
X-Yetty-Jobs-Name: daily-digest          (schedules only)
X-Yetty-Jobs-Token: asch_... | ajob_...  (the schedule/job this call is)

Verbatim from docs/YETTY-APPS-JOBS.md

The signature is HMAC-SHA256(secret, "v1:<ts>:<path>:<sha256hex(body)>") with a 5-minute replay window. You never hand-roll that: yetty.jobs.verify(req) is the whole check.

A schedule route, from the template

/**
 * Example SCHEDULE route. Register it on Yetty with:
 *   action: "add_schedule", path: "/api/cron/daily", cadence: "daily@07:30"
 * (or run it locally by POSTing with "Authorization: Bearer $LOCAL_JOBS_SECRET"
 * under YETTY_ADAPTER=local - same code, different adapter.)
 */
import { yetty } from "@/lib/yetty";

export async function POST(req: Request) {
  const call = await yetty.jobs.verify(req);
  if (!call.ok) return call.response;

  // call.kind === "schedule" for a tick; call.payload carries the tick context.
  // Your daily work goes here. Need heavier work later? Queue it:
  //   await yetty.jobs.enqueue({ path: "/api/jobs/send-reminder",
  //     payload: { userId: "u_123" }, runAt: new Date(Date.now() + 3600_000) });

  return yetty.jobs.ok({ ran: "daily", at: call.now });
}

// GET variant (the Vercel-cron style; the signature covers an empty body).
export async function GET(req: Request) {
  const call = await yetty.jobs.verify(req);
  if (!call.ok) return call.response;
  return yetty.jobs.ok({ ran: "daily", at: call.now });
}

Verbatim from apps-sdk/scaffold/app/api/cron/daily/route.ts

A deferred-job route, from the template

Note the two failure modes: a permanent problem answers ok() so a bad job is never retried; a transient one answers retry() to hit the backoff ladder.

/**
 * Example DEFERRED-JOB route. Yetty (or the local adapter) delivers a job you
 * enqueued back to this path with the payload as the JSON body.
 */
import { yetty } from "@/lib/yetty";

type ReminderPayload = { userId?: string; template?: string };

export async function POST(req: Request) {
  const call = await yetty.jobs.verify(req);
  if (!call.ok) return call.response;

  const payload = (call.payload ?? {}) as ReminderPayload;
  if (!payload.userId) {
    // A permanent problem: answer 2xx so the runner does NOT retry a bad job.
    return yetty.jobs.ok({ skipped: "no userId in payload" });
  }

  // Your delivery logic goes here. A transient failure (mail server down)
  // should `return yetty.jobs.retry("mail server down")` to hit the backoff
  // ladder instead of dropping the job.

  return yetty.jobs.ok({ reminded: payload.userId });
}

Verbatim from apps-sdk/scaffold/app/api/jobs/send-reminder/route.ts

Going live

Building is free. The template, the SDK, local dev, the test suite, sending your app through intake and seeing the honest verdict - none of that asks for a card. The paid gate sits exactly where value starts flowing: publishing your app to a live URL.

A live app runs on the Yetty Apps plan - $19/month per app, custom domain included. Current numbers and what ships in each plan live on the pricing page.

Publishing is explicit: a built app sits in preview until you press publish, and deploys are kept as history. Your app gets its own address on yetty.site, or your domain.

The eject story

Build 3x faster. Leave any time. We say this proudly, because platforms that are confident you'll stay do not need to lock the doors.

  • Your source was never captive. Builds pull from your GitHub repo or your zip. The code was in your hands the whole time.
  • The SDK is vendored. lib/yetty.ts lives in your repo, not in a registry we control. You own the bytes from minute one.
  • The platform is one file. App code imports interfaces only; everything Yetty-specific is lib/yetty/adapter-yetty.ts. Ejecting = swapping the adapter file; zero app-code changes.
  • And it is tested, not promised. The template ships a second, Yetty-free adapter, and npm test runs your example routes against BOTH adapters with identical app code - the eject suite is part of the scaffold you download.

Run your app with no Yetty anywhere in the loop, today, on your laptop:

YETTY_ADAPTER=local LOCAL_JOBS_SECRET=dev-secret LOCAL_APP_URL=http://localhost:3000 npm run dev

Verbatim from apps-sdk/scaffold/README.md

If you ever leave: keep the facade, keep your routes, swap the adapter, and point whatever scheduler your new host gives you (system cron, node-cron, your platform's cron) at the same routes. What stays behind is only Yetty's services - the signed runner, the job history, the panel - and the seams make replacing them mechanical, not a rewrite.

Get help

  • The developer hub - why builders and agencies deliver on Yetty, and the MCP toolbox.
  • Main docs - the static-site platform: publish, CMS, leads, SEO, versions, MCP.
  • Connect your AI - copy-paste MCP setup for Claude, Cursor, ChatGPT and more.
  • Contact us - a human reads it.
ابنِ على Yetty

سلّم الموقع، وانقل الملكية، وقده من ذكائك الاصطناعي.

Bring a backend. Keep your exits.

Download the template, run the eject test, and send us the app - it builds free, and it goes live only when you say so.