Cloudshort

Admin API

The HTTP API the dashboard uses, for scripts that manage links.

The dashboard talks to this API at https://<admin domain>/api/. You can use it from scripts too. Every request and response body is JSON.

Logging in

The API uses a session cookie named auth_token. Log in once and send the cookie with every other request:

curl -c cookies.txt -H 'Content-Type: application/json' \
  -d '{"password":"your admin password"}' https://admin.example.com/api/login

curl -b cookies.txt https://admin.example.com/api/links

The session lasts 24 hours. Without a valid cookie, every endpoint except the three login endpoints returns 401 with { "error": "Unauthorized" }. If JWT_SECRET is not set on the server, they return 500 instead.

Errors

Errors have one field: { "error": "<message>" }. A body that is not valid JSON returns 400 with Invalid request body. An unexpected problem returns 500 with Internal server error.

Login endpoints

POST /api/login

Body: { "password": "..." }

StatusMeaning
200{ "success": true } and the session cookie is set
400No password in the body
401Wrong password
429Too many wrong passwords from this IP. The Retry-After header says how many seconds to wait.
500ADMIN_PASSWORD or JWT_SECRET is not set

POST /api/logout

Clears the session cookie. Returns { "success": true }.

GET /api/auth/check

Returns { "authenticated": true } or { "authenticated": false }. Never returns 401.

A link looks like this:

{ "id": 4, "slug": "launch", "long_url": "https://example.com/events/launch-week", "created_at": 1790682903663, "clicks": 55 }

created_at is in milliseconds since 1970 (UTC).

Lists every link. Query options: sort is created_at (default) or clicks, and order is desc (default) or asc. Other values fall back to the defaults.

POST /api/links

Body: { "slug": "launch", "long_url": "https://example.com/events/launch-week" }

StatusMeaning
201Created. { "success": true }
400The slug or URL breaks the link rules
409Slug already taken

PATCH /api/links/:slug

Changes the destination. Body: { "long_url": "https://example.com/new" }. Returns 200, 400 for a bad URL, or 404 if the slug does not exist.

DELETE /api/links/:id

Deletes the link with this id and all of its clicks. Returns 200, or 404 if the id does not exist.

Settings

GET /api/settings

Returns { "root_url": "", "not_found_url": "", "short_domain": "" }. An empty string means the setting is off.

POST /api/settings

Send any of the three fields. Fields you leave out are not changed, and an empty string turns a setting off. Every value must be an http:// or https:// URL, otherwise the request returns 400 and nothing is saved. A trailing / on short_domain is removed.

Analytics

GET /api/analytics/:slug

Returns all-time stats for one link, or 404 if it does not exist:

{
  "slug": "launch",
  "long_url": "https://example.com/events/launch-week",
  "stats": {
    "clicks_over_time": [{ "date": "2026-09-29", "count": 14 }],
    "country_breakdown": [{ "country": "ID", "count": 30 }],
    "referrer_breakdown": [{ "referrer": "Direct", "count": 25 }]
  }
}

Days are in UTC. The country and referrer lists hold the top 10.