Skip to main content

URL Protection API

Protect shortened links with passwords or PINs. Protected links require visitors to verify their identity before being redirected.

Protection Types

TypeValueDescription
NonenoneDefault — public link, no protection
PasswordpasswordFreeform password (4-128 characters)
PINpinNumeric PIN (4 or 6 digits)

Content Classifications

ValueDescription
generalDefault — public content
personalPrivate/sentimental content
workSecret deployments, internal tools
nsfwAdult content
curl -X POST https://api.smo1.io/api/links \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/my-secret-deployment",
    "custom_slug": "my-staging",
    "password": "hunter2",
    "protection_hint": "The classic password",
    "content_classification": "work"
  }'
curl -X POST https://api.smo1.io/api/links \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/my-dissertation",
    "pin": "1234",
    "protection_hint": "Year I graduated",
    "content_classification": "personal"
  }'
Add protection to an existing link:
curl -X PATCH https://api.smo1.io/api/links/{id} \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "password": "new-secret",
    "protection_hint": "Updated hint"
  }'
Remove protection:
curl -X PATCH https://api.smo1.io/api/links/{id} \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "remove_protection": true
  }'

Verify Protection (Public)

This endpoint is public — no authentication required. Visitors use it to unlock protected links.
curl -X POST https://api.smo1.io/api/links/{id}/verify \
  -H "Content-Type: application/json" \
  -d '{"password": "hunter2"}'

Rate Limiting

Protection verification is rate-limited per IP address per link:
  • 5 failed attempts per 15-minute window
  • After exceeding the limit, all attempts are rejected until the window expires
  • Successful attempts are recorded but do not count toward the limit

Slug Lookup (Edge Workers)

The GET /api/links/slug/:slug endpoint includes protection metadata:
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "slug": "my-staging",
  "destination": "",
  "user_id": "...",
  "is_active": true,
  "expires_at": null,
  "protection_type": "password",
  "protection_hint": "The classic password",
  "content_classification": "work"
}
When a link is protected, the destination field is redacted (empty string) in slug lookup responses. The destination is only revealed through the /verify endpoint after successful authentication.

Security

  • Passwords and PINs are bcrypt-hashed before storage — never stored in plaintext
  • Hashes are never returned in any API response
  • The edge worker gate page uses Cache-Control: no-store to prevent caching
  • Protection hints are HTML-escaped to prevent XSS