Skip to main content

Overview

Stiletto uses a REST API backend built with Node.js. The API is available at stiletto-node-api on GitHub.

Base URL

The API base URL is configured via the VITE_API_URL environment variable. All endpoints are relative to this base URL.
# .env.example
VITE_API_URL=https://api.stiletto.deeme.dev

Authentication

Most API endpoints require authentication using a Bearer token obtained through Discord OAuth2.

Authorization Header

headers: {
  Authorization: `Bearer ${token}`
}
The token is obtained by authenticating with Discord and is stored in the browser’s local storage.

Getting a Token

POST /users/auth
endpoint
Authenticate with Discord and obtain an access tokenRequest Body:
{
  "code": "discord_oauth2_code"
}
Response:
{
  "discordid": "123456789",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
// From src/functions/requests/users.ts:107-123
export const authDiscord = async (code: string): Promise<LoginInfo> => {
  const response = await fetch(`${config.API_URL}/users/auth`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      code: code,
    }),
  });

  if (response) {
    return await response.json();
  }

  throw new Error("errors.apiConnection");
};

Users

Endpoints for managing user profiles and authentication.

Get User Profile

GET /users
endpoint
Get the authenticated user’s profile informationAuthentication: RequiredResponse:
{
  "discordid": "123456789",
  "discordtag": "username#1234",
  "nickname": "PlayerName",
  "clanname": "MyAwesomeClan",
  "leaderid": "987654321"
}
// From src/functions/requests/users.ts:7-20
export const getUser = async (): Promise<UserInfo> => {
  const response = await fetch(`${config.API_URL}/users`, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${getStoredItem("token")}`,
    },
  });

  if (response.ok) {
    return await response.json();
  }

  throw new Error("errors.apiConnection");
};

Update User Nickname

PUT /users
endpoint
Update the authenticated user’s in-game nicknameAuthentication: RequiredRequest Body:
{
  "dataupdate": "NewNickname"
}
Response: true on success
// From src/functions/requests/users.ts:22-39
export const addNick = async (newNick: string): Promise<boolean> => {
  const response = await fetch(`${config.API_URL}/users`, {
    method: "PUT",
    headers: {
      Authorization: `Bearer ${getStoredItem("token")}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      dataupdate: newNick,
    }),
  });

  if (response.ok) {
    return response.ok;
  }

  throw new Error("errors.apiConnection");
};

Delete User Account

DELETE /users
endpoint
Delete the authenticated user’s account and all associated dataAuthentication: RequiredResponse: true on success
// From src/functions/requests/users.ts:41-52
export const deleteUser = async (): Promise<boolean> => {
  const response = await fetch(`${config.API_URL}/users`, {
    method: "DELETE",
    headers: { Authorization: `Bearer ${getStoredItem("token")}` },
  });

  if (response.ok) {
    return response.ok;
  }

  throw new Error("errors.apiConnection");
};

User Tech Tree

GET /users/:discordId/tech
endpoint
Get a user’s learned tech tree itemsAuthentication: RequiredQuery Parameters:
  • tree: Tech tree type (e.g., “crafting”, “combat”)
Response:
{
  "learned": ["item1", "item2", "item3"]
}
PUT /users/:discordId/tech
endpoint
Update a user’s learned tech tree itemsAuthentication: RequiredQuery Parameters:
  • tree: Tech tree type
Request Body:
["item1", "item2", "item3"]
Response: Updated tech tree info

Clans

Endpoints for clan creation, management, and information.

Get Clan List

GET /clans
endpoint
Get a list of clans with optional filteringAuthentication: Not requiredQuery Parameters:
  • name (optional): Filter by clan name
  • region (optional): Filter by region
Response:
[
  {
    "clanid": 1,
    "name": "MyAwesomeClan",
    "discordid": "123456789",
    "symbol": "🛡️",
    "region": "NA"
  }
]
// From src/functions/requests/clans/index.ts:62-73
export const getClans = async (
  requestParams: GetClansRequestParams,
): Promise<ClanInfo[]> => {
  const params = objectToURLSearchParams(requestParams);

  const response = await fetch(`${config.API_URL}/clans?${params}`);
  if (response) {
    return await response.json();
  }

  throw new Error("errors.apiConnection");
};

Get Clan Information

GET /clans/:clanId
endpoint
Get detailed information about a specific clanAuthentication: RequiredResponse:
{
  "clanid": 1,
  "name": "MyAwesomeClan",
  "discordid": "123456789",
  "symbol": "🛡️",
  "region": "NA",
  "description": "A clan for awesome players"
}

Create Clan

POST /clans
endpoint
Create a new clanAuthentication: RequiredQuery Parameters:
  • name: Clan name (required)
  • symbol (optional): Clan symbol/emoji
  • region (optional): Clan region
  • description (optional): Clan description
Response: Clan information object
// From src/functions/requests/clans/index.ts:75-92
export const createClan = async (
  requestParams: CreateClanRequestParams,
): Promise<Response> => {
  const params = objectToURLSearchParams(requestParams);

  const response = await fetch(`${config.API_URL}/clans?${params}`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${getStoredItem("token")}`,
    },
  });

  if (response.ok) {
    return await response.json();
  }

  throw new Error("errors.apiConnection");
};

Update Clan

PUT /clans/:clanId
endpoint
Update clan information (leader only)Authentication: RequiredQuery Parameters:
  • name (optional): New clan name
  • symbol (optional): New clan symbol
  • region (optional): New clan region
  • description (optional): New clan description
Response:
{
  "message": "Clan updated successfully"
}

Delete Clan

DELETE /clans/:clanId
endpoint
Delete a clan (leader only)Authentication: RequiredResponse: true on success

Leave Clan

DELETE /clans
endpoint
Leave your current clanAuthentication: RequiredResponse:
{
  "message": "Left clan successfully"
}

Clan Members

Endpoints for managing clan membership and permissions.

Get Clan Members

GET /clans/:clanId/members
endpoint
Get a list of all clan membersAuthentication: RequiredResponse:
[
  {
    "discordid": "123456789",
    "discordtag": "username#1234",
    "nickname": "PlayerName",
    "isLeader": true,
    "canEdit": true
  }
]
// From src/functions/requests/clans/members.ts:12-25
export const getMembers = async (clanId: number): Promise<MemberInfo[]> => {
  const response = await fetch(`${config.API_URL}/clans/${clanId}/members`, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${getStoredItem("token")}`,
    },
  });

  if (response) {
    return await response.json();
  }

  throw new Error("errors.apiConnection");
};

Update Member

PUT /clans/:clanId/members/:memberId
endpoint
Accept, reject, or kick a clan memberAuthentication: RequiredQuery Parameters:
  • action: One of accept, reject, kick
Response:
{
  "message": "Member updated successfully"
}

Get Member Permissions

GET /clans/:clanId/members/:discordId/permissions
endpoint
Get a member’s permissionsAuthentication: RequiredResponse:
{
  "canEdit": true,
  "canInvite": false,
  "canKick": false
}

Update Member Permissions

PUT /clans/:clanId/members/:memberId/permissions
endpoint
Update a member’s permissions (leader only)Authentication: RequiredQuery Parameters:
  • canEdit (optional): Boolean
  • canInvite (optional): Boolean
  • canKick (optional): Boolean
Response:
{
  "message": "Permissions updated successfully"
}

Diplomacy

Endpoints for managing clan relationships.

Get Relationships

GET /clans/:clanId/relationships
endpoint
Get all diplomatic relationships for a clanAuthentication: RequiredResponse:
[
  {
    "relationshipid": 1,
    "clanid": 1,
    "otherclanid": 2,
    "otherclanname": "EnemyClan",
    "relationshiptype": "enemy"
  },
  {
    "relationshipid": 2,
    "clanid": 1,
    "otherclanid": 3,
    "otherclanname": "AlliedClan",
    "relationshiptype": "ally"
  }
]
// From src/functions/requests/clans/relationships.ts:10-27
export const getRelationships = async (
  clanid: number,
): Promise<RelationshipInfo[]> => {
  const response = await fetch(
    `${config.API_URL}/clans/${clanid}/relationships`,
    {
      headers: {
        Authorization: `Bearer ${getStoredItem("token")}`,
      },
    },
  );

  if (response) {
    return await response.json();
  }

  throw new Error("errors.apiConnection");
};

Create Relationship

POST /clans/:clanId/relationships
endpoint
Create a diplomatic relationship with another clanAuthentication: RequiredQuery Parameters:
  • otherclanid: ID of the other clan (required)
  • relationshiptype: Type of relationship - ally, enemy, or nap (required)
Response:
{
  "message": "Relationship created successfully"
}

Delete Relationship

DELETE /clans/:clanId/relationships/:relationshipId
endpoint
Remove a diplomatic relationshipAuthentication: RequiredResponse:
{
  "message": "Relationship deleted successfully"
}

Walkers

Endpoints for managing walker (vehicle) data.

Get Walkers

GET /walkers
endpoint
Get a list of walkers with optional filteringAuthentication: RequiredQuery Parameters:
  • clanid (optional): Filter by clan ID
  • type (optional): Filter by walker type
Response:
[
  {
    "walkerid": "abc123",
    "name": "Fast Spider",
    "type": "Spider",
    "ownerid": "123456789",
    "clanid": 1,
    "lastupdate": "2026-03-03T12:00:00Z"
  }
]
// From src/functions/requests/walkers.ts:11-27
export const getWalkers = async (
  requestParams: GetWalkersRequestParams,
): Promise<WalkerInfo[]> => {
  const params = objectToURLSearchParams(requestParams);

  const response = await fetch(`${config.API_URL}/walkers?${params}`, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${getStoredItem("token")}`,
    },
  });
  if (response) {
    return await response.json();
  }

  throw new Error("errors.apiConnection");
};

Update Walker

PUT /walkers/:walkerId
endpoint
Update walker informationAuthentication: RequiredRequest Body:
{
  "name": "New Walker Name",
  "type": "Spider",
  "description": "Fast transport walker"
}
Response:
{
  "message": "Walker updated successfully"
}

Delete Walker

DELETE /walkers/:walkerId
endpoint
Delete a walkerAuthentication: RequiredResponse: true on success

Trading

Endpoints for the trading system.

Get Trades

GET /trades
endpoint
Get a list of active trade offersAuthentication: Not requiredQuery Parameters:
  • item (optional): Filter by item name
  • type (optional): Filter by trade type (buy or sell)
Response:
[
  {
    "tradeid": 1,
    "discordid": "123456789",
    "discordtag": "username#1234",
    "item": "Iron",
    "quantity": 1000,
    "price": "500 flots",
    "type": "sell",
    "region": "NA",
    "timestamp": "2026-03-03T12:00:00Z"
  }
]
// From src/functions/requests/trades.ts:11-25
export const getTrades = async (
  queryParams: GetTradesQueryParams,
): Promise<TradeInfo[]> => {
  const params = objectToURLSearchParams(queryParams);

  const response = await fetch(`${config.API_URL}/trades?${params}`, {
    method: "GET",
  });

  if (response) {
    return await response.json();
  }

  throw new Error("errors.apiConnection");
};

Create Trade

POST /trades
endpoint
Create a new trade offerAuthentication: RequiredRequest Body:
{
  "item": "Iron",
  "quantity": 1000,
  "price": "500 flots",
  "type": "sell",
  "region": "NA"
}
Response:
{
  "message": "Trade created successfully"
}

Delete Trade

DELETE /trades/:tradeId
endpoint
Delete your own trade offerAuthentication: RequiredResponse: true on success

Maps

Endpoints for resource maps.

Get Maps

GET /maps
endpoint
Get a list of available mapsAuthentication: RequiredResponse:
[
  {
    "mapid": 1,
    "name": "Resource Map - Alpha Tile",
    "ownerid": "123456789",
    "clanid": 1,
    "isPrivate": true,
    "hasPassword": true
  }
]

Create Map

POST /maps
endpoint
Create a new resource mapAuthentication: Optional (required for private maps)Query Parameters:
  • name: Map name (required)
  • password (optional): Password for private map
Response:
{
  "mapid": 1,
  "password": "generated-password"
}

Get Map

GET /maps/:mapId
endpoint
Get a specific map with markersAuthentication: Not requiredQuery Parameters:
  • mappass (optional): Password for private maps
Response:
{
  "mapid": 1,
  "name": "Resource Map",
  "markers": [
    {
      "x": 100,
      "y": 200,
      "type": "iron",
      "description": "Large iron deposit"
    }
  ]
}

Update Map

PUT /maps/:mapId
endpoint
Update map information and markersAuthentication: Optional (required for owned maps)Request Body:
{
  "name": "Updated Map Name",
  "markers": [...],
  "mappass": "password"
}
Response:
{
  "message": "Map updated successfully"
}

Delete Map

DELETE /maps/:mapId
endpoint
Delete a mapAuthentication: RequiredResponse:
{
  "message": "Map deleted successfully"
}

Get Map Info

GET /maps/:mapId/mapinfo
endpoint
Get map metadata without markersAuthentication: RequiredResponse:
{
  "mapid": 1,
  "name": "Resource Map",
  "ownerid": "123456789",
  "clanid": 1,
  "isPrivate": true
}

Clusters

Endpoints for game cluster information.

Get Clusters

GET /clusters
endpoint
Get a list of available game clusters/serversAuthentication: Not requiredResponse:
[
  {
    "clusterid": 1,
    "name": "NA East",
    "region": "NA",
    "isActive": true
  }
]
// From src/functions/requests/clusters.ts:4-14
export const getClusters = async (): Promise<ClusterInfo[]> => {
  const response = await fetch(`${config.API_URL}/clusters`, {
    method: "GET",
  });

  if (response) {
    return await response.json();
  }

  throw new Error("errors.apiConnection");
};

Discord Bot

Endpoints for Discord bot integration.

Get Bot Configuration

GET /clans/:clanId/discordbot
endpoint
Get Discord bot configuration for a clanAuthentication: RequiredResponse:
{
  "guildid": "123456789",
  "discordid": "987654321"
}

Update Bot Configuration

PUT /clans/:clanId/discordbot
endpoint
Update Discord bot configuration (leader only)Authentication: RequiredQuery Parameters:
  • guildid (optional): Discord server ID
  • discordid (optional): Bot Discord user ID
Response:
{
  "message": "Bot configuration updated successfully"
}

Environment Variables

The following environment variables are required to configure the frontend:
VITE_PUBLIC_URL
string
The public URL of the Stiletto web applicationExample: https://stiletto.deeme.dev
VITE_API_URL
string
The base URL for the Stiletto APIExample: https://api.stiletto.deeme.dev
VITE_RESOURCES_URL
string
URL for game resources (icons, maps, etc.)Example: https://resources.stiletto.deeme.dev
VITE_DISCORD_CLIENT_ID
string
Discord OAuth2 application client IDExample: 123456789012345678Obtain from Discord Developer Portal
VITE_PLAUSIBLE_URL
string
Plausible Analytics URL (optional)Example: https://plausible.io

Error Handling

All API endpoints follow a consistent error handling pattern:

Success Response

When a request succeeds, the API returns:
  • HTTP status code 200 (OK) or 204 (No Content)
  • JSON response with the requested data or a success message

Error Response

When a request fails, the API returns:
  • Appropriate HTTP status code (400, 401, 403, 404, 500, etc.)
  • The frontend throws an error with message "errors.apiConnection"
if (response.ok) {
  return await response.json();
}

throw new Error("errors.apiConnection");

Common Error Codes

  • 400 Bad Request: Invalid request parameters
  • 401 Unauthorized: Missing or invalid authentication token
  • 403 Forbidden: Insufficient permissions
  • 404 Not Found: Resource not found
  • 500 Internal Server Error: Server-side error

Rate Limiting

The API may implement rate limiting to prevent abuse. Check the API repository for current rate limiting policies.

CORS

The API supports Cross-Origin Resource Sharing (CORS) to allow requests from the Stiletto web application domain.

Backend Repository

For more information about the API implementation, deployment, and contribution:

Build docs developers (and LLMs) love