{{define "api.html"}}{{template "base" .}}{{end}} {{define "content"}}

Get started

Upload files anywhere, from anything

Warpbox is a one endpoint upload API. Send a multipart file with curl, a script, ShareX, or the warpbox CLI and get back a shareable box link. Request JSON to also receive private manage and delete URLs.

Your first upload

No account required. This prints one plain box URL you can share immediately.

curl -F file=@./report.pdf {{.Data.UploadURL}}

Want file URLs, a manage link, and a delete link back? Add -H 'Accept: application/json'. See the JSON response.

Reference

Endpoints

Base URL {{.Data.BaseURL}}. Authentication is optional: send Authorization: Bearer <token> to upload as your account and use your account limits, or omit it to upload anonymously.

POST /api/v1/upload

The core endpoint. Accepts a multipart/form-data body with one or more files. Returns a plain box URL by default, or the full JSON object when you send Accept: application/json.

Request fields

file

One or more files. Repeat the field for multiple files. Used by curl, browsers, and the CLI.

sharex

Alternative file field used by ShareX custom uploader configs. Same behaviour as file.

max_days

Optional. Days before the box expires. Defaults to 7.

expires_minutes

Optional. Lifetime in minutes. Takes precedence over max_days when > 0. Use it for expiries under a day (e.g. 60 = one hour).

max_downloads

Optional. Auto-expire the box after this many downloads.

password

Optional. Password required before viewing or downloading.

obfuscate_metadata

Optional on. Hides file names/counts until unlock (only meaningful with a password).

Request headers

Accept

application/json to receive the JSON body; otherwise a single plain-text URL.

Authorization

Optional Bearer <token>. Attributes the upload to your account.

X-Warpbox-Batch

Optional grouping key. Uploads sharing a value within {{.Data.ShareXGroupWindow}} land in the same box. See Integrations.

Example

curl -F file=@./report.pdf \
  -F max_downloads=5 \
  -F expires_minutes=1440 \
  -H 'Accept: application/json' \
  {{.Data.UploadURL}}

JSON response

Returned when Accept: application/json is sent. The raw delete token appears only once, inside manageUrl and deleteUrl, so store them privately. Full schema: upload-response.json.

{
  "boxId": "abc123",
  "boxUrl": "{{.Data.BaseURL}}/d/abc123",
  "zipUrl": "{{.Data.BaseURL}}/d/abc123/zip",
  "thumbnailUrl": "{{.Data.BaseURL}}/d/abc123/thumb/file123",
  "manageUrl": "{{.Data.BaseURL}}/d/abc123/manage/private-token",
  "deleteUrl": "{{.Data.BaseURL}}/d/abc123/manage/private-token/delete",
  "expiresAt": "2026-06-05T12:00:00Z",
  "files": [
    {
      "id": "file123",
      "name": "report.pdf",
      "size": "2.4 MiB",
      "url": "{{.Data.BaseURL}}/d/abc123/f/file123",
      "thumbnailUrl": "{{.Data.BaseURL}}/d/abc123/thumb/file123"
    }
  ]
}

On error the body is { "error": "message" } with a non-2xx status. Common causes: 413 over the size limit, 429 rate limited or over your daily quota, 401 bad token.

Resumable uploads

For large files. Browser uploads use this by default. Create a session with file metadata, PUT exact-sized chunks, then complete. Chunks are temporary and cleaned if the session expires. Send the same Authorization header on every request for authenticated sessions.

POST/api/v1/uploads/resumableCreate a session
GET/api/v1/uploads/resumable/{sessionID}Session status
PUT/api/v1/uploads/resumable/{sessionID}/files/{fileID}/chunks/{index}Upload one chunk
POST/api/v1/uploads/resumable/{sessionID}/completeFinalize (returns the upload JSON)
# 1. Create a session.
curl -s {{.Data.BaseURL}}/api/v1/uploads/resumable \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"files":[{"name":"report.pdf","size":1048576,"contentType":"application/pdf"}],"expiresMinutes":1440}'

# 2. Upload each chunk using the returned sessionId, file id, and chunkSize.
dd if=./report.pdf bs=8388608 count=1 skip=0 2>/dev/null | \
  curl -X PUT --data-binary @- \
  {{.Data.BaseURL}}/api/v1/uploads/resumable/SESSION_ID/files/FILE_ID/chunks/0

# 3. Complete after all chunks are present. The response is the normal upload JSON.
curl -X POST -H 'Accept: application/json' \
  {{.Data.BaseURL}}/api/v1/uploads/resumable/SESSION_ID/complete

Incomplete chunks are stored under data/tmp/uploads before finalizing into the selected storage backend.

Health & schemas

GET/healthLiveness check
GET/api/v1/schemas/upload-request.jsonRequest JSON Schema
GET/api/v1/schemas/upload-response.jsonResponse JSON Schema

Terminal

The warpbox CLI

A tiny uploader script that wraps the API. It only needs curl (already on macOS, Linux, and Windows 10+). Point it at this instance once by setting WARPBOX_HOST to {{.Data.BaseURL}}, then upload from anywhere.

macOS & Linux

POSIX shell script (warpbox.sh).

Download for macOS / Linux
Windows

PowerShell script (warpbox.ps1).

Download for Windows

Install & add to PATH

macOS / Linux

Download into a directory on your PATH, then make it executable. ~/.local/bin is the recommended location.

mkdir -p ~/.local/bin
curl -fsSL {{.Data.BaseURL}}/static/api/warpbox.sh -o ~/.local/bin/warpbox
chmod +x ~/.local/bin/warpbox

# Point it at this instance (add to ~/.profile or ~/.zshrc to keep it set)
echo 'export WARPBOX_HOST={{.Data.BaseURL}}' >> ~/.profile

# If 'warpbox: command not found', add the dir to PATH:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.profile
# zsh users: use ~/.zshrc, then reload with: source ~/.profile

Verify with warpbox --help. Prefer a system wide install? Drop it in /usr/local/bin with sudo.

Windows (PowerShell)

Save the script, then add a function to your PowerShell profile so warpbox works anywhere.

# Save it to your home folder
iwr {{.Data.BaseURL}}/static/api/warpbox.ps1 -OutFile $HOME\warpbox.ps1

# Point it at this instance, and add a 'warpbox' command (run once)
setx WARPBOX_HOST "{{.Data.BaseURL}}"
Add-Content $PROFILE 'function warpbox { & "$HOME\warpbox.ps1" @args }'
. $PROFILE   # reload the profile

If scripts are blocked, allow local scripts for your user: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.

Usage

A password, an expiry of two days, and a glob the shell expands for you:

warpbox --password 123 --expiry 2d ./first_file.zip ./whatever.png ./all_*_photos.jpg
-p, --password

Require a password to open the box.

-e, --expiry

Lifetime: 30m, 6h, 2d, 1w (or bare minutes).

-n, --max-downloads

Expire after N downloads.

-o, --obfuscate

Hide names/counts until unlock (needs --password).

--json

Print the full JSON response instead of just the URL.

--host

Server to upload to. Defaults to your WARPBOX_HOST.

Windows uses PowerShell flags: warpbox -Password 123 -Expiry 2d .\file.zip.

Secure authentication

To upload as your account (and use your account's size, daily, and retention limits), the CLI needs an API token. Set it in your environment so it never appears in your shell history or in the process list that any user on the machine can read:

# macOS / Linux (add to ~/.profile or ~/.zshrc to persist)
export WARPBOX_TOKEN=wbx_your_token
warpbox ./photo.png

# Windows (persist for your user)
setx WARPBOX_TOKEN "wbx_your_token"

For CI or shared machines, keep the token in a file with locked down permissions and point the CLI at it. This avoids putting the secret on the command line at all:

printf '%s' "wbx_your_token" > ~/.warpbox-token
chmod 600 ~/.warpbox-token
warpbox --auth-file ~/.warpbox-token ./photo.png

--auth <token> exists for quick tests but is discouraged: it leaks into shell history and ps. Create or revoke tokens under Account, Access tokens.

Integrations

ShareX setup

Import the uploader once, then optionally add your API key to upload as your account instead of as an anonymous guest.

1. Import the uploader

  1. Download warpbox-anonymous.sxcu.
  2. In ShareX: Destinations → Custom uploader settings → Import → From file, then pick the .sxcu.

2. Add your API key (optional, upload as your account)

  1. Create a personal access token under Account, Access tokens and copy it.
  2. In Custom uploader settings, select the Warpbox uploader and open the Headers section.
  3. Add a header. Name Authorization, Value Bearer <your token>.

Without that header, uploads stay anonymous. With it, they're attributed to your account and use your account's limits.

{
  "Version": "1.0.0",
  "Name": "Warpbox (my account)",
  "DestinationType": "ImageUploader, FileUploader, TextUploader",
  "RequestMethod": "POST",
  "RequestURL": "{{.Data.ShareXExampleURL}}",
  "Headers": {
    "Accept": "application/json",
    "Authorization": "Bearer YOUR_API_TOKEN",
    "X-Warpbox-Batch": "sharex"
  },
  "Body": "MultipartFormData",
  "FileFormName": "{{.Data.ShareXFileFieldName}}",
  "URL": "{json:boxUrl}",
  "ThumbnailURL": "{json:thumbnailUrl}",
  "DeletionURL": "{json:deleteUrl}",
  "ErrorMessage": "{json:error}"
}

Grouping multiple files into one box

Grouping is opt in via the X-Warpbox-Batch request header. Without it, every file becomes its own box (the default). When the header is present, uploads sharing the same value (per account, or per IP for anonymous) within {{.Data.ShareXGroupWindow}} of each other are added to the same box, so a ShareX selection of several files produces one shareable link instead of one per file. The shipped config sets X-Warpbox-Batch: sharex; remove that header for one box per file.

The response also exposes {json:thumbnailUrl} for ShareX previews, {json:deleteUrl} for the deletion URL, and {json:error} so ShareX surfaces messages like rate limiting.

Cookbook

Examples

Every snippet hits POST {{.Data.UploadURL}}. Add -H 'Authorization: Bearer <token>' to any of them to upload as your account.

curl

Plain text (one URL) for the shell; JSON for automation.

# Just the box URL
curl -F file=@./report.pdf {{.Data.UploadURL}}

# Full JSON with manage + delete URLs, password and 1-hour expiry
curl -F file=@./report.pdf \
  -F password=hunter2 \
  -F expires_minutes=60 \
  -H 'Accept: application/json' \
  {{.Data.UploadURL}}

wget

The endpoint needs a real multipart/form-data body, which wget can't assemble on its own, so build the body by hand and post it. It also shows the wire format:

B=----warpbox$$
{ printf -- '--%s\r\nContent-Disposition: form-data; name="file"; filename="report.pdf"\r\nContent-Type: application/octet-stream\r\n\r\n' "$B"
  cat ./report.pdf
  printf -- '\r\n--%s--\r\n' "$B"; } > /tmp/wb.body

wget --quiet --output-document=- \
  --header="Content-Type: multipart/form-data; boundary=$B" \
  --header="Accept: application/json" \
  --post-file=/tmp/wb.body \
  {{.Data.UploadURL}}

Add more form fields (password, expires_minutes, …) by repeating the --%s … Content-Disposition: form-data; name="…" block before the closing boundary. If this feels fiddly, curl or the CLI build the body for you.

HTTPie

Multipart with form fields:

http --multipart POST {{.Data.UploadURL}} \
  Accept:application/json \
  file@./report.pdf \
  max_downloads=3 \
  expires_minutes=1440

Python (requests)

import requests

with open("report.pdf", "rb") as f:
    r = requests.post(
        "{{.Data.UploadURL}}",
        headers={"Accept": "application/json"},  # add "Authorization": "Bearer "
        files={"file": f},
        data={"expires_minutes": 1440, "max_downloads": 5},
    )
r.raise_for_status()
print(r.json()["boxUrl"])

Node.js (fetch)

import { readFile } from "node:fs/promises";

const form = new FormData();
form.set("file", new Blob([await readFile("report.pdf")]), "report.pdf");
form.set("expires_minutes", "1440");

const res = await fetch("{{.Data.UploadURL}}", {
  method: "POST",
  headers: { Accept: "application/json" }, // add Authorization: "Bearer "
  body: form,
});
const box = await res.json();
console.log(box.boxUrl);

PowerShell

PowerShell 7+ has native multipart with -Form:

$resp = Invoke-RestMethod -Uri "{{.Data.UploadURL}}" -Method Post -Headers @{ Accept = "application/json" } -Form @{
  file            = Get-Item ".\report.pdf"
  expires_minutes = 1440
}
$resp.boxUrl

On Windows PowerShell 5.1, use the bundled curl.exe (the same approach the CLI takes) or the warpbox.ps1 script.

Help

FAQ & troubleshooting

Quick answers, each linking back to the relevant part of the docs.

Do I need an account or API key?

No. Anonymous uploads work without one, see the quickstart. Add a token only to upload as your account and use your account's limits; set one up under Account, Access tokens and pass it as described in CLI authentication.

How do I send a password, expiry, or download limit?

They're multipart form fields on the upload endpoint: password, expires_minutes (or max_days), and max_downloads. See the full list under Endpoints, request fields, or use the CLI flags in CLI usage.

How do I get file URLs and a delete link back?

Send Accept: application/json. The response includes boxUrl, per-file urls, and the private manageUrl/deleteUrl (shown only once). See the JSON response.

How do I upload one big file reliably?

Use the resumable endpoints: create a session, PUT chunks, then complete. Interrupted uploads can resume from the last chunk.

Can I upload several files into one shareable link?

Yes. Send the X-Warpbox-Batch header with a shared value within {{.Data.ShareXGroupWindow}}. Details in Integrations, grouping.

Where's the keep-it-secret way to store my token?

Use the WARPBOX_TOKEN environment variable or --auth-file, not --auth on the command line. Full guidance in CLI authentication.

My upload returns an error, what do the codes mean?

Errors come back as { "error": "message" } with a non-2xx status: 413 too large, 429 rate limited / over quota, 401 invalid token. See error responses.

How do I use Warpbox from ShareX?

Import the .sxcu and (optionally) add your token header. Step by step with the config in Integrations, ShareX setup.

warpbox: command not found after install?

The install directory isn't on your PATH. Fix it per your platform in Install & add to PATH.

Is there a machine-readable schema?

Yes: upload-request.json and upload-response.json (JSON Schema 2020-12).

{{end}}