Help, routes, and examples for UI integrations

App Functions

Choose a function from the menu. The help page opens on the right side without leaving this screen.

SDK

Use the JavaScript SDK when building web, React, Vue, Flutter WebView, or Android WebView screens. Define the domain once, login once, then reuse the same client for REST calls and realtime.

  1. Include https://{domain}/app/sdk/biz1-sdk.js in the UI.
  2. Create one Biz1SDK.Biz1Client at app startup with domain and a storage object.
  3. Call client.login(). The SDK saves the bearer token in biz1_sdk_bearer_token.
  4. After login, call client.account.basic() and store user, organization, folders, statuses, team members, and field settings for the UI.
  5. Use route helpers like client.customers.list(), dynamic helpers like client.routes.Customer.List(), or generic client.request().
  6. For lists, send filters and paging, but keep length, limit, or per_page at 25 or less.
  7. Pass JavaScript Date objects or local datetime strings for date fields. The SDK posts UTC Y-m-d H:i:s; the server stores the value according to the table column type.
  8. Store ids returned from list/add calls, then reuse them for single, update, remove, documents, chat, recordings, missions, tickets, and related calls.
  9. If any request returns 401, the SDK clears the token. Show login again.

Include SDK

<script src="https://{domain}/app/sdk/biz1-sdk.js"></script>

Basic SDK Flow

const client = new Biz1SDK.Biz1Client({
  domain: 'https://{user}.bull36.com',
  storage: localStorage
});

await client.login({
  username: 'USER EMAIL',
  password: 'USER PASSWORD'
});

const user = await client.account.basic();
const customers = await client.customers.list({ folder_id: 1, length: 25 });
const total = await client.customers.count({ folder_id: 1 });
const single = await client.customers.get(customers.rows[0].customer_id || customers.rows[0].id);

console.log({ user, customers, total, single });

Any Route

const result = await client.request('Customer.List', {
  folder_id: 1,
  length: 25
});

const sameResult = await client.routes.Customer.List({
  folder_id: 1,
  length: 25
});

Date/Time Fields

await client.customers.add({
  name: 'John Demo',
  phone: '0500000000',
  followup: new Date(2026, 6, 20, 10, 0, 0)
});

// The SDK posts followup as UTC Y-m-d H:i:s.
const utcValue = Biz1SDK.toUtcDateTime(new Date(2026, 6, 20, 10, 0, 0));

Realtime

<script src="https://{domain}/realtime/socket.io/socket.io.js"></script>
<script src="https://{domain}/app/sdk/biz1-sdk.js"></script>

const socket = client.realtime.connect({
  platform: 'web',
  path: '/realtime/socket.io'
});

client.realtime.on('biz1:ready', function (payload) {
  console.log('socket ready', payload);
});

client.realtime.on('*', function (event) {
  console.log('realtime event', event);
});

Socket

Use the socket after login to update open screens without polling. The same bearer token used for API calls is used to register the socket device.

  1. Login with /app/Login or client.login() and keep the bearer token in app state.
  2. Connect to https://{domain} with path /realtime/socket.io.
  3. Wait for biz1:ready. The payload includes the user id and enabled event list.
  4. Listen to biz1:event or use SDK client.realtime.on('*') for all enabled events.
  5. For better performance, listen to specific event keys and refresh only the affected list, customer, mission, message, calendar, or settings cache.

Connect Sample

<script src="https://{domain}/realtime/socket.io/socket.io.js"></script>
<script src="https://{domain}/app/sdk/biz1-sdk.js"></script>

const client = new Biz1SDK.Biz1Client({
  domain: 'https://{domain}',
  storage: localStorage
});

await client.login({
  username: 'USER EMAIL',
  password: 'USER PASSWORD'
});

const socket = client.realtime.connect({
  path: '/realtime/socket.io',
  platform: 'web'
});

client.realtime.on('biz1:ready', function (payload) {
  console.log('Socket ready', payload.userId, payload.events);
});

client.realtime.on('*', function (event) {
  console.log('Any socket event', event.key, event.payload);
});

Specific Listeners

client.realtime.on('crm.lead.created', function (event) {
  const customerId = event.payload.customer_id;
  refreshCustomerList();
  openCustomerBadge(customerId);
});

client.realtime.on('mission.created', function (event) {
  refreshMissionList({ customer_id: event.payload.customer_id });
});

client.realtime.on('message.created', function (event) {
  refreshCustomerMessages(event.payload.customer_id);
});

client.realtime.on('customer.followup', function (event) {
  refreshCustomer(event.payload.customer_id);
});

Receive JSON: New Customer

{
  "id": 1784268468521,
  "key": "crm.lead.created",
  "createdAt": "2026-07-21T08:30:00.000Z",
  "source": "server",
  "payload": {
    "customer_id": 13745935,
    "event": "add_lead",
    "customer": {
      "id": 13745935,
      "name": "John Demo",
      "email": "[email protected]",
      "mobile": "0500000000"
    },
    "socket_registered_user_ids": [47]
  }
}

Receive JSON: Mission

{
  "id": 1784268468522,
  "key": "mission.created",
  "createdAt": "2026-07-21T08:31:00.000Z",
  "source": "app-node",
  "payload": {
    "mission_id": 500,
    "customer_id": 13745935
  }
}

Receive JSON: Status Or Internal Status

{
  "id": 1784268468523,
  "key": "statuses.edit.updated",
  "createdAt": "2026-07-21T08:32:00.000Z",
  "source": "app-node",
  "payload": {
    "id": 12,
    "route": "Statuses.Edit",
    "table": "all_status",
    "changed": ["name_he", "color"],
    "data": {
      "name_he": "In Progress",
      "color": "#2f80ed",
      "type": "internal_status"
    }
  }
}

Main Event Groups

  • New customer and follow-up: crm.lead.created, customer.updated, customer.followup, customer.restored
  • Messages and email: chat.message.received, message.created, mission.message.created, project.message.created, message.mark_read, message.replied, message.forwarded, message.deleted, email.created, email.deleted
  • Mission and reminder: mission.created, mission.updated, mission.done, mission.reopened, mission.deleted, mission.reminder, mission.open_popup, automation.reminder.popup
  • Meeting and appointment: meeting.created, meeting.updated, meeting.deleted, automation.appointment.open_popup, appointment.created, appointment.updated, appointment.deleted
  • Calls: call.incoming, call.status.popup
  • Tabs and statuses: entries.created, entries.updated, entries.deleted, statuses.add.created, statuses.edit.updated, statuses.delete.deleted
  • Internal status: Use the statuses.* events where payload.data.type is internal_status or the route help says it changes internal status.

When a route response contains socket_event, the same key can be used in client.realtime.on(socket_event, handler).

Prompt Date Rule

When using the prompt below, also tell the AI that every date/time field must be sent to the API as UTC Y-m-d H:i:s. The Biz1 SDK converts user local Date values and local datetime strings before POST.

Prompt

How To Call

POST /app/{Route.Name}
POST https://{user}.bull36.com/app/{Route.Name}
POST https://{domain}/app/{Route.Name}
Authorization: Bearer YOUR TOKEN

Instructions

  1. Call /app/Login with the user login details from your UI.
  2. If the response asks for OTP, show an OTP screen and finish login with the required code.
  3. When login returns a bearer token, save it in your app session storage or secure app state.
  4. For every protected function, send Authorization: Bearer YOUR TOKEN in the request header.
  5. Send function parameters as POST body fields. List routes allow paging and never return more than 25 rows per call.
  6. For every date/time field, work in the user local time in the UI, then send UTC Y-m-d H:i:s to the API. The SDK does this automatically for JavaScript Date values and known date fields.
  7. If a call returns 401, clear the saved token and send the user back to login.

Async Login Sample

async function getBearerToken({ domain = 'https://{domain}', username, password, otp = '' }) {
  const body = new URLSearchParams({ username, password });
  if (otp) body.set('otp', otp);

  const res = await fetch(`${domain}/app/Login`, {
    method: 'POST',
    body
  });

  const data = await res.json();
  if (data.otp_required) return { otpRequired: true, message: data.message, data };
  if (!data.token) throw new Error(data.message || 'Login failed');

  return {
    token: data.token,
    tokenType: data.token_type || 'Bearer',
    expiresAt: data.expires_at || null,
    raw: data
  };
}

const auth = await getBearerToken({
  domain: 'https://{user}.bull36.com',
  username: 'USER EMAIL',
  password: 'USER PASSWORD'
});

const token = auth.token;
// Use on every protected call:
// headers: { Authorization: `Bearer ${token}` }