Third-Party Interface

Build a tool that BakoshApp can drive: expose four HTTP endpoints on a local port and your app reacts to TikTok LIVE gifts, likes, follows and chat.

Info
Esta página ainda não foi traduzida para Português (BR) — exibindo o original em inglês.

BakoshApp can trigger actions inside your program. You expose a small HTTP API on a local port, BakoshApp reads your list of actions and calls one whenever a TikTok LIVE event fires.

That means anything you can control from code — smart lights, a microcontroller, a soundboard, another game, your own bot — can react to gifts, likes, follows and chat without a BakoshApp mod.

How it fits together

  1. Your tool starts and listens on http://127.0.0.1:8833.
  2. In BakoshApp the user adds an action: Actions → Add → All Actions → External → Third-Party Action.
  3. BakoshApp calls your API and fills two dropdowns with your categories and actions.
  4. The user picks one and assigns a trigger (Gift, Like, Follow, …).
  5. On every matching TikTok event BakoshApp POSTs to your tool, and your code does the work.
Info

BakoshApp must be running and the user must have selected your action. The connection is one-way: BakoshApp calls you, never the other way round.

Base URL

http://127.0.0.1:8833

Port 8833 is the default. The user can change it in Settings → Third-Party, so do not hard-code an assumption that yours is the only tool on the machine — if 8833 is taken, pick another port and tell your users which one to enter.

Warning

Port 8832 cannot be used: BakoshApp binds it for its own TikFinity-facing API. The Settings field rejects it.

There is no authentication. Bind to loopback (127.0.0.1), not 0.0.0.0, so the API is not reachable from the network.

Endpoints

Your tool must serve all four. Every response is JSON wrapped in a data member.

1. App info

Identifies your tool. BakoshApp shows this as "Connected with Mock Tool 1.0 by Bakosh" in the configurator and in Settings → Test connection.

GET /api/app/info
{
  "data": {
    "name": "Mock Tool",
    "version": "1.0",
    "author": "Bakosh"
  }
}

2. Categories

The first dropdown. Return the groups your actions are organised into.

GET /api/features/categories
{
  "data": [
    { "categoryId": "smart_device_control", "categoryName": "Control Smart Devices" },
    { "categoryId": "serial_port_control",  "categoryName": "Control Serial Port" }
  ]
}

3. Actions

The second dropdown. Return the actions belonging to one category.

GET /api/features/actions?categoryId=smart_device_control
{
  "data": [
    { "actionId": "turn_lights_on",  "actionName": "Turn Lights On" },
    { "actionId": "turn_lights_off", "actionName": "Turn Lights Off" }
  ]
}
Warning

categoryId and actionId are the only handles BakoshApp stores for a configured action. Keep them stable — if you rename an id, existing user setups stop matching. actionName is display-only and safe to change at any time.

4. Execute

Called every time a matching TikTok event fires.

POST /api/features/actions/exec

Request body:

{
  "categoryId": "smart_device_control",
  "actionId": "turn_lights_on",
  "context": {
    "triggerTypeId": 4,
    "eventType": "gift",
    "source": "eulerstream",
    "userId": "123456789",
    "username": "testuser",
    "nickname": "Test User",
    "giftName": "Rose",
    "giftId": 5655,
    "repeatCount": 3,
    "diamondCount": 1,
    "coins": 1,
    "likeCount": 15,
    "profilePictureUrl": "https://…",
    "subMonth": 2
  }
}

Response — return an empty data array once you have accepted the request:

{ "data": [] }
Tip

Answer immediately and do the slow work in the background. If an action takes seconds to finish, BakoshApp's next call may already be on its way — a viewer can send gifts faster than a serial port can move.

The context object

context describes the TikTok event that fired the action. Only fields that exist for that event are present, so always code defensively — check before you read.

FieldTypePresent when
triggerTypeIdnumberAlways — see the table below
eventTypestringAlways — gift, like, chat, follow, share, joined, subscribe, tikfinity, test
sourcestringAlways — eulerstream, tikfinity or test
usernamestringAlmost always — the viewer's @handle
nicknamestringAlmost always — the viewer's display name
userIdstringAlmost always
profilePictureUrlstringUsually — viewer avatar (TikTok CDN links expire)
commentstringChat messages
giftNamestringGift events
giftIdnumberGift events
repeatCountnumberGift events — gifts in the streak
diamondCountnumberGift events — coin value
coinsnumberGift events — alias of diamondCount
likeCountnumberLike events
subMonthnumberSubscribe events
Info

The set is richest on the native connector (PRO), which receives full TikTok event data. When events arrive through TikFinity on the Free plan, expect little more than username.

Trigger types

triggerTypeId tells you why the action fired — it reflects the trigger the user configured, so gift-by-name and gift-by-coin-value are distinguishable:

IdTrigger
1Share
3Gift (min–max coins)
4Gift (specific gift)
6Joined
7Likes
9Follow
10Subscribe
11Chat
0Anything else (Test button, relayed from TikFinity)

Treat unknown ids as 0; more may be added later.

Reporting problems

To surface a problem to the user, put a message in the response body instead of (or alongside) data:

{ "message": "Serial port COM3 is not available" }

BakoshApp records it in its log. HTTP error statuses are respected too, but a message explains what went wrong.

Working with TikFinity too

This interface is deliberately identical to TikFinity's third-party interface — same four paths, same field names. A tool written for one works with the other; only the port differs (TikFinity expects 8832, BakoshApp defaults to 8833). To support both, listen on two ports, or make the port configurable.

Two details matter if you want that portability:

  • CORS. TikFinity is a web app, so it needs Access-Control-Allow-Origin: *, Access-Control-Allow-Headers: * and Access-Control-Allow-Methods: * on every response, plus answers to OPTIONS preflight requests. BakoshApp is a native app and does not need any of this — but sending the headers costs nothing and keeps you compatible.
  • Extra context fields. BakoshApp sends a superset of TikFinity's fields. Ignore what you do not recognise.

A complete working tool

Zero dependencies — save as tool.mjs and run node tool.mjs:

import http from 'node:http';
 
const PORT = 8833;
 
const APP_INFO = { name: 'Mock Tool', version: '1.0', author: 'Bakosh' };
 
const CATALOG = [
  {
    categoryId: 'smart_device_control',
    categoryName: 'Control Smart Devices',
    actions: [
      { actionId: 'turn_lights_on', actionName: 'Turn Lights On' },
      { actionId: 'turn_lights_off', actionName: 'Turn Lights Off' },
    ],
  },
];
 
// Wildcard CORS keeps the same tool usable from TikFinity as well.
const CORS = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': '*',
  'Access-Control-Allow-Methods': '*',
};
 
const send = (res, status, body) => {
  res.writeHead(status, { 'Content-Type': 'application/json', ...CORS });
  res.end(JSON.stringify(body));
};
 
const readBody = (req) =>
  new Promise((resolve) => {
    let raw = '';
    req.on('data', (c) => { raw += c; });
    req.on('end', () => { try { resolve(JSON.parse(raw || '{}')); } catch { resolve({}); } });
  });
 
// Your actual work goes here. Return a string to report a problem.
function runAction(categoryId, actionId, context) {
  console.log(`${categoryId}/${actionId} fired by ${context.username ?? 'someone'}`);
  // turnLightsOn() …
  return null;
}
 
http.createServer(async (req, res) => {
  const url = new URL(req.url, `http://127.0.0.1:${PORT}`);
 
  if (req.method === 'OPTIONS') {           // preflight (TikFinity only)
    res.writeHead(204, CORS);
    return res.end();
  }
 
  if (req.method === 'GET' && url.pathname === '/api/app/info') {
    return send(res, 200, { data: APP_INFO });
  }
 
  if (req.method === 'GET' && url.pathname === '/api/features/categories') {
    return send(res, 200, {
      data: CATALOG.map(({ categoryId, categoryName }) => ({ categoryId, categoryName })),
    });
  }
 
  if (req.method === 'GET' && url.pathname === '/api/features/actions') {
    const cat = CATALOG.find((c) => c.categoryId === url.searchParams.get('categoryId'));
    return send(res, 200, { data: cat ? cat.actions : [] });
  }
 
  if (req.method === 'POST' && url.pathname === '/api/features/actions/exec') {
    const body = await readBody(req);
    const problem = runAction(body.categoryId ?? '', body.actionId ?? '', body.context ?? {});
    return problem ? send(res, 200, { message: problem }) : send(res, 200, { data: [] });
  }
 
  send(res, 404, { message: `No such endpoint: ${url.pathname}` });
}).listen(PORT, '127.0.0.1', () => console.log(`Listening on http://127.0.0.1:${PORT}`));

Testing your tool

  1. Start your tool.
  2. In BakoshApp open Settings → Third-Party, check the host and port, and press Test connection. You should see your name, version and author.
  3. Go to a mod's Actions tab → Add → pick a trigger → ExternalThird-Party ActionConfigure.
  4. Choose your category and action, then Save.
  5. Press Test (▶) on the new row — your tool receives an exec with eventType: "test".
  6. Go live and confirm real events arrive.
Tip

If the configurator says "Unable to connect", check that your tool is listening on 127.0.0.1 (not only on an external interface), that the port matches Settings, and that GET /api/app/info returns the data wrapper.

Checklist

  • All four endpoints answer, each wrapping its payload in data.
  • Bound to 127.0.0.1 on a port that is not 8832.
  • categoryId / actionId values are stable across restarts.
  • exec returns promptly; slow work is backgrounded.
  • Unknown context fields are ignored rather than rejected.
  • Problems are reported through message.