msrips API

URL Shortener API

This is the public integration API for Moonshot Games's short link service. There is exactly one write endpoint exposed for external apps; everything else is admin-only.

Authentication

All API requests require a key minted from the admin panel. Pass it as a Bearer token:

Authorization: Bearer msr_xxxxxxxxxxxxxxxx

Or, if your client can't set Authorization, use the header X-API-Key instead.

Keys are shown once at creation. Store them securely — there is no way to retrieve a key after the dialog closes. Revoke a leaked key from the API Keys page.

Base URL

https://msrips.com/api

Slug rules

A slug is the short tail of the URL — e.g. in https://msrips.com/jay the slug is jay.

Create a short link

POST/api/links

Request body

FieldTypeRequiredNotes
longUrlstringyesMust be a valid http(s) URL.
customSlugstringnoVanity slug. See slug rules above. Omit to get an auto-generated slug.
domainstringnoHostname to build the short URL against. Defaults to the configured default domain.

Response codes

StatusMeaning
200Idempotent hit. A link with the requested customSlug already exists and already points to the same longUrl + domain. Body is the existing Link. Safe to call repeatedly.
201Created. Body is the new Link.
400Body missing/malformed, longUrl not a valid http(s) URL, or invalid/reserved slug format.
401Missing or invalid API key.
409Slug conflict. The slug exists and points to a different URL. Body includes the existing record so you can negotiate (see below).
500Slug generation or insert failed. Safe to retry.

Success body — Link

{
  "id": 42,
  "slug": "jay",
  "longUrl": "https://stream.moonshot.games/clips/abc123",
  "domain": "msrips.com",
  "custom": true,
  "createdAt": "2026-05-03T18:22:11.000Z",
  "shortUrl": "https://msrips.com/jay"
}

Always use the returned shortUrl verbatim — don't reconstruct it client-side, since the response domain may differ from the request host.

Slug conflict body — 409

{
  "error": "Slug \"jay\" is already in use",
  "existing": {
    "id": 17,
    "slug": "jay",
    "longUrl": "https://different.example.com/page",
    "domain": "msrips.com",
    "custom": true,
    "createdAt": "2026-04-12T09:01:33.000Z",
    "shortUrl": "https://msrips.com/jay"
  }
}

Recommended client flow on 409:

  1. Show the user what jay currently points to (existing.longUrl).
  2. Let them either pick a different customSlug and retry, or accept the existing shortUrl as-is.
Idempotency tip: If your client retries the same customSlug + longUrl after a network hiccup, you'll get a 200 with the original record instead of a 409. You can rely on this.

Examples

Auto-generated slug:

curl -X POST https://msrips.com/api/links \
  -H "Authorization: Bearer msr_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"longUrl":"https://stream.moonshot.games/clips/abc123"}'

# → 201 { ..., "shortUrl": "https://msrips.com/k3p" }

Vanity slug:

curl -X POST https://msrips.com/api/links \
  -H "Authorization: Bearer msr_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"longUrl":"https://stream.moonshot.games/jayson","customSlug":"jay"}'

# → 201 { ..., "shortUrl": "https://msrips.com/jay" }
# → 200 if the same { customSlug, longUrl } pair was already created (idempotent)
# → 409 if "jay" already maps to a different longUrl

Get click count for a slug

GET/api/links/<slug>/stats

Returns the link plus its total click count and last-clicked timestamp. Accessible with the same API key (or an admin session). Poll this to display click counts in your own app.

Example

curl https://msrips.com/api/links/jay/stats \
  -H "Authorization: Bearer msr_xxxxxxxxxxxxxxxx"

Success body — LinkStats

{
  "id": 42,
  "slug": "jay",
  "longUrl": "https://stream.moonshot.games/clips/abc123",
  "domain": "msrips.com",
  "custom": true,
  "createdAt": "2026-05-03T18:22:11.000Z",
  "shortUrl": "https://msrips.com/jay",
  "clickCount": 137,
  "lastClickAt": "2026-05-04T11:09:52.000Z"
}

lastClickAt is null if the link has never been clicked. Slug lookup is case-insensitive. Returns 404 if the slug doesn't exist.

Polling tip: click counts update on every redirect, but logging is fire-and-forget — a freshly-clicked link may take a moment to reflect. Polling every 15–60s is plenty; there's no need to hammer it per-second.

Resolving short links

No API call is needed to resolve a short link — just hit the URL. The service issues a 302 redirect to the underlying longUrl and records the click. Both forms work and are equivalent:

GET https://msrips.com/<slug>     →  302 Location: <longUrl>
GET https://msrips.com/r/<slug>   →  302 Location: <longUrl>

Lookups are case-insensitive: /JAY and /jay both work. Always advertise the lowercase form returned in shortUrl.

Implementation notes

Support

Questions, or a key revoked by mistake? Reach Jayson at Moonshot Games.