REST API v1

Documentation.

Everything the site does, available over HTTP. JSON in, JSON out, one response shape throughout.

base urlhttps://api.weggle.xyz/v1 rate limit100 req / 15 min authX-API-Key header

Every endpoint lives on api.weggle.xyz. The page you are reading is the only thing at www.weggle.xyz/api — it is documentation, not an endpoint, and requests to www.weggle.xyz/api/v1/… answer 404 rather than redirecting, so a misaddressed client fails where the mistake is instead of somewhere later.

00 / Overview

One shape, every time.

Every response is JSON and every response carries a success boolean. On failure there is an error string instead of data, so a client can branch on one field and never guess.

base urls
# production
https://api.weggle.xyz/v1

# local development
http://api.weggle.test:3000/v1

# not an API — this page
https://www.weggle.xyz/api
response shape
// success
{ "success": true, "data": { … } }

// failure
{ "success": false, "error": "message" }

00 / Authentication

Bring a key.

Two ways in. Anything that is not a browser should use an API key. Sessions expire, keys do not, and a key can be revoked on its own without logging you out everywhere.

api keyrecommended
# generate one at /settings/api-keys
X-API-Key: wk_your_key_here
session cookiebrowsers
# set by POST /v1/auth/login
Cookie: weggle.session_token=…

Key allowance per plan

Free 3 · Pro 10 · Premium 25. Keys are prefixed wk_ and managed at /settings/api-keys.

00 / Errors

Status codes.

CodeMeaning
200 OK. The request worked
201 Created. The thing now exists
400 Bad request. A parameter is missing or malformed
401 Unauthorized. No credentials, or they were rejected
403 Forbidden. Authenticated, but not allowed to do that
404 Not found. No such thing
422 Unprocessable. Validation failed, see the details array
429 Too many requests. You hit the rate limit
500 Server error. Our fault, not yours

00 / Rate limiting

Counted per IP.

Every response tells you where you stand, so there is no need to guess or back off blindly.

response headers
RateLimit-Limit:     100
RateLimit-Remaining: 87
RateLimit-Reset:     1748192400
100Standard · per 15 min
30Uploads · per hour
20Auth · per 15 min

01 / Auth

Auth

Session endpoints. Programmatic clients should use an API key instead and skip this section entirely.

POST /v1/auth/login Log in

Authenticate and set a session cookie on the response. Send "email", or send "username" — which takes a name or an address, as the sign-in form does. At least one is required; if both are sent, "username" is the one used. Failures answer 401 for bad credentials, 422 for a body the validator refused, or 403 with a code of account_banned or account_suspended.

request
{ "email": "user@example.com", "password": "secret" }
response200
{ "success": true, "user": { "id": "…", "username": "alice", "role": "user" }, "redirectTo": "/home" }
GET /v1/auth/register/challenge Registration challenge

Step one of registration. Returns a proof-of-work challenge, its difficulty and the rule to satisfy. Find any nonce where the hex SHA-256 of the challenge and the nonce concatenated begins with that many zeros, then send the challenge and the nonce to the register endpoint. A challenge lasts 10 minutes and is good for one attempt.

response200
{ "success": true, "data": { "challenge": "…", "difficulty": 5, "algorithm": "sha256", "rule": "hex(sha256(challenge + nonce)) must start with 5 zeros", "expiresInSeconds": 600 } }
POST /v1/auth/register Register

Create a new account. Requires a solved challenge from the endpoint above; the same proof-of-work protects the browser form. Usernames are 3 to 20 characters, letters, numbers and underscores. Proof-of-work failures carry a code of pow_missing, pow_invalid, pow_expired or pow_already_used. Limited to 6 attempts an hour and 5 accounts a day from one address.

request
{ "username": "alice", "email": "user@example.com", "password": "secret", "_pow_challenge": "…", "_pow_nonce": "48213" }
response200
{ "success": true, "user": { "id": "…", "username": "alice" }, "redirectTo": "/home" }
POST /v1/auth/logout Log out

Invalidate the current session.

response200
{ "success": true }

02 / Users

Users

Reading and writing profile data.

GET /v1/users/me Current user

The authenticated account in full. Requires authentication.

response200
{ "success": true, "user": { "id": "…", "username": "alice", "email": "…", "role": "user", "plan": "free", "storage_used": 0 } }
GET /v1/users/profile Public profile

A user's public profile, looked up by username or id.

Parameters

  • username optional username to look up
  • userId optional user id to look up
response200
{ "success": true, "profile": { "username": "alice", "bio": "…", "avatar_url": "…" } }
PUT /v1/users/profile Update profile

Change fields on the authenticated account.

request
{ "bio": "My new bio", "display_username": "Alice" }
response200
{ "success": true, "user": { … } }
POST /v1/users/avatar Upload avatar

Multipart form-data. Images only, 5 MB ceiling.

request
Content-Type: multipart/form-data
field: avatar   (image file, max 5MB)
response200
{ "success": true, "avatarUrl": "https://cdn.weggle.xyz/…" }
GET /v1/users/search Search users

Match accounts by username.

Parameters

  • q required search query, two characters minimum
  • limit optional max results, default 10
response200
{ "success": true, "users": [{ "username": "alice", "avatar_url": "…" }] }

03 / Files

Files

The upload and hosting surface. Every one of these accepts an X-API-Key header.

GET /v1/files List files

Files belonging to the authenticated account.

Parameters

  • limit optional max results, default 25, ceiling 100
  • offset optional pagination offset
  • search optional filename search
  • type optional mime prefix, e.g. image/
  • publicStatus optional true or false
response200
{
  "success": true,
  "data": {
    "files": [{ "id": 1, "filename": "photo.jpg", "mimeType": "image/jpeg", "size": 102400,
                "humanReadableSize": "100 KB", "isPublic": false, "downloadCount": 0, "url": "/file/1" }],
    "pagination": { "total": 42, "limit": 25, "offset": 0, "hasMore": true }
  }
}
GET /v1/files/:fileId Get file

Metadata for one file. Private files require the owner's credentials.

Parameters

  • fileId required integer file id, in the path
response200
{ "success": true, "data": { "file": { "id": 1, "filename": "photo.jpg", "size": 102400, "url": "/file/1" } } }
POST /v1/files/upload Upload

Multipart form-data. The size ceiling depends on the account plan.

request
Content-Type: multipart/form-data
field: file       (required, binary)
field: isPublic   (optional, boolean, default false)
response200
{ "success": true, "file": { "id": 1, "url": "/file/1" } }
PUT /v1/files/:fileId Update file

Rename a file or change its visibility.

Parameters

  • fileId required path parameter
request
{ "filename": "new-name.jpg", "isPublic": true }
response200
{ "success": true, "file": { … } }
DELETE /v1/files/:fileId Delete file

Permanent. There is no recycle bin behind this.

Parameters

  • fileId required path parameter
response200
{ "success": true, "message": "File deleted" }

04 / Memes

Memes

Generate images from templates without opening the editor.

GET /v1/memes List memes

Memes belonging to the authenticated account.

Parameters

  • limit optional max results, default 25, ceiling 100
  • offset optional pagination offset
  • templateId optional filter by template
response200
{
  "success": true,
  "data": {
    "memes": [{ "id": 1, "title": "My Meme", "templateId": "drake", "isPublic": true,
                "viewCount": 42, "imageUrl": "/meme/1/view" }],
    "pagination": { "total": 5, "limit": 25, "offset": 0, "hasMore": false }
  }
}
POST /v1/memes Create meme

Render a template with your own text. Needs write:memes on the key.

request
{ "templateId": "drake", "title": "My Meme", "textFields": ["top text", "bottom text"], "isPublic": true }
response200
{ "success": true, "meme": { "id": 1, "imageUrl": "/meme/1/view" } }
DELETE /v1/memes/:memeId Delete meme

Removes a meme you own.

Parameters

  • memeId required path parameter
response200
{ "success": true }

05 / Social

Social

Friends, followers, and the wall.

GET /v1/friends List friends

Every accepted friendship for the current account.

response200
{ "success": true, "friends": [{ "id": "…", "username": "bob" }] }
POST /v1/friends Send request

Open a friend request to another account.

request
{ "addresseeId": 123 }
response200
{ "success": true, "friendship": { "status": "pending" } }
PUT /v1/friends/respond Respond to request

Accept or decline an incoming request.

request
{ "friendshipId": "…", "action": "accept" }
response200
{ "success": true }
GET /v1/followers Followers

Accounts following the current user.

response200
{ "success": true, "followers": [{ "username": "…" }], "total": 42 }
POST /v1/follow Follow

Follow another account.

request
{ "followingId": 123 }
response200
{ "success": true }
GET /wall Wall posts

Public wall posts, paginated.

Parameters

  • page optional page number, default 1
response200
{ "success": true, "posts": [{ "id": "…", "content": "Hello world", "username": "alice" }] }
POST /wall Create wall post

Post to the public wall. Requires authentication.

request
{ "content": "Hello, world!" }
response200
{ "success": true, "post": { "id": "…" } }

06 / Messages

Messages

Direct messages between friends.

GET /v1/messages/conversations Conversations

Every direct thread the current account is part of.

response200
{ "success": true, "data": [{ "id": 12, "other_username": "bob", "last_content": "Hey!", "unread": 2 }] }
GET /v1/messages/contacts Contacts

Everyone the account can message right now, built from accepted friendships.

response200
{ "success": true, "data": [{ "id": "…", "username": "bob", "status": "online", "conversation_id": 12 }] }
GET /v1/messages/with/:username Open a thread

Finds or creates the direct conversation with one user.

Parameters

  • username required path parameter
response200
{ "success": true, "data": { "conversationId": 12, "other": { "username": "bob" } } }
GET /v1/messages/conversation/:id Read a thread

One page of messages, newest last. Pass ?before=<id> for older ones.

Parameters

  • id required path parameter
  • before optional message id to page back from
response200
{ "success": true, "data": [{ "id": 88, "content": "Hello!", "sender_id": "…" }], "hasMore": false }
POST /v1/messages/conversation/:id Send a message

Post into a conversation the account is a participant of.

Parameters

  • id required path parameter
request
{ "content": "Hello!" }
response200
{ "success": true, "data": { "id": 89, "content": "Hello!" } }
DELETE /v1/messages/message/:id Delete a message

Author, group admin or staff only.

Parameters

  • id required path parameter
response200
{ "success": true, "data": { "id": 89 } }
GET /v1/messages/groups Group chats

Every group the account belongs to.

response200
{ "success": true, "data": [{ "id": 4, "group_name": "Crew", "member_count": 3, "my_role": "admin" }] }

07 / Plans

Plans

Reading plan and subscription state.

GET /v1/plans List plans

Every subscription tier and what it includes.

response200
{ "success": true, "plans": [{ "id": "free", "name": "Free", "storage": 5368709120 }] }
GET /v1/subscriptions/current Current subscription

The authenticated account's active plan and usage.

response200
{ "success": true, "subscription": { "plan": "free", "storage_used": 0, "storage_limit": 5368709120 } }