REST API
Base path: /api/v1
Automate Webklip with plain HTTP — no account required for anonymous clips.
All clip endpoints accept and return JSON unless noted. Legacy server-PIN clips
require the X-Clip-Pin header or a ?pin= query parameter.
Passphrase E2E clips return ciphertext; decrypt locally with the passphrase (the API never
receives it).
Quick start
curl -s -X POST https://webklip.com/api/v1/clips/my-clip \
-H "Content-Type: application/json" \
-d '{"content":"hello from the API","ttl":3600}'
curl -s https://webklip.com/api/v1/clips/my-clip
Health check
/api/health
Returns service status.
{ "status": "ok", "version": "0.3.0" }
Read a clip
/api/v1/clips/:slug
Fetches clip content. Counts as a read for burn-on-read clips and view limits. Link-preview crawlers receive a preview response without consuming a read.
Response (200):
{
"slug": "my-clip",
"content": "hello",
"contentType": "text",
"expiresAt": 1720000000,
"burnOnRead": false,
"maxViews": null,
"viewCount": 1,
"webhookUrl": null,
"encrypted": false,
"e2eSalt": null,
"e2eWrappedKey": null,
"e2eKdf": null
}
After a burn-on-read clip is consumed, the final response includes "burned": true.
When encrypted is true, content is ciphertext and
e2eSalt / e2eWrappedKey / e2eKdf let a client unwrap the
content key from the passphrase.
Errors: 400 invalid slug, 401 legacy PIN required, 404 not found, 429 rate limited.
Create a clip
/api/v1/clips/:slug
Creates a new clip. Returns 409 if the slug already exists.
JSON body (all fields optional):
{
"content": "secret note",
"burnOnRead": true,
"ttl": 3600,
"maxViews": 3,
"pin": "1234",
"webhook": "https://example.com/hook",
"visibility": "private",
"ownerPassword": "optional-owner-secret"
}
burnOnRead— defaults tofalse. Settruefor burn-after-read (deletes on first read; unread clips still expire after 7 days).ttl— seconds until expiry (default900= 15 min; max 1 year).maxViews— delete after N API reads (0= unlimited).pin— legacy server PIN gate (plaintext at rest). Prefer passphrase E2E in the web UI for true encryption.webhook— URL notified on read, burn, or expiry (see Webhooks).visibility—private(default) orpublic(listed on Klipwall; requiresownerPassword).ownerPassword— recovers edit access on a new device (min 8 characters; required for public clips).-
content— max 1,000,000 characters (includes multi-tab workspace JSON when used).
Plain-text body is also accepted (Content-Type: text/plain) as the clip content.
Response (201):
{
"slug": "my-clip",
"content": "secret note",
"maxViews": 3,
"webhookUrl": "https://example.com/hook",
"pinSet": true
}
Update a clip
/api/v1/clips/:slug
Replaces clip content. Returns 404 if the clip does not exist. May return 403 when write access is restricted.
curl -X PUT https://webklip.com/api/v1/clips/my-clip \
-H "Content-Type: application/json" \
-d '{"content":"updated text"}'
Delete a clip
/api/v1/clips/:slug
Permanently deletes the clip, attached files, and version history.
{ "ok": true }
Download a file
/api/v1/files/:slug/:fileId
Returns the raw file bytes (Content-Disposition: inline) so the clip UI can preview images, PDFs, text, and media in a modal, or download them.
Requires legacy PIN header/query when the clip uses a server PIN.
Attach files from the clip UI by dropping, browsing, or pasting an image from the clipboard (Ctrl+V / Cmd+V).
Limits: 10 files per clip, 10 MB per file, 50 MB total (env
MAX_FILE_SIZE_MB / MAX_TOTAL_FILES_MB).
File uploads are disabled on passphrase E2E clips. File IDs are shown in the clip UI after upload.
Rate limits
API responses include X-RateLimit-Remaining. When the limit is exceeded the
server returns 429 with { "error": "Rate limit exceeded" }.