whyc · sdk only

Three lines.
One deployed preview.

whyc.generate(yc_url) is a single SDK call. It hands a job-posting URL to a Gemini ADK pipeline, runs a Phoenix-traced self-improvement loop, and returns a live Cloud Run URL — usually before they finish writing the JD for round two.

$ npm i whyc  # or: pip install whyc copy
ship.ts typescript
import { whyc } from "whyc";

const job = await whyc.generate({
  url: "https://workatastartup.example/jobs/82957",
  webhook: "https://your.app/hooks/whyc",
  converge: { specFit: 0.92 }   // stop when judge agrees
});

// → { jobId: "wh_4Tq…", statusUrl: "…", previewUrl: null }
// 24h later, webhook fires with { previewUrl, specFit: 0.96 }
quickstart

Call it. Forget about it. Read the webhook.

The SDK is async-by-default. A single generate() call returns a job handle in milliseconds. The convergence loop runs for hours — your host app is free to render whatever UI it wants while it waits.

request curl
curl https://api.whyc.dev/v1/generate \
  -H "authorization: bearer $WHYC_KEY" \
  -H "content-type: application/json" \
  -d '{
    "url": "https://workatastartup.example/jobs/82957",
    "webhook": "https://your.app/hooks/whyc",
    "converge": { "specFit": 0.92 }
  }'
response · 202 Accepted json
{
  "jobId": "wh_4TqMv8nE2xK",
  "status": "queued",
  "statusUrl": "https://api.whyc.dev/v1/jobs/wh_4TqMv8nE2xK",
  "previewUrl": null,
  "specFit": null,
  "trace": {
    "phoenixProject": "whyc-prod",
    "rootSpan": "whyc.generate.wh_4TqMv8nE2xK"
  },
  "eta": "~22h"
}
lifecycle

API call → webhook → background loop.

One synchronous call. One async webhook. Zero dashboards required — the host app decides whether to render anything at all.

01 · SYNC
generate() returns a handle
202 in <200ms with a jobId, statusUrl, and a Phoenix root span you can subscribe to from your own observability stack.
02 · BACKGROUND
ADK pipeline ships v0
URL → spec → Next.js code → Cloud Run. First preview is live and reachable in roughly 10 minutes at ~71% spec-fit.
03 · LOOP
Phoenix-judged regeneration
LLM-as-judge reads OpenInference traces, scores under-spec flows, regenerates only the weak ones. 71 → 84 → 92 → 96.
04 · WEBHOOK
job.converged fires
POST to your webhook with { previewUrl, specFit, traceUrl, judgmentLog }. Ship it, embed it, mock it — your call.
examples

Embed it where the work already happens.

WhyC owns no surface beyond this docs page. Every example below is a different host app pulling the SDK in unchanged. The product is the package.

VC deal-flow CLI
node · diligence script
for (const co of portfolio) {
  const job = await whyc.generate({
    url: co.jdUrl,
    webhook: slack(co.channel)
  });
  log(co.name, job.jobId);
}
Founder's personal site
next.js · server action
"use server";
export async function roast(url: string) {
  return whyc.generate({
    url,
    webhook: "/api/whyc/done",
    converge: { specFit: 0.96 }
  });
}
Slack bot
python · slash command
from whyc import client
def on_slash(cmd):
    job = client.generate(
        url=cmd.text,
        webhook=cmd.response_url
    )
    return f"shipping… {job.job_id}"
webhook

One event. Everything you need.

When the convergence threshold is hit (or 24h wall-clock fires, whichever first), WhyC posts a single signed payload. No SSE channel, no client lib state, nothing to embed.

POST · your.app/hooks/whyc json
{
  "event":    "job.converged",
  "jobId":    "wh_4TqMv8nE2xK",
  "previewUrl": "https://wh-4tqmv8.run.app",
  "specFit":   0.96,
  "iterations": 4,
  "latencyMs":  79420318,         // ~22h, well under the 24h headline
  "judgment": {
    "flowsRegenerated": ["signup", "pricing", "docs"],
    "finalVerdict":     "matches extracted spec; ship it."
  },
  "trace": {
    "phoenixUrl": "https://phoenix.arize.com/projects/whyc/spans/…"
  }
}