REST API

Publish with a POST.

The whole page lifecycle over plain HTTP: publish, list, read stats, update, delete, and read form submissions. One token, curl-friendly, no SDK.

01 What this is

Everything you can do from a chat with the MCP connector, you can also do from a script. Same account, same plan limits, same URLs, just plain JSON over HTTPS.

Reach for the API when you want to publish from a backend, a CI job, a cron task, or a one-line curl. Reach for MCP instead when you want an AI assistant to build and publish pages for you in chat, or when you need the drag-and-drop photo upload slots (those need a browser).

02 Authenticate

Every request carries a Personal Access Token in the Authorization header.

  1. Sign in, then create a token in Settings, Connect AI clients. It's shown once, so copy it.
  2. Send it as a bearer token on every call: Authorization: Bearer yapp_pat_…
export YAPP_TOKEN=<your token>
Keep the token secret. It has the same access as your account. Revoke it any time in Settings, and it stops working on the next request.

03 Base URL

All endpoints live under one versioned base.

https://yapp.page/api/v1

Published pages get their own subdomain, so a page's live URL is https://<id>.yapp.page/, not a path under the API.

04 Endpoints

Seven calls cover the full lifecycle, including image upload. Responses are JSON.

POST /pages

Publish a new page. Send exactly one content source: html (inline), htmlUrl (a public https URL we fetch byte-for-byte, best for large files), or fileBase64 + mime (host a PDF, image, or ZIP as-is). Optional: filename, name, slug, expires (days), password.

curl -X POST https://yapp.page/api/v1/pages \ -H "Authorization: Bearer $YAPP_TOKEN" \ -H "Content-Type: application/json" \ -d '{"html":"<h1>Hello world</h1>","name":"My page"}'
201 Created
{ "id": "k3n8x2", "url": "https://k3n8x2.yapp.page/", "editKey": "e_9f…" }

Big document or one you already host? Skip inline HTML and let yapp fetch it:

-d '{"htmlUrl":"https://example.com/report.html","slug":"q3-report","expires":30}'
POST /pages/:id/assets

Upload an image (or any static file) straight from disk. One multipart file field, no URL and no base64 needed. The name defaults to the uploaded filename, or set name to control the path (e.g. images/hero.jpg). Uploading a name that already exists replaces it. Reference the file from your HTML by that name.

curl -X POST https://yapp.page/api/v1/pages/k3n8x2/assets \ -H "Authorization: Bearer $YAPP_TOKEN" \ -F "[email protected]"
200 OK
{ "ok": true, "name": "hero.jpg", "size": 84213, "url": "https://k3n8x2.yapp.page/hero.jpg" }

Typical flow: publish the HTML that references hero.jpg, then upload the file. It appears on the live page instantly.

GET /pages

List your active pages, newest first.

curl https://yapp.page/api/v1/pages \ -H "Authorization: Bearer $YAPP_TOKEN"
200 OK
{ "count": 1, "pages": [ { "id": "k3n8x2", "name": "My page", "url": "https://k3n8x2.yapp.page/", "views": 128, "expiresAt": "2026-08-01T00:00:00.000Z", "hasForm": false, "submissions": 0, "hasPassword": false } ] }
GET /pages/:id/stats

Traffic stats for one page. Human traffic only, bot hits are filtered out. Detailed breakdowns (referrers, countries, devices) require the Pro plan.

curl https://yapp.page/api/v1/pages/k3n8x2/stats \ -H "Authorization: Bearer $YAPP_TOKEN"
200 OK
{ "id": "k3n8x2", "views": { "total": 128, "today": 4, "week": 31, "month": 128 }, "visitors": 96, "referrers": { "google.com": 40 }, "countries": { "US": 55, "DE": 20 }, "devices": { "desktop": 70, "mobile": 26 }, "botHitsFiltered": 12, "expiresAt": "2026-08-01T00:00:00.000Z" }
PATCH /pages/:id

Change any subset in one call. Replace content with html or htmlUrl. Set name, expires (a number of days or "forever"), password (a string to set, or null to remove), and slug (the page moves to the new URL, the old one stops working).

curl -X PATCH https://yapp.page/api/v1/pages/k3n8x2 \ -H "Authorization: Bearer $YAPP_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Launch","expires":90,"html":"<h1>Updated</h1>"}'
200 OK
{ "id": "k3n8x2", "url": "https://k3n8x2.yapp.page/" }

Password protection and custom expiry are Pro features. A few common single-field edits:

-d '{"slug":"launch-2026"}' # move to launch-2026.yapp.page -d '{"password":"s3cret"}' # gate the page -d '{"password":null}' # make it public again -d '{"expires":"forever"}' # stop auto-delete
DELETE /pages/:id

Delete a page permanently. The URL stops working and form submissions are removed. This cannot be undone.

curl -X DELETE https://yapp.page/api/v1/pages/k3n8x2 \ -H "Authorization: Bearer $YAPP_TOKEN"
200 OK
{ "ok": true }
GET /pages/:id/submissions

Read form submissions captured from a page, newest first. yapp auto-captures any <form> on a page. Add ?limit= (1 to 200, default 50).

curl "https://yapp.page/api/v1/pages/k3n8x2/submissions?limit=50" \ -H "Authorization: Bearer $YAPP_TOKEN"
200 OK
{ "count": 1, "submissions": [ { "createdAt": "2026-07-09T10:00:00.000Z", "data": { "email": "[email protected]" } } ] }
Photos and assets. Three ways to get images onto a page: upload the file directly with POST /pages/:id/assets (shown above, the simplest), point your HTML at a public https URL, or bundle a whole site as a ZIP via fileBase64. The drag-and-drop drop-slots (for uploading a file from inside a chat) are the MCP connector's browser flow; over HTTP you just POST the file.

05 Errors & limits

Errors return a JSON body: { "error": "message", "code": "PLAN_LIMIT" }. The code field is present on some errors (like plan limits) and absent on others.

StatusMeans
400Bad request, invalid slug, missing content, or a malformed body.
401Missing, invalid, or revoked token.
402Plan limit reached (for example, password or extra pages on the free plan). Carries code.
403The token's account does not own this page.
404No such page.
409That slug is already taken.
413Content too large (12 MB max per request).
429Rate limited. The API allows 60 requests per minute per IP.

Prefer publishing from a chat? See Use with AI for the MCP connector.