3
API
fen edited this page 2026-09-10 11:13:34 -05:00
This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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_in is a Go duration string (90m, 6h, 336h). Omit for no expiry.
  • visibility is public or unlisted.
  • Alternatively (or additionally), public may be sent as a boolean (#83): false maps to unlisted and true maps to public. When both fields are present, the boolean public takes precedence over the string visibility. Omitting both defaults to public.
  • burn_after_reads sets how many reads the paste survives (default 1 when burn_after_read is 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-time deletion_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.