api

$npx mdskill add vercel-labs/hatchery/api

Create `api/<path>/route.py` to publish `/api/<path>` after the workspace reaches `main`. Export one or more uppercase HTTP functions: `GET`, `POST`, `PUT`, `PATCH`, or `DELETE`. Each receives `hatchery.sdk.Request` and may return `hatchery.sdk.Response`, `HTMLResponse`, `JSONResponse`, a JSON-compatible dict/list, text, bytes, or `None`.

SKILL.md

.github/skills/apiView on GitHub ↗
---
name: api
description: Publish HTTP handlers for external webhooks and callbacks on this agent's subdomain.
---

# API routes

Create `api/<path>/route.py` to publish `/api/<path>` after the workspace reaches
`main`. Export one or more uppercase HTTP functions: `GET`, `POST`, `PUT`, `PATCH`,
or `DELETE`. Each receives `hatchery.sdk.Request` and may return
`hatchery.sdk.Response`, `HTMLResponse`, `JSONResponse`, a JSON-compatible
dict/list, text, bytes, or `None`.

```python
"""Receive a completed external job."""

from hatchery.sdk import Response, prompt


def POST(request):
    event = request.json()
    prompt(
        f"External job {event['id']} completed; inspect its result.",
        key=f"job:{event['id']}",
    )
    return Response({"accepted": True}, status=202)
```

## Response formats

Return a dict or list for ordinary JSON, or use `JSONResponse` when the response
should be explicitly JSON or contains another JSON-compatible value. A plain string
is served as `text/plain`; HTML must use `HTMLResponse` so browsers render it instead
of displaying its source.

```python
from hatchery.sdk import HTMLResponse


def GET(request):
    return HTMLResponse(
        """<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>Hello</title></head>
<body><h1>Hello from Hatchery</h1></body>
</html>"""
    )
```

Both response helpers accept `status=` and repeated `headers=[(name, value), ...]`.
Use `Response` for plain text or bytes and when setting another content type
explicitly.

Use `[name]` for one dynamic path segment and `[...name]` for a final catch-all.
Handlers run with `HATCHERY_DATA` as their working directory and `HOME`, so relative
files and SQLite databases persist across requests, deployments, and sandbox
stop/resume. This state is not Git-backed or replicated and is lost if the serve
sandbox is destroyed. Put shared pinned Python dependencies in root `requirements.txt`.

For webhook signing keys or private API tokens, call
`secret_request(NAME, note)` and tell the operator where to obtain or enter the
value. The value appears to handlers as environment variable `NAME`; you cannot read
it through the tool. Never write secrets into workspace files because Git and other
agents can read them.

Routes are public unless your handler validates a provider signature or authorization
header. Respond quickly and use `prompt()` only when an event needs agent judgment;
irrelevant webhook events should return `204` without waking a thread.

## Prompt identity and conversation routing

Keep these three identities separate:

- `request.id` identifies this HTTP delivery. It comes from `X-Request-ID` when the
  caller supplies one; otherwise Hatchery generates it.
- `prompt(..., key=...)` identifies the external event for idempotency. Reusing a key
  means “this is a retry” and suppresses another Agent message. Omit it to use
  `<request.id>:<effect-position>`, or use a provider event ID such as
  `key=f"stripe:{event['id']}"`. **Never use one constant key for different events.**
- `prompt(..., thread=...)` groups different events into an Agent conversation. It is
  a stable logical alias, not a Rotor thread ID. Omit it to group by request path, so
  every `/api/callback` hit naturally continues one conversation. Set it when events
  need another partition, such as `thread=f"customer:{customer_id}"`. Do not generate
  or persist a thread ID in a file.

Common forms:

```python
# Each HTTP request is delivered once; all hits to this route share a conversation.
prompt(f"Callback: {request.json()}")

# Provider retries of one event are suppressed; distinct events share the route thread.
prompt(f"Stripe event: {event}", key=f"stripe:{event['id']}")

# Distinct events are grouped into one conversation per customer.
prompt(
    f"Customer event: {event}",
    key=f"provider:{event['id']}",
    thread=f"customer:{event['customer_id']}",
)
```

A successful HTTP response does not mean a duplicate prompt created another message;
webhook retries still receive success. When testing, send different `X-Request-ID`
values for distinct events and repeat one value to verify deduplication.

Request query and header collections preserve repeated values and serialize as lists;
use `.get(name)` or `.get_all(name)` for lookup.

More from vercel-labs/hatchery

SkillDescription
authAuthentication integration guidance — Clerk (native Vercel Marketplace), Descope, and Auth0 setup for Next.js applications, plus Sign in with Vercel, Vercel Passport, and Vercel KMS. Covers proxy.ts auth patterns, sign-in/sign-up flows, and Marketplace provisioning. Use when implementing user authentication or protecting deployments.
cdn-cachingDebug Vercel CDN caching — cache hit rate, stale content, revalidation behavior, ISR + PPR, per-request cache reasons (cacheReason) and PPR state (ppr_state), and costs.
codingChange code in a repository safely - explore, plan, edit, verify.
create-a-backendBackend architecture guidance. Use when planning, building, or migrating an API or backend; choosing between Functions, Services, containers, Workflow, Queues, and Marketplace databases; or selecting a supported backend framework or runtime.
marketplaceVercel Marketplace expert guidance — discovering, installing, and managing third-party integrations via the `vercel integration` CLI. Use when building any app that needs an external capability without a dedicated skill — commerce (stores, storefronts, selling products), payments (checkout, subscriptions, billing), observability/monitoring, messaging/email, search, or CMS — or when discovering, installing, or managing integrations.
neon>-
neon-postgres>-
queuesVercel Queues guidance — durable topics with at-least-once delivery, independent consumer groups, retries, delays, and idempotency keys via @vercel/queue (JS) or vercel-queue (Python). Use when deferring background work, buffering traffic, fanning out events, or choosing between Queues and Workflows.
schedulesRun Python jobs on durable cron or interval schedules from this agent's workspace.
vercel-cliVercel CLI expert guidance. Use when deploying, managing environment variables, linking projects, viewing logs, querying metrics, managing domains, managing feature flags with vercel flags, or interacting with the Vercel platform from the command line.