Skip to content

Endpoints

All paths are relative to https://www.remilia.net/api/v1. Responses are JSON. Send Authorization: Bearer <access_token> on any endpoint requiring a scope (see Login Quickstart and API Quickstart).

Every endpoint except the two profile reads wraps its success payload in a data envelope:

json
{ "data": { "...": "endpoint payload" } }

Errors always use the error envelope, regardless of endpoint.

Profiles

Get a user profile

http
GET /users/{username}

Returns the public profile for {username}.

Scope: none — public.

A token is optional. An expired or malformed token returns 401 invalid_token (tokens are validated even here). A valid app-only token receives the same anonymous view as a tokenless request; a user-delegated token personalizes viewerContext and the isAuthenticated / isOwnProfile flags.

sh
curl https://www.remilia.net/api/v1/users/remilia

The profile object is returned directly (no data wrapper):

json
{
  "user": {
    "username": "remilia",
    "displayName": "Remilia",
    "bio": "...",
    "location": "...",
    "pfpUrl": "...",
    "friendCount": 0,
    "achievementsCount": 0
  },
  "achievementContext": {},
  "viewerContext": { "areFriends": false },
  "isAuthenticated": false,
  "isOwnProfile": false
}

An unknown username returns 404 with the error envelope:

json
{ "error": { "code": "not_found", "message": "user not found" } }

Get your own profile

http
GET /me

Returns the profile of the token's user — the same shape as GET /users/{username} for your own username, with isOwnProfile: true and the owner-only fields (user.stats, user.aggregateScores, user.property) populated.

Scope: none · user-delegated token required — a bare openid grant works.

sh
curl https://www.remilia.net/api/v1/me \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Like the public profile read, the object is returned bare (no data wrapper). An app-only token returns 403 requires_user.

Get your account statistics

http
GET /me/stats

Returns per-platform statistics and aggregate scores for the token's user.

Scope: remilia:stats.read · user-delegated token required

sh
curl https://www.remilia.net/api/v1/me/stats \
  -H "Authorization: Bearer $ACCESS_TOKEN"
json
{
  "data": {
    "handle": "remilia",
    "display_name": "Remilia",
    "stats": {
      "miladychan": { "...": "..." },
      "twitter": { "...": "..." },
      "profiles": { "...": "..." },
      "beetle_game": { "...": "..." },
      "miladycraft": { "...": "..." },
      "ethereum": { "cult_tier": "...", "total_owned": 0, "collections": {} },
      "merch": { "...": "..." }
    },
    "aggregate_scores": { "...": 0 }
  }
}

Notifications

List your notifications

http
GET /me/notifications

Scope: remilia:notifications.read · user-delegated token required

Query parameterMeaning
limitPage size. Default 10; values outside [1, 50] fall back to the default.
page1-based page number. Default 1.
sh
curl "https://www.remilia.net/api/v1/me/notifications?limit=20&page=1" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
json
{
  "data": {
    "notifications": [
      {
        "type": "poke",
        "read": false,
        "from": {
          "handle": "milady",
          "display_name": "Milady",
          "avatar_url": "..."
        },
        "created_at": 1754870400
      }
    ],
    "page": 1,
    "limit": 20,
    "has_more": false
  }
}

count, from, guild_name, and message appear only on notification types that carry them.

Beetle

Get your Beetle state

http
GET /me/beetle

Returns the caller's Beetle game state — XP, level, inventory, hammer durabilities, and cooldowns — wrapped in data.

Scope: remilia:beetle.read · user-delegated token required

List your discovered Beetle cards

http
GET /me/beetle/cards

Returns the caller's discovered cards keyed by card codename, wrapped in data.

Scope: remilia:beetle.read · user-delegated token required

Pokes

Poke a user

http
POST /users/{handle}/poke

Sends a poke to {handle}, attributed to the authenticated user.

Scope: remilia:pokes.write · user-delegated token required

sh
curl -X POST https://www.remilia.net/api/v1/users/remilia/poke \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Success:

json
{ "data": { "target_handle": "remilia", "poked_at": 1754870400 } }

Poking the same user again within 24 hours returns 429 poke_cooldown:

json
{
  "error": {
    "code": "poke_cooldown",
    "message": "poke on cooldown (23h59m30s remaining)",
    "next_allowed_at": 1754956800
  }
}

Global chat

List global chat messages

http
GET /global-chat/messages

Returns global chat messages, oldest first.

Scope: remilia:chat.read · user-delegated or app-only token

Query parameterMeaning
limitPage size. Default 20, capped at 100. Must be a positive integer.
beforeA message id; returns messages older than it. Omit for the newest page.
sh
curl "https://www.remilia.net/api/v1/global-chat/messages?limit=50" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
json
{
  "data": {
    "messages": [
      {
        "id": "1042",
        "author": {
          "handle": "milady",
          "display_name": "Milady",
          "avatar_url": "..."
        },
        "text": "gm",
        "created_at": 1754870400
      }
    ],
    "has_more": true,
    "next_cursor": "998"
  }
}

When has_more is true, pass next_cursor as before to fetch the next-older page. Messages may also carry reply_to_id, media (kind, url, thumbnail_url, width, height, is_animated), reactions (emoji, count), and edited_at.

Post a global chat message

http
POST /global-chat/messages

Posts a text message to global chat as the authenticated user.

Scope: remilia:chat.write · user-delegated token required

sh
curl -X POST https://www.remilia.net/api/v1/global-chat/messages \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text": "gm", "reply_to_id": "1042"}'

text is required, up to 8192 bytes. reply_to_id is optional.

Returns 201 with the created message (same shape as the list) in data.

Statuserror.codeWhen
400invalid_requestMissing or over-long text, malformed body, or a non-numeric reply_to_id
400invalid_replyreply_to_id does not reference a real message
403forbiddenYou are muted or not permitted to post to global chat
413invalid_requestRequest body too large
429rate_limitedPosting faster than one message every 2 seconds

Directory

Resolve directory identities

http
POST /directory/resolve

Resolves up to 50 tagged identifiers to narrow public identities in one batch. Results preserve request order; each result echoes its query, with identity set to null for a miss.

Scope: remilia:directory.read · user-delegated or app-only token

Identifier type is one of handle, x_handle, github, discord.

sh
curl -X POST https://www.remilia.net/api/v1/directory/resolve \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"identifiers": [{"type": "handle", "value": "remilia"}]}'
json
{
  "data": {
    "results": [
      {
        "query": { "type": "handle", "value": "remilia" },
        "identity": {
          "handle": "remilia",
          "display_name": "Remilia",
          "avatar_url": "...",
          "avatar_url_small": "..."
        }
      }
    ]
  }
}

Enumerate the user directory

http
GET /users

Pages through all directory-visible users in a stable order, using cursor pagination.

Scope: remilia:directory.read · user-delegated or app-only token

Query parameterMeaning
limitPage size. Default 25, capped at 100. Must be a positive integer.
cursorOpaque continuation token from the previous page. Omit for the first page.
sh
curl "https://www.remilia.net/api/v1/users?limit=100" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
json
{
  "data": {
    "users": [
      {
        "handle": "remilia",
        "display_name": "Remilia",
        "avatar_url": "...",
        "avatar_url_small": "..."
      }
    ],
    "next_cursor": "MTA0Mg"
  }
}

next_cursor is null on the final page; otherwise pass it back verbatim as cursor.

Errors

Errors use the envelope:

json
{ "error": { "code": "...", "message": "..." } }
Statuserror.codeWhen
400invalid_requestA malformed query parameter or request body
401unauthorizedNo token on an endpoint that requires one
401invalid_tokenThe bearer token is invalid or expired — sent even on public endpoints
403insufficient_scopeThe token lacks the scope the endpoint requires
403requires_userThe endpoint acts for a user but the token is app-only (API client)
403forbiddenThe account is disabled, or the action is not permitted
404not_foundNo such endpoint, or no such user profile
404user_not_foundThe target user of an action does not exist
405method_not_allowedWrong HTTP method for the path
429rate_limited, poke_cooldownSending too fast; poke_cooldown includes next_allowed_at
500internal_errorSomething failed on our side — safe to retry with backoff

Endpoint-specific codes (invalid_reply, poke_cooldown) are listed with their endpoints above.

Built by Remilia Corporation