REST API
The complete admin and redirect reference. The Admin API runs on port 9090 (localhost only by default); the redirect endpoint runs on port 8081 (public).
Admin API — URL Mappings
Create Short URL
POST /api/shortenRequest body:
{
"originalUrl": "https://example.com/very/long/path",
"alias": "my-link",
"expiresAt": "2025-12-31T23:59:59Z"
}| Field | Required | Description |
|---|---|---|
originalUrl | ✅ | The target URL |
alias | ❌ | Custom short code (3–32 chars, [A-Za-z0-9_-]) |
expiresAt | ❌ | ISO 8601 expiry timestamp |
Response 201 Created:
{
"shortCode": "my-link",
"originalUrl": "https://example.com/very/long/path",
"active": true,
"createdAt": "2025-01-01T10:00:00Z",
"expiresAt": "2025-12-31T23:59:59Z"
}List Mappings
| Endpoint | Description |
|---|---|
GET /api/list | Paginated list (default 50) |
GET /api/list/all | All mappings |
GET /api/list/active | Active only |
GET /api/list/inactive | Inactive only |
GET /api/list/expired | Expired only |
GET /api/list/count | Count statistics |
Response 200 OK:
[
{
"shortCode": "my-link",
"originalUrl": "https://example.com/very/long/path",
"active": true,
"createdAt": "2025-01-01T10:00:00Z"
}
]Edit Mapping
POST /api/edit{
"shortCode": "my-link",
"originalUrl": "https://new-target.com",
"active": true
}Delete Mapping
POST /api/delete{ "shortCode": "my-link" }Toggle Active
POST /api/toggleActive{ "shortCode": "my-link" }Admin API — Import / Export
Export
GET /api/list/exportDownloads a ZIP archive containing all mappings as JSON.
Validate Import
POST /api/list/import/validate
Content-Type: multipart/form-dataReturns validation results before applying. Detects conflicts and invalid entries.
Apply Import
POST /api/list/import/applyApplies a previously validated import staging.
Check Staging
| Endpoint | Description |
|---|---|
GET /api/list/import/staging/conflicts | Conflicting entries |
GET /api/list/import/staging/invalid | Invalid entries |
Admin API — Statistics
| Endpoint | Description |
|---|---|
GET /api/statistics/count | Total redirect count |
GET /api/statistics/hourly | Hourly breakdown |
GET /api/statistics/daily | Daily breakdown |
GET /api/statistics/timeline | Timeline data |
GET /api/statistics/config | Statistics configuration |
GET /api/statistics/debug | Raw event data (debug) |
Hourly response example:
{
"shortCode": "my-link",
"buckets": [
{ "hour": "2025-01-01T10:00:00Z", "count": 42 },
{ "hour": "2025-01-01T11:00:00Z", "count": 17 }
]
}Admin API — API Keys
| Endpoint | Description |
|---|---|
GET /api/admin/apikeys | List API keys for the current user |
POST /api/admin/apikeys | Create a new API key (plaintext returned once) |
DELETE /api/admin/apikeys/{id} | Revoke an API key |
MCP Server
The MCP server runs as a separate process on port 9091 (loopback by default). It exposes the shortener as Model Context Protocol tools over JSON-RPC 2.0.
POST /mcp
X-API-Key: <key>
Content-Type: application/jsonX-API-Key header, which is forwarded to the Admin API — jCustos remains the single authorization point.Link tools
| Tool | Description |
|---|---|
create_short_link | Create with optional alias, expiresAt, active |
resolve_shortcode | Look up the original URL for a short code |
edit_link | Update target URL or active state |
toggle_active | Toggle the active flag |
delete_link | Remove a mapping |
list_links | List with all | active | expired | inactive filter, query, limit |
bulk_create_links | Create multiple links in one call |
bulk_validate | Validate a batch without applying |
export_links | Export all mappings as Base64-encoded ZIP |
import_links_validate | Validate a Base64 ZIP import |
import_links_apply | Apply a previously validated import |
Statistics tools
| Tool | Description |
|---|---|
get_statistics | Total redirect count (all / single date / date range) |
get_statistics_hourly | Hourly breakdown per short code |
get_statistics_daily | Daily breakdown per short code |
get_statistics_timeline | Timeline data |
get_statistics_config | Read statistics configuration |
set_statistics_enabled | Enable or disable statistics collection |
update_statistics_config | Update statistics configuration |
export_statistics | Export statistics as Base64-encoded ZIP |
import_statistics | Import statistics from Base64-encoded ZIP (idempotent) |
Admin API — Preferences
| Endpoint | Description |
|---|---|
GET /api/admin/preferences/columns | Get column visibility settings |
POST /api/admin/preferences/columns/edit | Bulk update preferences |
POST /api/admin/preferences/columns/single | Update single preference |
Admin API — Store Info
GET /api/store/infoReturns metadata about the active storage backend (type, record count, last modified).
Redirect Endpoint
GET /{shortCode}Ports: 8081 — public-facing, no authentication.
| Status | Condition |
|---|---|
302 Found | Active, non-expired mapping found |
404 Not Found | Unknown short code |
410 Gone | Mapping exists but is inactive or expired |
Records a RedirectEvent (timestamp, User-Agent, Referer, IP hash, Accept-Language) on every successful redirect.
Error Responses
All error responses use a consistent JSON envelope.
{
"error": "ALIAS_ALREADY_EXISTS",
"message": "The alias 'my-link' is already in use.",
"status": 409
}| Code | Meaning |
|---|---|
400 | Invalid request body or URL |
404 | Short code not found |
409 | Alias already exists |
422 | Alias policy violation |
500 | Internal server error |