Back to Docs

API Reference

All endpoints are available at https://linksnap.forjio.com/api/v1. Authenticate with a Bearer token (JWT) or API key via the Authorization header.

Authentication

POST/auth/signup

Register a new account.

Request body

{
  "email": "user@example.com",
  "password": "securePassword123",
  "name": "Jane Doe"
}

Response

{
  "data": {
    "accessToken": "eyJhbG...",
    "refreshToken": "eyJhbG...",
    "user": { "id": "usr_...", "email": "user@example.com", "name": "Jane Doe" }
  }
}
POST/auth/login

Authenticate with email and password.

Request body

{
  "email": "user@example.com",
  "password": "securePassword123"
}

Response

{
  "data": {
    "accessToken": "eyJhbG...",
    "refreshToken": "eyJhbG...",
    "user": { "id": "usr_...", "email": "user@example.com" }
  }
}
GET/auth/meAuth required

Get current user profile and usage stats.

Response

{
  "data": {
    "id": "usr_...",
    "email": "user@example.com",
    "name": "Jane Doe",
    "plan": "PRO",
    "linksUsed": 42,
    "linksLimit": 5000
  }
}

Links

POST/linksAuth required

Create a new short link.

Request body

{
  "url": "https://example.com/my-long-page",
  "slug": "my-link",        // optional
  "expiresAt": "2026-12-31", // optional
  "maxClicks": 1000,         // optional
  "tags": ["marketing"]      // optional
}

Response

{
  "data": {
    "id": "lnk_...",
    "url": "https://example.com/my-long-page",
    "slug": "my-link",
    "shortUrl": "https://lsnp.io/my-link",
    "createdAt": "2026-03-23T10:00:00Z"
  }
}
GET/linksAuth required

List links (paginated). Supports ?q=search&tag=marketing&sort=clicks&order=desc&cursor=...&limit=20.

Response

{
  "data": [
    { "id": "lnk_...", "url": "...", "slug": "my-link", "clicks": 142 }
  ],
  "meta": { "cursor": "next_cursor_value", "hasMore": true }
}
GET/links/:idAuth required

Get a single link by ID or slug.

Response

{
  "data": {
    "id": "lnk_...",
    "url": "https://example.com",
    "slug": "my-link",
    "clicks": 142,
    "tags": ["marketing"],
    "expiresAt": null,
    "maxClicks": null,
    "status": "active",
    "createdAt": "2026-03-23T10:00:00Z"
  }
}
PATCH/links/:idAuth required

Update a link's URL, slug, expiry, max clicks, tags, or status.

Request body

{
  "url": "https://example.com/updated",
  "slug": "new-slug",
  "tags": ["updated"],
  "status": "archived"
}

Response

{
  "data": {
    "id": "lnk_...",
    "url": "https://example.com/updated",
    "slug": "new-slug",
    "status": "archived"
  }
}
DELETE/links/:idAuth required

Permanently delete a link.

Response

{
  "data": { "success": true }
}

Analytics

GET/links/:id/statsAuth required

Get click analytics for a link. Supports ?from=2026-01-01&to=2026-03-31 date range filtering.

Response

{
  "data": {
    "totalClicks": 142,
    "uniqueClicks": 98,
    "countries": { "US": 45, "ID": 32, "GB": 21 },
    "devices": { "mobile": 78, "desktop": 52, "tablet": 12 },
    "browsers": { "Chrome": 65, "Safari": 42, "Firefox": 20 },
    "referrers": { "twitter.com": 30, "direct": 62 },
    "timeSeries": [
      { "date": "2026-03-22", "clicks": 12 },
      { "date": "2026-03-23", "clicks": 8 }
    ]
  }
}

QR Codes

POST/qr-codesAuth required

Create a new QR code with optional style customization.

Request body

{
  "url": "https://example.com",
  "title": "My QR Code",        // optional
  "linkId": "lnk_...",           // optional — link to an existing short link
  "styleConfig": {               // optional
    "pattern": "square",         // square | dots | rounded | classy
    "corners": "square",         // square | rounded | dots
    "foregroundColor": "#000000",
    "backgroundColor": "#ffffff",
    "frame": "none"              // none | border | rounded-border | badge
  }
}

Response

{
  "data": {
    "id": "qr_...",
    "url": "https://example.com",
    "slug": "qr-a1b2c3d4",
    "scanUrl": "https://lsnp.io/q/qr-a1b2c3d4",
    "hasWatermark": true,
    "totalScans": 0,
    "createdAt": "2026-04-02T10:00:00Z"
  }
}
GET/qr-codesAuth required

List QR codes (paginated). Supports ?q=search&cursor=...&limit=20.

Response

{
  "data": [
    { "id": "qr_...", "url": "...", "slug": "qr-a1b2c3d4", "totalScans": 42 }
  ],
  "meta": { "cursor": "next_cursor_value", "hasMore": true }
}
GET/qr-codes/:idAuth required

Get a single QR code by ID.

Response

{
  "data": {
    "id": "qr_...",
    "url": "https://example.com",
    "slug": "qr-a1b2c3d4",
    "scanUrl": "https://lsnp.io/q/qr-a1b2c3d4",
    "styleConfig": { ... },
    "totalScans": 42,
    "hasWatermark": false
  }
}
GET/qr-codes/:id/download?format=pngAuth required

Download QR code image. Supported formats: png, svg (svg requires Pro or Business plan).

Response

Binary image data (Content-Type: image/png or image/svg+xml)
GET/qr-codes/:id/statsAuth required

Get scan analytics for a QR code. Supports ?from=2026-01-01&to=2026-03-31 date range.

Response

{
  "data": {
    "totalScans": 142,
    "scansByDay": [{ "date": "2026-04-01", "count": 12 }],
    "scansByCountry": [{ "country": "US", "count": 45 }],
    "scansByDevice": [{ "device": "mobile", "count": 78 }],
    "scansByBrowser": [{ "browser": "Chrome", "count": 65 }]
  }
}
DELETE/qr-codes/:idAuth required

Permanently delete a QR code and all scan data.

Response

{
  "data": { "success": true }
}

Billing

GET/billing/plans

List available plans and pricing. No authentication required.

Response

{
  "data": [
    { "key": "FREE", "name": "Free", "price": 0, "linksLimit": 25 },
    { "key": "PRO", "name": "Pro", "price": 49000, "linksLimit": 5000 },
    { "key": "BUSINESS", "name": "Business", "price": 149000, "linksLimit": 50000 }
  ]
}
POST/billing/checkoutAuth required

Create a payment checkout session for upgrading to a paid plan.

Request body

{
  "plan": "PRO"
}

Response

{
  "data": {
    "snapToken": "...",
    "redirectUrl": "https://app.midtrans.com/snap/..."
  }
}