API
Every endpoint speaks JSON. Errors return { "error": "...", "code": "..." } with the right HTTP status. Authenticate with Authorization: Bearer <owner_token | api_key>.
Endpoints
| Method & path | Auth | Consumes view? | Purpose |
|---|---|---|---|
POST /api/jot | optional API key | n/a | Create a jot. Optional password protects it. Returns id, url, raw_url, owner_token (shown once), expires_at. |
GET /api/jot/{id}/meta | none | No | Metadata only (no body), incl. is_password_protected. The "look before you consume" call. |
POST /api/jot/{id}/unlock | none | No | Verify a password (body { "password": "..." }) → returns an unlock_token. Throttled. |
GET /api/jot/{id} | owner optional | Yes (burn/limit) | JSON including content. Owner (Bearer) reads never consume. A password-protected jot returns 401 until unlocked (no view consumed). |
GET /raw/{id} | owner optional | Yes (burn/limit) | text/plain body, hardened headers. |
PATCH /api/jot/{id} | owner token or API key | No | Update content/settings. |
DELETE /api/jot/{id} | owner token | No | Delete the jot now. Returns 204. |
POST /api/key | none (PoW) | n/a | Mint an API key with no registration. |
GET /api/jots | API key | No | List the jots created with this key. |
GET /healthz | none | n/a | Returns { status, version }. |
Create a jot
curl -X POST https://jotary.com/api/jot \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: my-unique-key-123' \
-d '{"content":"hello world","syntax":"plain","expiration":"1d","view_limit":1,"exposure":"unlisted"}'Python
import requests
r = requests.post(
"https://jotary.com/api/jot",
headers={"Idempotency-Key": "my-unique-key-123"},
json={"content": "hello world", "syntax": "plain", "expiration": "1d"},
)
data = r.json()
print(data["url"], data["owner_token"])Node
const res = await fetch("https://jotary.com/api/jot", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Idempotency-Key": "my-unique-key-123",
},
body: JSON.stringify({ content: "hello world", syntax: "plain", expiration: "1d" }),
});
const data = await res.json();
console.log(data.url, data.owner_token);Read a jot
Use /meta to inspect without consuming a view, then read content when you mean it. For burn/view-limited jots, a content read (GET /api/jot/{id} or /raw) consumes a view; an owner (Bearer) read never consumes.
# Inspect without consuming a view:
curl https://jotary.com/api/jot/ABC12345/meta
# Read content (consumes a view for burn/view-limited jots):
curl https://jotary.com/api/jot/ABC12345
# Raw text:
curl https://jotary.com/raw/ABC12345Password protection
Pass password at create time to protect a jot. A protected jot's content surfaces (GET /api/jot/{id}, /raw, and the HTML reader) return 401 until you unlock — no view is consumed while locked, so a burn/view-limited jot survives a failed or absent unlock. Unlock with POST /api/jot/{id}/unlock to receive a short-lived unlock_token; re-present it on reads via the X-Unlock-Token header (agents) — browsers receive an unlock_{id} cookie automatically. The owner can change or clear the password with PATCH (clearing = an empty password); any change invalidates outstanding unlock tokens.
# Create a password-protected jot:
curl -X POST https://jotary.com/api/jot \
-H 'Content-Type: application/json' \
-d '{"content":"top secret","password":"hunter2"}'
# A protected read is 401 until you unlock (no view is consumed):
curl -i https://jotary.com/api/jot/ABC12345 # -> 401
# Unlock with the password to get a short-lived unlock_token:
curl -X POST https://jotary.com/api/jot/ABC12345/unlock \
-H 'Content-Type: application/json' \
-d '{"password":"hunter2"}'
# -> {"unlock_token":"ABC12345.<exp>.<ver>.<sig>","expires_at":<ms>}
# Re-present the token on reads via the X-Unlock-Token header:
curl https://jotary.com/api/jot/ABC12345 \
-H 'X-Unlock-Token: <unlock_token>'
# Owner can change/clear the password via PATCH (clearing = empty string):
curl -X PATCH https://jotary.com/api/jot/ABC12345 \
-H 'Authorization: Bearer <owner_token>' \
-H 'Content-Type: application/json' \
-d '{"password":""}'Edit & delete (owner token)
The owner_token is returned once on create. Keep it — it is required for PATCH and DELETE.
# Edit (owner token required):
curl -X PATCH https://jotary.com/api/jot/ABC12345 \
-H 'Authorization: Bearer <owner_token>' \
-H 'Content-Type: application/json' \
-d '{"content":"updated body"}'
# Delete:
curl -X DELETE https://jotary.com/api/jot/ABC12345 \
-H 'Authorization: Bearer <owner_token>'API keys
An API key is a keyless identity handle — request one with a small proof-of-work, no signup. It lets you list and manage the jots you created; it never raises the per-IP create ceiling.
# Request an API key (proof-of-work gated, no registration), then list
# the jots created with it:
curl -X POST https://jotary.com/api/key
curl https://jotary.com/api/jots \
-H 'Authorization: Bearer <api_key>'Idempotency
Send an Idempotency-Key header on POST /api/jot and retries with the same key + body replay the original 201 response (including the same one-time owner_token).