Pages CRUD
These endpoints back the dashboard. They accept either a JWT
Authorization: Bearer header or an op_… API
token. Files are sent as multipart form fields.
get_current_user — JWT cookie, JWT bearer, or API token.
Users only see and modify their own pages.
GET /pages
List all pages owned by the current user, newest first.
Response 200
[
{
"id": "Ab12Cd34",
"name": "Hello World",
"visibility": "public",
"passcodes": ["letmein"],
"allowed_emails": [],
"default_file": "index.html",
"created_at": "2026-05-03T17:42:11",
"updated_at": "2026-05-03T17:42:11"
}
]
cURL
curl https://outpost.click/pages \
-H "Authorization: Bearer op_YOUR_TOKEN"
JavaScript (browser)
const res = await fetch('/pages', {
headers: { Authorization: `Bearer ${localStorage.getItem('token')}` }
});
const pages = await res.json();
Python
import requests
r = requests.get(
"https://outpost.click/pages",
headers={"Authorization": f"Bearer {token}"},
)
r.raise_for_status()
pages = r.json()
passcodes array
in the response is the decrypted plain-text list — it's intended for
the owner's dashboard to display. Don't expose this response to viewers.
POST /pages
Create a new page. Provide either a file upload
or a source_url, not both. The server generates an
8-character page id and detects the default file (see
default-file detection).
Form fields
| Field | Type | Required | Notes |
|---|---|---|---|
name | text | yes | Display name; up to 200 chars. |
visibility | text | yes | One of private, shared, public. |
passcodes | text | no | Comma-separated list. Encrypted with Fernet derived from jwt_secret. |
allowed_emails | text | no | Comma-separated list. Used for shared visibility. |
file | file | one of | ZIP archive (≤ 50 MB) or a single .html file. |
source_url | text | one of | http(s)://…; the page and same-domain assets are fetched. |
Examples
ZIP upload — public, no passcode
curl -X POST https://outpost.click/pages \
-H "Authorization: Bearer op_YOUR_TOKEN" \
-F "name=My Site" \
-F "visibility=public" \
-F "file=@./site.zip"
Single HTML — private
curl -X POST https://outpost.click/pages \
-H "Authorization: Bearer op_YOUR_TOKEN" \
-F "name=Notes" \
-F "visibility=private" \
-F "file=@./notes.html"
Public with two passcodes
curl -X POST https://outpost.click/pages \
-H "Authorization: Bearer op_YOUR_TOKEN" \
-F "name=Friends Only" \
-F "visibility=public" \
-F "passcodes=red-rover,blue-jay" \
-F "file=@./site.zip"
Shared with a team
curl -X POST https://outpost.click/pages \
-H "Authorization: Bearer op_YOUR_TOKEN" \
-F "name=Team Wiki" \
-F "visibility=shared" \
-F "allowed_emails=alice@acme.com,bob@acme.com" \
-F "file=@./wiki.zip"
Import from URL
curl -X POST https://outpost.click/pages \
-H "Authorization: Bearer op_YOUR_TOKEN" \
-F "name=Mirror" \
-F "visibility=public" \
-F "source_url=https://example.com"
Response 200
{
"id": "Ab12Cd34",
"name": "My Site",
"visibility": "public",
"passcodes": [],
"allowed_emails": [],
"default_file": "index.html",
"created_at": "2026-05-03T17:42:11",
"updated_at": "2026-05-03T17:42:11"
}
Errors
400 Invalid visibility400 Either file or source_url must be provided400 Provide either file or source_url, not both400 Upload exceeds maximum size of 50MB400 Total page size exceeds maximum of 100MB400 Invalid zip file400 Only .zip archives or .html files are supported400 URL must return HTML content401 Missing credentials
StorageError after the pages row was inserted,
the row is deleted before the error is returned — you won't see orphan
page records.
PUT /pages/{page_id}
Update any subset of metadata, and optionally replace the page files.
Pass only the fields you want to change. Any uploaded file
fully replaces the previous content.
Form fields (all optional)
| Field | Behavior |
|---|---|
name | Replace the page name. |
visibility | Replace; must still be private/shared/public. |
passcodes |
Comma-separated, replaces the entire list. Pass an empty string to clear. |
allowed_emails |
Comma-separated, replaces the entire list. |
file |
Optional ZIP or HTML upload. Replaces all stored files; the previous content is deleted before the new files are written. |
Examples
Rename + flip to private
curl -X PUT https://outpost.click/pages/Ab12Cd34 \
-H "Authorization: Bearer op_YOUR_TOKEN" \
-F "name=My Renamed Site" \
-F "visibility=private"
Replace passcodes
curl -X PUT https://outpost.click/pages/Ab12Cd34 \
-H "Authorization: Bearer op_YOUR_TOKEN" \
-F "passcodes=newpass1,newpass2"
Clear all passcodes
curl -X PUT https://outpost.click/pages/Ab12Cd34 \
-H "Authorization: Bearer op_YOUR_TOKEN" \
-F "passcodes="
Replace files
curl -X PUT https://outpost.click/pages/Ab12Cd34 \
-H "Authorization: Bearer op_YOUR_TOKEN" \
-F "file=@./new-build.zip"
Response 200
{
"id": "Ab12Cd34",
"name": "My Renamed Site",
"visibility": "private",
"passcodes": ["newpass1", "newpass2"],
"allowed_emails": [],
"default_file": "index.html",
"created_at": "2026-05-03T17:42:11",
"updated_at": "2026-05-03T18:01:55"
}
Errors
400 Invalid visibility400any storage error from the upload (size, zip integrity).404 Page not found(also returned when you don't own the page — this is intentional, to avoid leaking the existence of other users' pages).
updated_at column is set to datetime.utcnow()
on every successful PUT.
DELETE /pages/{page_id}
Delete the page and its files. The page_files rows are
deleted explicitly; the pages row is then deleted (cascading
would also clean up files on its own).
curl -X DELETE https://outpost.click/pages/Ab12Cd34 \
-H "Authorization: Bearer op_YOUR_TOKEN"
200 OK
{ "deleted": "Ab12Cd34" }
Errors
404 Page not found(page doesn't exist, or you don't own it).
Visibility rules
| Value | Who can view /p/<id> |
|---|---|
private |
Only the owner. Anyone else gets the access gate; if any passcode is set, supplying it grants access for 24 hours. |
shared |
The owner, anyone whose logged-in email is in allowed_emails, or anyone who supplies a valid passcode. |
public |
Anyone — no login required. If a passcode is set, the access gate is shown until the visitor supplies it. |
See Page serving for the gate workflow,
the page_access_<id> cookie, and SPA-style fallback to
the default file.