TALQENDocs

Examples

Starting-point samples in Roblox Lua, curl, and TypeScript. Placeholders only — never paste real credentials into this documentation or your client.

Roblox Lua

Compact server startlua
-- Server Script only (ServerScriptService). Enable HttpService.
local HttpService = game:GetService("HttpService")

local API_BASE = "https://api-staging.talqen.net/v1"
local API_KEY = "<TALQEN_TEST_API_KEY>" -- ServerStorage / secret store

local function startShift(player)
  local response = HttpService:RequestAsync({
    Url = API_BASE .. "/activity/shifts/start",
    Method = "POST",
    Headers = {
      ["Authorization"] = "Bearer " .. API_KEY,
      ["Content-Type"] = "application/json",
      ["Idempotency-Key"] = "start-" .. player.UserId .. "-" .. HttpService:GenerateGUID(false),
    },
    Body = HttpService:JSONEncode({
      robloxUserId = tostring(player.UserId),
      universeId = tostring(game.GameId),
      placeId = tostring(game.PlaceId),
      serverId = game.JobId,
    }),
  })

  if not response.Success then
    warn("Talqen start failed", response.StatusCode)
    return nil
  end

  local payload = HttpService:JSONDecode(response.Body)
  return payload.data.shift.id
end
Helper ModuleScriptlua
-- Starting point only. Place in ServerScriptService.
-- Never use a LocalScript. Never store keys in ReplicatedStorage.

local HttpService = game:GetService("HttpService")
local Players = game:GetService("Players")

local API_BASE = "https://api-staging.talqen.net/v1"
local API_KEY = "<TALQEN_TEST_API_KEY>"

local activeShifts = {}

local function talqenRequest(method, path, body, idempotencyKey)
  local headers = {
    ["Authorization"] = "Bearer " .. API_KEY,
    ["Content-Type"] = "application/json",
  }
  if idempotencyKey then
    headers["Idempotency-Key"] = idempotencyKey
  end

  local ok, response = pcall(function()
    return HttpService:RequestAsync({
      Url = API_BASE .. path,
      Method = method,
      Headers = headers,
      Body = body and HttpService:JSONEncode(body) or nil,
    })
  end)

  if not ok then
    warn("Talqen request failed")
    return nil
  end

  if not response.Success then
    -- Log status and request id only — never log the API key.
    warn("Talqen API error", response.StatusCode, response.Headers["x-request-id"])
    return nil
  end

  return HttpService:JSONDecode(response.Body)
end

local function startShift(player)
  local payload = talqenRequest("POST", "/activity/shifts/start", {
    robloxUserId = tostring(player.UserId),
    universeId = tostring(game.GameId),
    placeId = tostring(game.PlaceId),
    serverId = game.JobId,
  }, "start-" .. player.UserId .. "-" .. HttpService:GenerateGUID(false))

  if payload and payload.data and payload.data.shift then
    activeShifts[player.UserId] = payload.data.shift.id
    return payload.data.shift.id
  end
end

local function heartbeatShift(shiftId)
  talqenRequest("POST", "/activity/shifts/" .. shiftId .. "/heartbeat", {
    serverId = game.JobId,
  }, "heartbeat-" .. shiftId .. "-" .. HttpService:GenerateGUID(false))
end

local function stopShift(shiftId, reason)
  talqenRequest("POST", "/activity/shifts/" .. shiftId .. "/stop", {
    reason = reason or "manual",
  }, "stop-" .. shiftId .. "-" .. HttpService:GenerateGUID(false))
end

return {
  startShift = startShift,
  heartbeatShift = heartbeatShift,
  stopShift = stopShift,
  activeShifts = activeShifts,
}
Clock-in RemoteEventlua
-- Starting point: custom clock-in RemoteEvent handled server-side.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Talqen = require(script.Parent.TalqenClient) -- your ModuleScript helper

local clockIn = Instance.new("RemoteEvent")
clockIn.Name = "TalqenClockIn"
clockIn.Parent = ReplicatedStorage

clockIn.OnServerEvent:Connect(function(player)
  -- Enforce your own eligibility rules here (team, rank, etc.).
  local shiftId = Talqen.startShift(player)
  if shiftId then
    -- Store shiftId server-side for heartbeat and disconnect cleanup.
  end
end)
Team change trackinglua
-- Starting point: track Team changes as shift boundaries.
local Teams = game:GetService("Teams")
local Talqen = require(script.Parent.TalqenClient)

local STAFF_TEAM_NAME = "Staff"

local function onTeamChanged(player)
  local isStaff = player.Team and player.Team.Name == STAFF_TEAM_NAME
  local existing = Talqen.activeShifts[player.UserId]

  if isStaff and not existing then
    Talqen.startShift(player)
  elseif (not isStaff) and existing then
    Talqen.stopShift(existing, "manual")
    Talqen.activeShifts[player.UserId] = nil
  end
end

-- Wire Player:GetPropertyChangedSignal("Team") / team assignment in your game.
Disconnect cleanuplua
-- Starting point: clean up on disconnect and shutdown.
local Players = game:GetService("Players")
local Talqen = require(script.Parent.TalqenClient)

Players.PlayerRemoving:Connect(function(player)
  local shiftId = Talqen.activeShifts[player.UserId]
  if shiftId then
    Talqen.stopShift(shiftId, "player_left")
    Talqen.activeShifts[player.UserId] = nil
  end
end)

game:BindToClose(function()
  for userId, shiftId in pairs(Talqen.activeShifts) do
    Talqen.stopShift(shiftId, "server_shutdown")
    Talqen.activeShifts[userId] = nil
  end
end)

curl

Start shiftbash
curl -X POST "https://api-staging.talqen.net/v1/activity/shifts/start" \
  -H "Authorization: Bearer <TALQEN_TEST_API_KEY>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: start-<ROBLOX_USER_ID>-001" \
  -d '{
    "robloxUserId": "<ROBLOX_USER_ID>",
    "universeId": "<UNIVERSE_ID>",
    "placeId": "<PLACE_ID>",
    "serverId": "<SERVER_ID>"
  }'
Leaderboardbash
curl "https://api-staging.talqen.net/v1/activity/leaderboard?period=week&limit=25" \
  -H "Authorization: Bearer <TALQEN_TEST_API_KEY>"

TypeScript

Start shiftts
const API_BASE = "https://api-staging.talqen.net/v1";
const API_KEY = "<TALQEN_TEST_API_KEY>";

async function startShift(input: {
  robloxUserId: string;
  universeId: string;
  placeId: string;
  serverId: string;
  idempotencyKey: string;
}) {
  const response = await fetch(`${API_BASE}/activity/shifts/start`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": input.idempotencyKey,
    },
    body: JSON.stringify({
      robloxUserId: input.robloxUserId,
      universeId: input.universeId,
      placeId: input.placeId,
      serverId: input.serverId,
    }),
  });

  const requestId = response.headers.get("x-request-id");
  const body = await response.json();

  if (!response.ok) {
    throw new Error(
      `Talqen error ${body?.error?.code ?? response.status} (requestId=${requestId})`,
    );
  }

  return body.data as {
    shift: { id: string; status: string; startedAt: string; lastHeartbeatAt: string };
    existing?: boolean;
  };
}
Heartbeatts
async function heartbeatShift(shiftId: string, idempotencyKey: string) {
  const response = await fetch(
    `${API_BASE}/activity/shifts/${shiftId}/heartbeat`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": idempotencyKey,
      },
      body: JSON.stringify({ serverId: "<SERVER_ID>" }),
    },
  );

  if (!response.ok) {
    const body = await response.json();
    throw new Error(body?.error?.message ?? "Heartbeat failed");
  }

  return response.json();
}