# Memeforge API v1 — agent reference

Base URL: `https://memeforge-ai.nonhumanagent.workers.dev`
Spec (machine-readable): `GET /v1/openapi.json` (OpenAPI 3.1, keyless)
Human docs: https://memeforge-8dq.pages.dev/api.html

## Auth

All `/v1/*` except `/v1/health` and `/v1/openapi.json` require a key:

```
Authorization: Bearer mf_live_...
```

Bad/missing key → `401 {"error":{"code":"unauthorized","message":"..."}}`.
All errors are JSON shaped `{"error":{"code":"<snake_case>","message":"<human>"}}`.

## Endpoints

| Method | Path | Key? | What |
|---|---|---|---|
| GET | /v1/health | no | status, version, daily budget usage |
| GET | /v1/openapi.json | no | full OpenAPI 3.1 spec |
| GET | /v1/templates | yes | 60 meme templates; `?q=` searches names |
| GET | /v1/usage | yes | this key's quota + usage today |
| POST | /v1/images/backdrop | yes | text prompt → fresh meme backdrop image |
| POST | /v1/images/restyle | yes | prompt + image → AI-reimagined image |
| POST | /v1/memes/caption | yes | **magic prompt:** AI captions an asset (template/URL/upload) and renders the finished meme in Impact style (SVG). Supply `top`/`bottom` to skip the AI writer (free) |
| GET | /v1/filters | yes | list image filters (style presets; face swaps locked) |
| POST | /v1/images/filter | yes | apply a style filter (`wojakify`, `gigachad`, `deepfry`, …) to an image |

## Image responses

Default: raw image bytes (`Content-Type: image/jpeg` or `image/png` — always
check the header, never assume). With `Accept: application/json` (or
`?format=json`) you get a JSON envelope instead:

```json
{"image_base64":"...","content_type":"image/jpeg","model":"@cf/black-forest-labs/flux-1-schnell","ms":1740}
```

## Examples

```bash
KEY="mf_live_..."; B="https://memeforge-ai.nonhumanagent.workers.dev"

# health (keyless)
curl -s $B/v1/health

# search templates
curl -s -H "Authorization: Bearer $KEY" "$B/v1/templates?q=drake"

# generate a backdrop (raw bytes → file)
curl -s --max-time 200 -X POST $B/v1/images/backdrop \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"prompt":"a cat typing on a laptop at 3am, dramatic lighting"}' \
  -o backdrop.jpg

# restyle an image (data URL input, JSON envelope back)
IMG=$(python3 -c "import base64;print('data:image/jpeg;base64,'+base64.b64encode(open('photo.jpg','rb').read()).decode())")
curl -s --max-time 200 -X POST $B/v1/images/restyle \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d "{\"prompt\":\"cyberpunk neon style\",\"image\":\"$IMG\"}"

# magic prompt — AI captions a template and renders the finished meme.
# returns SVG by default (image/svg+xml); use Accept: application/json
# for an envelope with svg_base64 + the caption texts.
curl -s --max-time 200 -X POST $B/v1/memes/caption \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"prompt":"me looking at my bank account after saying treat yourself","q":"drake"}'

# same, but your own captions (skips the AI — free, no quota spent)
curl -s -X POST $B/v1/memes/caption \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"template_id":"181913649","top":"when the code compiles","bottom":"first try"}' \
  -o meme.svg

# list style filters (Memelord-style; face swaps are listed but locked)
curl -s -H "Authorization: Bearer $KEY" $B/v1/filters

# apply the wojakify filter to an image
IMG=$(python3 -c "import base64;print('data:image/jpeg;base64,'+base64.b64encode(open('photo.jpg','rb').read()).decode())")
curl -s --max-time 200 -X POST $B/v1/images/filter \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d "{\"filter_id\":\"wojakify\",\"image\":\"$IMG\"}" -o wojak.jpg

# quota check
curl -s -H "Authorization: Bearer $KEY" $B/v1/usage
```

## Rules for agents

- **Timeouts:** generation is synchronous and takes 30–120s. Set client
  timeouts to **180s+** and do not retry on slow responses — a retry spends
  quota twice.
- **Prompt:** 1–300 chars. Restyle `image`: `data:image/...;base64,...`,
  max ~2.5MB decoded.
- **Quotas:** 50 generations/day/key (free tier). `429` means quota or the
  global daily budget (150) is spent — read `error.code`
  (`quota_exceeded` vs `daily_budget_exhausted`) and back off until tomorrow.
- **CORS:** `*` on `/v1/*` — callable from browsers and servers alike.
- **Honest limits:** `restyle` reimagines the image from your prompt — it is
  not a face-swap and will not preserve identity. Backdrops are AI-generated
  scenes, not template memes; overlay captions yourself or via `/v1/templates`.
- **Beta metering:** per-key counters are best-effort per edge node; the
  global daily budget is enforced exactly.
