Files
palette/docs/API.md
T
poslop 9df6224a27
CI / test (push) Successful in 23s
CI / docker (push) Skipped
Result box: color-code by status + friendly error messages from error codes (#105)
- Backend create/can validation paths emit machine-readable error codes
  (slug_taken, slug_invalid, content_empty, content_too_large,
  expiry_invalid, rate_limited, ...) alongside the human message
- new.html JS maps codes to plain-language guidance with generic fallback
- Result card colored via --ok/--err/--warn left border (result-ok/err/warn)
- docs/API.md error section documents the code field
- Tests assert the code on every validation path
2026-09-09 17:28:25 -05:00

4.6 KiB
Raw Blame History

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/409 custom slug malformed
slug_taken 409 custom slug already in use
slug_reserved 409 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

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.