Palette API
All endpoints are JSON unless noted. The web UI is served from the same port.
Create paste
curl -X POST http://localhost:8080/api/pastes \
-H "Content-Type: application/json" \
-d '{
"content": "print(hello)",
"title": "my snippet",
"language": "python",
"expires_in": "168h",
"password": "optional",
"custom_slug": "optional",
"burn_after_read": false,
"burn_after_reads": 1,
"visibility": "public"
}'
expires_inis a Go duration string (90m,6h,336h). Omit for no expiry.visibilityispublicorunlisted.- Alternatively (or additionally),
publicmay be sent as a boolean (#83):falsemaps tounlistedandtruemaps topublic. When both fields are present, the booleanpublictakes precedence over the stringvisibility. Omitting both defaults topublic. burn_after_readssets how many reads the paste survives (default 1 whenburn_after_readis true). A read is counted per unique viewer session; the same viewer returning within 15 minutes does not count again.- Response includes
id,url,raw_url,api_url,expires_at,created_at, and a one-timedeletion_token.
Errors return JSON with a human-readable error message plus a
machine-readable code the web UI maps to plain-language guidance (#105):
| Code | Status | Meaning |
|---|---|---|
content_empty |
400 | content is required |
slug_invalid |
400 | custom slug malformed |
slug_taken |
400 | custom slug already in use |
slug_reserved |
400 | custom slug is reserved |
expiry_invalid |
400 | expires_in out of 1 minute – 1 year range |
content_too_large |
413 | content or body exceeds the size cap |
rate_limited |
429 | too many requests; see Retry-After; see Security Notes for limiter details |
Other statuses: 400 invalid body, 401 password required, 404 paste
expired/burned/gone. Unknown codes should be treated as a generic failure.
Get paste
curl http://localhost:8080/api/pastes/{id}
# password-protected pastes:
curl "http://localhost:8080/api/pastes/{id}?password=secret"
# or via header: X-Paste-Password: secret
The response includes reads_remaining (null when no read budget is set).
Raw content
curl http://localhost:8080/raw/{id}
Raw reads count against a burn-after-read budget, same as page views.
Delete
# soft delete (requires the deletion token from the create response)
curl -X DELETE -H "Authorization: Bearer TOKEN" http://localhost:8080/api/pastes/{id}
# ...or via query param; the creator browser (viewer cookie) may also delete without a token
curl -X DELETE "http://localhost:8080/api/pastes/{id}?token=TOKEN"
# hard delete immediately (requires the one-time deletion token)
curl -X DELETE "http://localhost:8080/api/pastes/{id}/redeem?token=TOKEN"
Lists
curl "http://localhost:8080/api/public?limit=25&offset=0" # public history
curl http://localhost:8080/api/mine # this browser's pastes (viewer cookie)
Language detection
curl -X POST http://localhost:8080/api/guess-language \
-H "Content-Type: application/json" \
-d '{"content": "package main"}'
Cans (bundles of items)
A can bundles multiple text items (and files) into one shareable page at
/can/{id}. Password, expiry, visibility, custom slug, and viewer-scoped
deletion work exactly like pastes; cans appear as normal rows (with a can
badge) in /api/public and /api/mine.
curl -X POST http://localhost:8080/api/pastes/can \
-F "title=My bundle" \
-F "expires_in=48h" \
-F "custom_slug=my-bundle" \
-F 'json_items=[{"title":"notes.txt","content":"some notes"}]' \
-F "files=@screenshot.png" \
-F "files=@log.txt"
curl http://localhost:8080/api/cans/{id}
curl http://localhost:8080/api/cans/{id}/items/{item_id}
curl -X DELETE http://localhost:8080/api/cans/{id} # creator browser only (vwr cookie)
Password-protected cans use the same unlock flow as pastes: POST /can/{id}
with the password sets an HMAC-bound cookie; item fetches accept the cookie as
well as the X-Paste-Password header / ?password= query param.
Web pages
/new— create a paste/history— public paste history/mine— pastes created from this browser/{id}— view a paste/unlock/{id}— password gate for protected pastes/raw/{id}— raw content with original content type
Expiry and deletion
- Expired pastes are soft-deleted by a background sweeper (runs every minute).
- Soft-deleted pastes are hard-deleted after a 7-day grace period.
- Deletion tokens allow immediate hard delete.
- Burn-after-read pastes are soft-deleted once the read budget is exhausted.