The API
Twelve routes and one header
Everything a key can do, on one page. If you are here to be told when something happens rather than to ask, webhooks are the other half and are usually what people actually want.
Authenticating
A bearer token, and nothing else. Keys are made on your files, where you also choose what each one may do.
curl https://velt.lol/api/v1/me \ -H "Authorization: Bearer vk_live_…"
A key carries scopes, and a request outside them is a 403 rather than a quiet no-op. That is the point of them: a capture tool’s config file sitting on a shared machine should not be able to delete your short links.
The routes
- GET
/api/v1/mereadWho the key belongs to, and what it may do.
- GET
/api/v1/uploadsreadYour files, newest first.
- GET
/api/v1/uploads/:slugreadOne file.
- DELETE
/api/v1/uploads/:slugdeleteRemove a file.
- POST
/api/v1/uploaduploadSend a file. Multipart, and what the capture tools use.
- GET
/api/v1/linksreadYour short links.
- POST
/api/v1/linksshortenShorten one. Screened exactly as the form screens it.
- DELETE
/api/v1/links/:slugshortenRemove a short link.
- GET
/api/v1/pastesreadYour pastes.
- POST
/api/v1/pastespastePublish one.
- DELETE
/api/v1/pastes/:slugpasteRemove a paste.
- GET
/api/v1/statsreadYour numbers, and the daily series behind them.
Two examples
Shortening a link:
curl -X POST https://velt.lol/api/v1/links \
-H "Authorization: Bearer vk_live_…" \
-H "Content-Type: application/json" \
-d '{"target":"https://example.com/a/long/address","slug":"docs"}'
{
"slug": "docs",
"url": "https://velt.lol/s/docs",
"target": "https://example.com/a/long/address",
"safety": "clean"
}A shell alias that pastes whatever you pipe into it:
velt() {
jq -Rs --arg t "${1:-untitled}" '{content: ., title: $t}' \
| curl -sS -X POST https://velt.lol/api/v1/pastes \
-H "Authorization: Bearer $VELT_KEY" \
-H "Content-Type: application/json" -d @- \
| jq -r .url
}
$ cat crash.log | velt "the crash"
https://velt.lol/p/k3f9ptqWhen something is wrong
Every failure is JSON with a code that is safe to switch on. The message beside it is for a person reading a log and may be reworded; the code will not be.
{ "error": { "code": "forbidden_scope", "message": "That key does not carry …" } }- 401 no_key
- No Authorization header.
- 401 bad_key
- Not a key, revoked, or expired. The three are one answer on purpose.
- 403 forbidden_scope
- A real key, without the scope this route needs.
- 404 not_found
- No such thing — or it is somebody else’s, which is the same answer.
- 409 preserved
- A reported file, frozen until it has been looked at. Not deletable by anybody.
- 422 blocked_target
- A link we followed and will not forward people to.
- 429 rate_limited
- Per key, not per address. Retry-After says how long.
Two things worth knowing
- A 404 for somebody else’s thing. Asking for a slug that exists but is not yours answers exactly as if it did not exist. Anything else is a way to find out what other people have.
- The view count is the same number the page shows, and there is no other one. No raw request total to fall back on — a second, larger, better-looking number is how the honest one stops being the one anybody quotes. How velt counts.