API docs — Super Admin only

API dokumentacija — samo Super Admin

Checking access…

Provjera pristupa…

DigitalChip API

DigitalChip Platform API Reference

API referenca za DigitalChip platformu

Complete Cloud Functions API — IoT gateway, mobile client, B2B, tenant and admin callables.

Kompletan Cloud Functions API — IoT gateway, mobilni klijent, B2B, tenant i admin callable funkcije.

Region: us-central1 · Project: yosh-digitalchip Region: us-central1 · Projekat: yosh-digitalchip

Overview

Pregled

Backend is Firebase Cloud Functions (v2). There are two transport styles:

Backend su Firebase Cloud Functions (v2). Postoje dva tipa transporta:

Base URL pattern for HTTP functions:

Osnovni URL obrazac za HTTP funkcije:

https://us-central1-yosh-digitalchip.cloudfunctions.net/<functionName>

API Catalog

Katalog API-ja

Function Type Who Ko Purpose Svrha
gatewayPing POST IoT controller IoT kontroler Heartbeat + fetch pending commands (still fully supported) Heartbeat + preuzimanje komandi (i dalje potpuno podržano)
gatewayStream GET/POST SSE IoT controller IoT kontroler Outbound push stream for commands (no static IP) Outbound push stream za komande (bez statične IP)
MQTT + mqttAuth MQTT / POST IoT + EMQX IoT + EMQX Recommended push for larger fleets (~500 devices) Preporučeni push za veće flote (~500 uređaja)
activateService onCall USER / guest USER / gost Spend tokens & queue START_SERVICE Potroši tokene i stavi START_SERVICE u red
getUserProfile onCall Any signed-in user Bilo koji ulogovani Read own profile Čitanje vlastitog profila
updateUserProfile onCall Any signed-in user Bilo koji ulogovani Update name & phone Ažuriranje imena i telefona
changeUserPassword onCall Email/password users Email/lozinka nalozi Change password securely Sigurna promjena lozinke
onboardEmployee onCall B2B_ADMIN / SUPER_ADMIN B2B_ADMIN / SUPER_ADMIN Create/link employee account Kreiranje/povezivanje zaposlenog
dispenseTokens onCall B2B_ADMIN / SUPER_ADMIN B2B_ADMIN / SUPER_ADMIN Company wallet → employee wallet Prenos kompanija → zaposleni
getCompanySpendingReport onCall B2B_ADMIN / SUPER_ADMIN B2B_ADMIN / SUPER_ADMIN Corporate DEBIT spend report Izvještaj potrošnje kompanije
listVehicles / createVehicle / updateVehicle / deactivateVehicle onCall Owner / B2B_ADMIN / SUPER Vlasnik / B2B_ADMIN / SUPER Personal or company fleet CRUD Lična / kompanijska flota
getVehicleWashStats onCall B2B_ADMIN / SUPER_ADMIN B2B_ADMIN / SUPER_ADMIN Per-vehicle wash / token stats Statistika pranja po vozilu
listWashHistory onCall Signed-in user Ulogovani korisnik Wash history (+ optional vehicle filter) Istorija pranja (+ filter vozila)
createTenantWithAdmin onCall SUPER_ADMIN SUPER_ADMIN Create tenant + TENANT_ADMIN login Kreiranje tenanta + TENANT_ADMIN
rotateDeviceApiKey onCall SUPER_ADMIN / TENANT_ADMIN SUPER_ADMIN / TENANT_ADMIN Rotate parent device API key Rotacija API ključa uređaja
getEarningsReport onCall SUPER_ADMIN / TENANT_ADMIN SUPER_ADMIN / TENANT_ADMIN Earnings (gross / per-tenant fee / net) Zarada (bruto / fee po tenantu / neto)
creditOwnWallet onCall Any signed-in user Bilo koji ulogovani Credit / replace own wallet balance Dopuna / zamjena vlastitog stanja
updateSpendingRules onCall B2B_ADMIN / SUPER_ADMIN B2B_ADMIN / SUPER_ADMIN Update company spending rules Ažuriranje pravila potrošnje
createB2bCompany onCall SUPER_ADMIN SUPER_ADMIN Create B2B company + wallet Kreiranje B2B kompanije + novčanik
deleteB2bCompany onCall SUPER_ADMIN SUPER_ADMIN Delete company & unlink employees Brisanje kompanije i odvajanje zaposlenih
assignCompanyAdmin onCall SUPER_ADMIN SUPER_ADMIN Promote user to B2B_ADMIN Postavljanje B2B_ADMIN
removeCompanyEmployee onCall B2B_ADMIN / SUPER_ADMIN B2B_ADMIN / SUPER_ADMIN Unlink employee from company Uklanjanje zaposlenog iz kompanije
registerDevice onCall SUPER_ADMIN / TENANT_ADMIN SUPER_ADMIN / TENANT_ADMIN Register parent device (+ apiKey) Registracija uređaja (+ apiKey)
registerSubDevice onCall SUPER_ADMIN / TENANT_ADMIN SUPER_ADMIN / TENANT_ADMIN Register bay / sub-device Registracija bay / poduređaja
deleteDevice / deleteSubDevice onCall SUPER_ADMIN / TENANT_ADMIN SUPER_ADMIN / TENANT_ADMIN Delete device or sub-device Brisanje uređaja ili poduređaja
linkDeviceToTenant onCall SUPER_ADMIN SUPER_ADMIN Move device to another tenant Premještanje uređaja na drugi tenant
adminUpdateUser onCall SUPER_ADMIN SUPER_ADMIN Update role / company / tenant link Uloga / kompanija / tenant veza
creditUserWallet onCall SUPER_ADMIN SUPER_ADMIN Credit any user wallet Dopuna bilo kojeg novčanika
deleteUserAccount onCall SUPER_ADMIN SUPER_ADMIN Delete Auth user + profile + wallet Brisanje Auth + profil + novčanik
deleteTenantCascade onCall SUPER_ADMIN SUPER_ADMIN Cascade delete tenant + devices Kaskadno brisanje tenanta + uređaja

IoT Device Gateway Ping

Pingovanje IoT uređaja (Gateway)

The physical controller on-site reports its telemetry and can fetch pending relay commands. This endpoint remains fully supported. For lower-latency command delivery without a static IP, also open gatewayStream.

Fizički kontroler na lokaciji prijavljuje telemetriju i može preuzeti zapovijedi za releje. Ovaj endpoint ostaje potpuno podržan. Za bržu isporuku komandi bez statične IP, otvorite i gatewayStream.

POST https://us-central1-yosh-digitalchip.cloudfunctions.net/gatewayPing

Authentication required

Obavezna autentifikacija

Every ping must include the parent device API key in the HTTP header. The key is generated automatically when the controller is registered and is unique per parent device (not per sub-device).

Svaki ping mora sadržati API ključ parent uređaja u HTTP headeru. Ključ se automatski generiše pri registraciji kontrolera i jedinstven je po parent uređaju (ne po poduređaju).

Required Headers

Obavezni headeri

Header Type Requirement Obaveznost Description Opis
Content-Type String RequiredObavezno application/json
X-Device-Api-Key String RequiredObavezno Secret API key stored on the parent device document (devices/{deviceId}.apiKey). Alias header X-Api-Key is also accepted. Tajni API ključ sa parent device dokumenta (devices/{deviceId}.apiKey). Prihvata se i alias header X-Api-Key.
cURL Example cURL primjer
curl -X POST https://us-central1-yosh-digitalchip.cloudfunctions.net/gatewayPing \
  -H "Content-Type: application/json" \
  -H "X-Device-Api-Key: dc_dev_wash_01_api_key_demo_2026" \
  -d '{
    "deviceId": "dev_wash_01",
    "status": "ONLINE",
    "subDevices": [
      { "id": "bay_1", "status": "ACTIVE" },
      { "id": "bay_2", "status": "INACTIVE" }
    ]
  }'

Request Payload

Tijelo zahtjeva (Request Payload)

Field Type Requirement Obaveznost Description Opis
deviceId String RequiredObavezno Unique hardware UUID or MAC address. Jedinstveni UUID ili MAC adresa kontrolera.
status String RequiredObavezno "ONLINE" | "OFFLINE"
subDevices Array RequiredObavezno Current state list of connected sub-modules (e.g. wash bays, washing machines). Trenutno stanje povezanih jedinica (mjesta za pranje, releja).
subDevices[].id String RequiredObavezno Sub-device identifier (e.g., "bay_1"). ID poduređaja (npr. "bay_1").
subDevices[].status String RequiredObavezno "ACTIVE" | "INACTIVE" | "ERROR"
Request Body Example Primjer tijela zahtjeva
{
  "deviceId": "dev_wash_01",
  "status": "ONLINE",
  "subDevices": [
    { "id": "bay_1", "status": "ACTIVE" },
    { "id": "bay_2", "status": "INACTIVE" }
  ]
}

Gateway Response

Odgovor mrežnog prolaza

Field Type Description Opis
timestamp String Server ISO timestamp. Serverska ISO vremenska oznaka.
commands Array List of active activation commands (maximum 5). Status automatically changes to SENT on delivery. Lista aktivnih zapovijedi za aktivaciju (najviše 5). Status automatski prelazi u SENT.
commands[].id String Command ID token. ID oznaka zapovijedi.
commands[].action String "START_SERVICE" or "STOP_SERVICE"
commands[].subDeviceId String Specific sub-device port/bay. Port/relej poduređaja koji treba pokrenuti.
commands[].value Number Token amount / service units (calculated pulses) to apply on the device. Količina tokena / jedinica usluge (izraženo u impulsima) za uređaj.
Response Body Example Primjer odgovora
{
  "timestamp": "2026-07-14T10:52:45.312Z",
  "commands": [
    {
      "id": "cmd_8xL92kPz1",
      "action": "START_SERVICE",
      "subDeviceId": "bay_1",
      "value": 50
    }
  ]
}
Response Body Example (No Pending Commands) Primjer odgovora (Kada nema zapovijedi na čekanju)
{
  "timestamp": "2026-07-14T10:52:48.125Z",
  "commands": []
}

IoT Command Stream (gatewayStream)

IoT stream komandi (gatewayStream)

Outbound Server-Sent Events (SSE) stream. The controller opens a long-lived connection to the cloud (works behind NAT / without a static IP). When a wash is activated, pending commands are pushed immediately using the same JSON shape as gatewayPing. Keep gatewayPing for telemetry / online status (and as command fallback).

Outbound Server-Sent Events (SSE) stream. Kontroler otvara dugotrajnu konekciju ka cloudu (radi iza NAT-a / bez statične IP). Kad se aktivira pranje, PENDING komande se šalju odmah u istom JSON formatu kao gatewayPing. Zadržite gatewayPing za telemetriju / online status (i kao fallback za komande).

GET https://us-central1-yosh-digitalchip.cloudfunctions.net/gatewayStream?deviceId=dev_wash_01

Also accepts POST with JSON body {"deviceId":"..."}. Response Content-Type: text/event-stream. Prihvata i POST sa JSON tijelom {"deviceId":"..."}. Odgovor Content-Type: text/event-stream.

Why not a device webhook URL?

Zašto ne webhook URL na uređaju?

Most controllers have no public/static IP. The cloud cannot reliably POST to the device. The device must open an outbound connection instead.

Većina kontrolera nema javnu/statičnu IP. Cloud ne može pouzdano POST-ovati na uređaj. Uređaj mora otvoriti outbound konekciju.

Required Headers

Obavezni headeri

Header Type Requirement Obaveznost Description Opis
X-Device-Api-Key String RequiredObavezno Same parent-device API key as gatewayPing. Alias X-Api-Key accepted. Isti API ključ parent uređaja kao za gatewayPing. Prihvata se i X-Api-Key.
cURL Example (SSE) cURL primjer (SSE)
curl -N "https://us-central1-yosh-digitalchip.cloudfunctions.net/gatewayStream?deviceId=dev_wash_01" \
  -H "X-Device-Api-Key: dc_dev_wash_01_api_key_demo_2026" \
  -H "Accept: text/event-stream"

SSE event format

Format SSE događaja

Each command push is one SSE data: line whose JSON matches the gatewayPing response body exactly:

Svaki push komandi je jedna SSE data: linija čiji JSON je identičan tijelu odgovora gatewayPing:

Pushed event example Primjer push događaja
data: {"timestamp":"2026-07-30T04:52:45.312Z","commands":[{"id":"cmd_8xL92kPz1","action":"START_SERVICE","subDeviceId":"bay_1","value":50}]}

: keepalive 2026-07-30T04:53:00.000Z

Recommended firmware pattern

Preporučeni pattern u firmveru

  1. Open gatewayStream and keep it open; on disconnect, reconnect with backoff. Otvorite gatewayStream i držite otvorenim; na disconnect reconnect sa backoff-om.
  2. On each data: JSON with a non-empty commands array, execute commands exactly as for gatewayPing. Na svaki data: JSON sa nepraznim commands, izvršite komande isto kao za gatewayPing.
  3. Continue gatewayPing for bay status / Live Tracking (every 3s still OK; with stream you may slow ping to 30–60s if desired). Nastavite gatewayPing za status boksova / Live Tracking (3s i dalje OK; uz stream možete usporiti ping na 30–60s).

MQTT command delivery (recommended for fleets)

MQTT isporuka komandi (preporučeno za flote)

For larger fleets (tens to hundreds of controllers), prefer MQTT over polling every 3s or long-lived SSE. The device opens an outbound MQTT connection to a managed broker (e.g. EMQX Cloud). DigitalChip publishes commands when a wash is activated — same JSON as gatewayPing. Keep a slower gatewayPing for bay telemetry / Live Tracking.

Za veće flote (desetine do stotine kontrolera) preferirajte MQTT umjesto pinga svake 3s ili dugog SSE. Uređaj otvara outbound MQTT konekciju ka managed brokeru (npr. EMQX Cloud). DigitalChip publish-uje komande kad se aktivira pranje — isti JSON kao gatewayPing. Zadržite sporiji gatewayPing za telemetriju boksova / Live Tracking.

Architecture

Arhitektura

activateService / stopService → Firestore PENDING command → Cloud Function onDeviceCommandCreated publishes to topic → device executes. If MQTT is not configured or publish fails, the command stays/reverts to PENDING and gatewayPing / gatewayStream still deliver it.

activateService / stopService → Firestore PENDING komanda → Cloud Function onDeviceCommandCreated publish na topic → uređaj izvršava. Ako MQTT nije podešen ili publish padne, komanda ostaje/vrati se u PENDING i isporučuju je gatewayPing / gatewayStream.

1. Broker setup (EMQX Cloud)

1. Podešavanje brokera (EMQX Cloud)

  1. Create an EMQX Cloud deployment (Serverless is fine to start). Note the mqtts://…:8883 endpoint. Kreirajte EMQX Cloud deployment (Serverless je OK za početak). Zabilježite mqtts://…:8883 endpoint.
  2. Create a publisher user (e.g. digitalchip_publisher) with ACL allowing publish to digitalchip/+/commands. Kreirajte publisher korisnika (npr. digitalchip_publisher) sa ACL-om koji dozvoljava publish na digitalchip/+/commands.
  3. Add an HTTP authentication authenticator pointing at mqttAuth (below) so devices log in with deviceId + apiKey. Dodajte HTTP authentication authenticator ka mqttAuth (ispod) da se uređaji prijave sa deviceId + apiKey.
  4. Device ACL (in EMQX): each client may subscribe only to digitalchip/${username}/commands (and usually not publish). ACL uređaja (u EMQX): klijent smije samo subscribe na digitalchip/${username}/commands (obično bez publish-a).

2. DigitalChip Cloud params

2. DigitalChip Cloud parametri

Set environment params for Cloud Functions (file functions/.env.yosh-digitalchip or Google Cloud Console → Cloud Functions / Cloud Run env):

Postavite env parametre za Cloud Functions (fajl functions/.env.yosh-digitalchip ili Google Cloud Console → Cloud Functions / Cloud Run env):

functions/.env.yosh-digitalchip
MQTT_URL=mqtts://xxxxx.emqxsl.com:8883
MQTT_USERNAME=digitalchip_publisher
MQTT_PASSWORD=your_publisher_password

If MQTT_URL is empty, MQTT publish is skipped (no error) — ping/stream keep working.

Ako je MQTT_URL prazan, MQTT publish se preskače (bez greške) — ping/stream i dalje rade.

3. HTTP auth endpoint for EMQX

3. HTTP auth endpoint za EMQX

POST https://us-central1-yosh-digitalchip.cloudfunctions.net/mqttAuth

EMQX sends device credentials. DigitalChip checks devices/{username}.apiKey.

EMQX šalje kredencijale uređaja. DigitalChip provjerava devices/{username}.apiKey.

Field Description Opis
username Must equal registered deviceId. Mora biti registrovani deviceId.
password Parent device apiKey (same as X-Device-Api-Key on ping). Parent apiKey (isti kao X-Device-Api-Key na pingu).
Example EMQX → mqttAuth body Primjer tijela EMQX → mqttAuth
{
  "username": "dev_wash_01",
  "password": "dc_…",
  "clientid": "dev_wash_01-fw"
}
Allow response Odgovor allow
{
  "result": "allow",
  "is_superuser": false,
  "acl": [
    { "permission": "allow", "action": "subscribe", "topic": "digitalchip/dev_wash_01/commands" },
    { "permission": "deny", "action": "all", "topic": "#" }
  ]
}

Deny / unknown device → {"result":"deny"} (HTTP 200 — EMQX convention).

Deny / nepoznat uređaj → {"result":"deny"} (HTTP 200 — EMQX konvencija).

4. Device MQTT client

4. MQTT klijent na uređaju

Setting Postavka Value
Broker URL Broker URL mqtts://…:8883 (from EMQX)
Username Korisničko ime deviceId
Password Lozinka apiKey
Subscribe topic Topic za subscribe digitalchip/{deviceId}/commands
QoS QoS 1 recommended
Client ID Client ID {deviceId}-fw

5. Message payload (same as gatewayPing)

5. Payload poruke (isti kao gatewayPing)

UTF-8 JSON body on the topic (not wrapped in SSE). Process exactly like a ping response:

UTF-8 JSON na topicu (nije SSE). Obrada identična odgovoru na ping:

Published message example Primjer publish poruke
{
  "timestamp": "2026-07-30T05:52:45.312Z",
  "commands": [
    {
      "id": "cmd_8xL92kPz1",
      "action": "START_SERVICE",
      "subDeviceId": "bay_1",
      "value": 50
    }
  ]
}

6. Cloud Functions involved

6. Uključene Cloud Functions

Function Type Role Uloga
mqttAuth HTTP POST EMQX HTTP authenticator EMQX HTTP authenticator
onDeviceCommandCreated Firestore trigger Publish on new PENDING command Publish na novu PENDING komandu

Firmware Integration Flow

Tok integracije na kontroleru

Recommended stack: MQTT for commands + gatewayPing every 30–60s for telemetry. gatewayStream and 3s ping remain fully supported fallbacks.

Preporučeni stack: MQTT za komande + gatewayPing svakih 30–60s za telemetriju. gatewayStream i 3s ping ostaju potpuno podržani fallback-ovi.

  1. Boot & Connect Pokretanje i mrežna veza: Connect to network (Wi-Fi/LTE), load the provisioned deviceId + apiKey from secure storage, and sync time. Povežite se na mrežu (Wi-Fi/LTE), učitajte dodijeljeni deviceId + apiKey iz sigurnog skladišta i sinhronizujte sat ako je moguće.
  2. MQTT (recommended) MQTT (preporučeno): Connect to the broker with username=deviceId, password=apiKey. Subscribe to digitalchip/{deviceId}/commands. On message, parse JSON and run commands like a ping response. See MQTT section. Konektujte se na broker sa username=deviceId, password=apiKey. Subscribe na digitalchip/{deviceId}/commands. Na poruku parsirajte JSON i izvršite commands kao kod ping odgovora. Vidi MQTT sekciju.
  3. Command stream SSE (optional fallback) SSE stream (opcioni fallback): Open gatewayStream if MQTT is unavailable. Process pushed commands the same way. Otvorite gatewayStream ako MQTT nije dostupan. Obradjujte pushane commands isto.
  4. Poll Loop (required for telemetry) Petlja pinga (obavezna za telemetriju): POST gatewayPing with header X-Device-Api-Key and JSON body (controller + sub-device status). Stamps lastPing / lastSeenAt (Live Tracking: online if last ping < ~3 minutes). With MQTT, 30–60 seconds is enough; 3 seconds remains supported. POST gatewayPing sa headerom X-Device-Api-Key i JSON tijelom (stanje kontrolera i boksova). Upisuje lastPing / lastSeenAt (Live Tracking: online ako je zadnji ping < ~3 minuta). Uz MQTT dovoljno je 30–60 sekundi; 3 sekunde ostaje podržano.
  5. Command Processing Obrada zapovijedi:
    • If `commands` is not empty (from MQTT, ping, or stream), loop through all received command items.
    • Ako niz `commands` nije prazan (iz MQTT, pinga ili streama), prođite kroz sve zapovijedi u petlji.
    • For each command, map the `subDeviceId` to the physical relay port and trigger the relay to generate value pulses (or run for `value` seconds).
    • Za svaku zapovijed, povežite `subDeviceId` sa odgovarajućim fizičkim portom releja i aktivirajte ga tako da generiše value impulsa (ili radi `value` sekundi).
    • Do NOT trigger multiple overlapping activations on the same relay; queue them internally or ignore if busy. Deduplicate by command id.
    • NEMOJTE pokretati više preklapajućih aktivacija na istom releju istovremeno; skladištite ih u internom redu ili ignorišite ako je relej zauzet. Deduplikujte po id zapovijedi.
  6. Completion Završetak: There is no callback needed to confirm execution. The server marks commands as SENT when delivering them via MQTT, ping, or stream. Nema potrebe za povratnom potvrdom. Server označava zapovijedi kao SENT kad ih isporuči preko MQTT, pinga ili streama.

Device Command Safety

Bezbjednost rada uređaja

Ensure that in case of watchdog reset or network interruption, the controller fails safely (all relays opened/off). The device should handle deduplication by ignoring commands with `id`s it has recently executed.

Osigurajte da u slučaju resetovanja kontrolera (watchdog) ili nestanka mrežne veze svi releji odu u bezbjedno isključeno stanje (open/off). Kontroler bi trebao da ignoriše zapovijedi sa ID-jevima koje je već izvršio u skorije vrijeme.

API key errors

Greške API ključa

Missing or wrong X-Device-Api-Key returns 401 Unauthorized. An unregistered deviceId returns 404. Unknown devices are never auto-created from a ping.

Nedostajući ili pogrešan X-Device-Api-Key vraća 401 Unauthorized. Neregistrovan deviceId vraća 404. Nepoznati uređaji se nikad ne kreiraju automatski iz pinga.

Mobile Service Activation

Aktivacija usluge sa mobilne aplikacije

Triggered by the mobile client app to deduct wallet balance and queue a pending command. Supports corporate B2B rules engines (time schedules, allowed days, and daily user limits).

Pokreće se iz mobilne aplikacije radi skidanja tokena sa novčanika i dodavanja zapovijedi na čekanje. Podržava B2B kontrolu limita (dozvoljeni dani, vrijeme rada, dnevni limiti po zaposlenom).

HTTPS Callable (v2 onCall) https://us-central1-yosh-digitalchip.cloudfunctions.net/activateService

Authentication Header Required

Potrebna autorizacija

Requires a valid Firebase ID Token in the header: Authorization: Bearer <TOKEN>. Also, the calling user UID must match the userId parameter in the payload.

Zahtijeva ispravan Firebase ID Token u zaglavlju: Authorization: Bearer <TOKEN>. Takođe, UID ulogovanog korisnika mora odgovarati parametru userId.

Request Structure (Wrapped in a "data" property)

Struktura zahtjeva (Umotana u "data" objekat)

Field Type Requirement Obaveznost Description Opis
data.userId String RequiredObavezno The UID of the user purchasing the service. UID korisnika koji aktivira uslugu.
data.tenantId String RequiredObavezno Associated branch location/tenant ID. ID lokacije/poslovnice.
data.deviceId String RequiredObavezno IoT Gateway controller ID. ID mrežnog prolaza (kontrolera).
data.subDeviceId String RequiredObavezno Target relay module (e.g. bay_1). ID specifičnog modula/releja koji se uključuje.
data.tokensToSpend Number RequiredObavezno Quantity of tokens to spend from the wallet. Broj tokena koje korisnik troši.
data.qrSessionId String RequiredObavezno From createQrSession (300s / 5 min presence). Iz createQrSession (300s / 5 min prisustvo).
data.vehicleId String ConditionalUslovno Optional. If set, must be an active vehicle in the caller’s scope (company fleet or personal). Obavezno ako postoji ≥1 aktivno vozilo u opsegu (flota ili lično).
Request Body Example Primjer tela zahtjeva
{
  "data": {
    "userId": "UserUID_12345",
    "deviceId": "dev_wash_01",
    "subDeviceId": "bay_1",
    "tokensToSpend": 5,
    "qrSessionId": "sess_abc123",
    "vehicleId": "veh_xyz"
  }
}

Response Structure (Returned under a "result" property)

Struktura odgovora (Vraća se unutar "result" objekta)

Field Type Description Opis
result.success Boolean Indicates if the balance was successfully debited and the command queued. Označava da li je transakcija uspješno prošla i zapovijed upisana u bazu.
result.message String Transaction output summary message. Tekstualna poruka o statusu aktivacije.
result.commandId String The created command ID document. ID kreirane zapovijedi u bazi podataka.
result.value Number Calculated pulse count based on tokens spent and sub-device calibration factor. Izračunati broj impulsa na osnovu potrošenih tokena i kalibracije uređaja.
result.remainingBalance Number New wallet token balance. Preostalo stanje tokena u novčaniku nakon skidanja.
Response Body Example Primjer odgovora
{
  "result": {
    "success": true,
    "message": "Service activated successfully.",
    "commandId": "cmd_act_912384x",
    "value": 50,
    "remainingBalance": 45
  }
}

Calling HTTPS Callable functions

Pozivanje HTTPS Callable funkcija

All onCall functions require a valid Firebase Auth session. Native apps use the Firebase SDK (Android/iOS/Flutter). Raw HTTP clients must send:

Sve onCall funkcije zahtijevaju Firebase Auth sesiju. Native app koristi Firebase SDK. Raw HTTP klijenti šalju:

JS SDK
import { getFunctions, httpsCallable } from "firebase/functions";
const fn = httpsCallable(getFunctions(), "getUserProfile");
const { data } = await fn({});

getUserProfile

getUserProfile

HTTPS Callable https://us-central1-yosh-digitalchip.cloudfunctions.net/getUserProfile

Returns the authenticated user's profile. No request fields required.

Vraća profil ulogovanog korisnika. Nema obaveznih polja u zahtjevu.

Response (result)
{
  "success": true,
  "profile": {
    "userId": "abc123",
    "name": "Marko Marković",
    "email": "marko@alphacorp.com",
    "phone": "+38761111222",
    "role": "USER",
    "b2bCompanyId": "comp_alpha",
    "tenantId": null,
    "isGuest": false,
    "emailVerified": false,
    "providers": ["password"]
  }
}

updateUserProfile

updateUserProfile

HTTPS Callable https://us-central1-yosh-digitalchip.cloudfunctions.net/updateUserProfile

Updates own name and phone. Cannot change role, email, or org links.

Ažurira vlastito name i phone. Ne mijenja ulogu, email ni org veze.

FieldTypeRequiredObaveznoNotesNapomena
data.name String Yes 1–100 chars
data.phone String Optional Max 40 chars (empty string allowed) Max 40 znakova (prazan string OK)
Request
{
  "data": {
    "name": "Petar Petrović",
    "phone": "+38761123456"
  }
}

changeUserPassword

changeUserPassword

HTTPS Callable https://us-central1-yosh-digitalchip.cloudfunctions.net/changeUserPassword

Verifies currentPassword via Identity Toolkit, then sets newPassword (min 6). Guests / anonymous / non-password providers are rejected.

Provjerava currentPassword preko Identity Toolkit-a, zatim postavlja newPassword (min 6). Gosti / anonimni / bez password providera se odbijaju.

FieldTypeRequiredObavezno
data.currentPassword String Yes
data.newPassword String Yes (≥ 6)
Request
{
  "data": {
    "currentPassword": "oldSecret",
    "newPassword": "newSecret123"
  }
}

onboardEmployee

onboardEmployee

HTTPS Callable https://us-central1-yosh-digitalchip.cloudfunctions.net/onboardEmployee

B2B_ADMIN (own company) or SUPER_ADMIN. Creates Auth + Firestore user + wallet if needed, links to company as USER.

B2B_ADMIN (svoja kompanija) ili SUPER_ADMIN. Kreira Auth + Firestore + novčanik ako treba, veže na kompaniju kao USER.

FieldTypeRequiredObavezno
data.companyIdStringYes
data.emailStringYes
data.nameStringYes
data.phoneStringYes (≥ 6)
data.passwordStringYes (≥ 6)
Request
{
  "data": {
    "companyId": "comp_alpha",
    "email": "ana@alphacorp.com",
    "name": "Ana Anić",
    "phone": "+38761123456",
    "password": "Password123!"
  }
}
Response (result)
{
  "success": true,
  "userId": "uid_...",
  "created": true,
  "alreadyLinked": false,
  "email": "ana@alphacorp.com"
}

dispenseTokens

dispenseTokens

HTTPS Callable https://us-central1-yosh-digitalchip.cloudfunctions.net/dispenseTokens

Moves tokens from corporate wallet to employee wallet (atomic). B2B_ADMIN of that company or SUPER_ADMIN.

Prenosi tokene s korporativnog na zaposlenički novčanik (atomski). B2B_ADMIN te kompanije ili SUPER_ADMIN.

FieldTypeRequiredObavezno
data.fromCompanyIdStringYes
data.toUserIdStringYes
data.amountNumberYes (> 0)
Request
{
  "data": {
    "fromCompanyId": "comp_alpha",
    "toUserId": "employeeUid",
    "amount": 50
  }
}
Response (result)
{
  "success": true,
  "newBalance": 450,
  "employeeBalance": 50
}

getCompanySpendingReport

getCompanySpendingReport

HTTPS Callable https://us-central1-yosh-digitalchip.cloudfunctions.net/getCompanySpendingReport

Lists corporate-wallet DEBIT transactions (who / when / where) plus per-user totals.

Lista DEBIT transakcija korporativnog novčanika (ko / kada / gdje) i totali po korisniku.

FieldTypeRequiredObavezno
data.companyIdStringYes
data.limitNumberOptional (default 200, max 500)
Request
{
  "data": { "companyId": "comp_alpha", "limit": 100 }
}
Response (result) — shape
{
  "success": true,
  "companyId": "comp_alpha",
  "companyName": "Alpha Corp",
  "totals": { "gross": 120, "transactionCount": 8 },
  "users": [
    { "userId": "...", "name": "...", "email": "...", "gross": 40, "transactionCount": 3 }
  ],
  "transactions": [
    {
      "id": "...",
      "userId": "...",
      "userName": "...",
      "amount": 5,
      "tenantId": "tenant_wash_central",
      "tenantName": "...",
      "deviceId": "dev_wash_01",
      "subDeviceId": "bay_1",
      "subDeviceName": "Bay 1",
      "timestamp": "2026-07-15T12:00:00.000Z"
    }
  ]
}

createTenantWithAdmin

createTenantWithAdmin

HTTPS Callable https://us-central1-yosh-digitalchip.cloudfunctions.net/createTenantWithAdmin

SUPER_ADMIN only. Creates tenant, TENANT_ADMIN Auth user, wallets (tenant balance starts at 0 — tenants sell services).

Samo SUPER_ADMIN. Kreira tenant, TENANT_ADMIN Auth nalog, novčanike (tenant balance počinje na 0 — tenant prodaje usluge).

FieldTypeRequiredObavezno
data.tenantIdStringYes
data.tenantNameStringYes
data.adminNameStringYes
data.adminEmailStringYes
data.adminPasswordStringYes (≥ 6)
data.industryTypeStringOptionalCAR_WASH | LAUNDRY | EV_CHARGING
data.currencyStringOptionalTOKEN | BAM | EUR | RSD | USD
data.addressStringOptional
data.jibStringOptional
data.platformFeePercentNumberOptional — 0–100, default 15
Request
{
  "data": {
    "tenantId": "tenant_eco_wash",
    "tenantName": "Eco Wash Center",
    "industryType": "CAR_WASH",
    "adminName": "Petar Petrović",
    "adminEmail": "admin@ecowash.com",
    "adminPassword": "Password123!",
    "currency": "BAM",
    "address": "Ulica 1, Sarajevo",
    "jib": "4200123450001",
    "platformFeePercent": 12.5
  }
}

updateTenantFeeTerms

updateTenantFeeTerms

HTTPS Callable https://us-central1-yosh-digitalchip.cloudfunctions.net/updateTenantFeeTerms

SUPER_ADMIN only. Sets tenants/{tenantId}.platformFeePercent (0–100). Used by getEarningsReport. Clients cannot write this field (Firestore rules).

Samo SUPER_ADMIN. Postavlja tenants/{tenantId}.platformFeePercent (0–100). Koristi getEarningsReport. Klijent ne može pisati ovo polje (Firestore rules).

Request / Response
// Request
{ "data": { "tenantId": "tenant_eco_wash", "platformFeePercent": 10 } }

// Response
{ "success": true, "tenantId": "tenant_eco_wash", "platformFeePercent": 10 }

rotateDeviceApiKey

rotateDeviceApiKey

HTTPS Callable https://us-central1-yosh-digitalchip.cloudfunctions.net/rotateDeviceApiKey

SUPER_ADMIN or owning TENANT_ADMIN. Issues a new apiKey for devices/{deviceId}. Old key stops working on gatewayPing.

SUPER_ADMIN ili vlasnički TENANT_ADMIN. Izdaje novi apiKey za devices/{deviceId}. Stari ključ prestaje raditi na gatewayPing.

Request / Response
// Request
{ "data": { "deviceId": "dev_wash_01" } }

// Response
{ "success": true, "deviceId": "dev_wash_01", "apiKey": "dc_a1b2c3..." }

getEarningsReport

getEarningsReport

HTTPS Callable https://us-central1-yosh-digitalchip.cloudfunctions.net/getEarningsReport

Aggregates DEBIT spend by tenant → device → sub-device. Fee = each tenant’s platformFeePercent (default 15% if missing). Totals sum per-tenant fee math. SUPER_ADMIN (optional tenantId filter) or TENANT_ADMIN (own tenant only).

Agregira DEBIT potrošnju tenant → uređaj → poduređaj. Fee = platformFeePercent po tenantu (podrazumevano 15% ako nedostaje). Totals su zbir po tenantima. SUPER_ADMIN (opcionalni filter tenantId) ili TENANT_ADMIN (samo svoj tenant).

Request
{
  "data": { "tenantId": "tenant_wash_central" }
}
Response (result) — shape
{
  "success": true,
  "platformFeePercent": 12.5,
  "defaultPlatformFeePercent": 15,
  "transactionCount": 42,
  "totals": { "gross": 1000, "platformFee": 125, "net": 875, "transactionCount": 42 },
  "tenants": [
    {
      "tenantId": "tenant_wash_central",
      "tenantName": "Wash Central",
      "platformFeePercent": 12.5,
      "gross": 1000,
      "platformFee": 125,
      "net": 875,
      "transactionCount": 42,
      "devices": [
        {
          "deviceId": "dev_wash_01",
          "gross": 1000,
          "platformFee": 125,
          "net": 875,
          "platformFeePercent": 12.5,
          "subDevices": [
            { "subDeviceId": "bay_1", "name": "Bay 1", "gross": 600, "platformFee": 75, "net": 525, "platformFeePercent": 12.5 }
          ]
        }
      ]
    }
  ]
}

Vehicles API (mobile-ready)

API vozila (za mobilnu app)

Collection /vehicles/{vehicleId}. Client writes denied — all mutations via callables. Wash ledger stores vehicleId + vehiclePlate on DEBIT txs.

Kolekcija /vehicles/{vehicleId}. Client write zabranjen — mutacije preko callable. Ledger čuva vehicleId + vehiclePlate na DEBIT tx.

listVehicles

{ "companyId": "comp_alpha" }  // omit → personal vehicles
// includeInactive?: boolean
// Auth: personal owner; company members; SUPER_ADMIN

createVehicle

{ "plate": "A12-B-345", "label": "Van 1", "companyId": "comp_alpha" }
// omit companyId → personal vehicle for caller
// companyId → B2B_ADMIN of company or SUPER_ADMIN

updateVehicle / deactivateVehicle

{ "vehicleId": "...", "plate": "...", "label": "...", "active": true }
{ "vehicleId": "..." }  // deactivateVehicle → active:false

getVehicleWashStats

{ "companyId": "comp_alpha" }
// B2B_ADMIN / SUPER → per-vehicle washCount, tokensSpent, lastWashAt

listWashHistory

{ "vehicleId": "optional", "limit": 50 }
// Caller DEBITs (personal wallet or company wallet where userId=caller)
// Filter by vehicleId when set

Admin / B2B / Device Mutations

Admin / B2B / Device mutacije

All platform writes used by the PWA (and native apps) go through these callables — not direct Firestore client writes.

Sve platformerske mutacije koje koristi PWA (i native appovi) idu preko ovih callable funkcija — ne direktnim Firestore write-ovima sa klijenta.

Auth

Auth

Same as other onCall: Firebase ID token. Role checks happen server-side.

Isto kao i ostali onCall: Firebase ID token. Provjera uloge je na serveru.

createB2bCompany

{
  "companyId": "comp_alpha",
  "name": "Alpha Corp",
  "adminEmail": "admin@alphacorp.com",
  "initialBalance": 500,
  "dailyLimit": 50,
  "currency": "BAM",
  "address": "Ulica 1",
  "jib": "4200123450001"
}

updateSpendingRules

{
  "companyId": "comp_alpha",
  "rules": {
    "startTime": "08:00",
    "endTime": "20:00",
    "dailyLimit": 50,
    "allowedDays": [1, 2, 3, 4, 5]
  }
}

registerDevice

{
  "deviceId": "dev_wash_02",
  "tenantId": "tenant_wash_central",
  "firmwareVersion": "1.0.0",
  "status": "OFFLINE"
}
/* Response includes server-generated apiKey */

registerSubDevice

{
  "deviceId": "dev_wash_02",
  "subDeviceId": "bay_3",
  "name": "Bay 3",
  "qrCodePayload": "bay_3",
  "pulsePerToken": 10
}

adminUpdateUser / creditUserWallet / creditOwnWallet

/* adminUpdateUser — SUPER_ADMIN */
{ "userId": "...", "role": "B2B_ADMIN", "b2bCompanyId": "comp_alpha", "tenantId": null }

/* creditUserWallet — SUPER_ADMIN */
{ "userId": "...", "amount": 100 }

/* creditOwnWallet — any signed-in user */
{ "amount": 10, "replace": false }

Other mutation IDs

Ostali mutacijski ID-jevi

deleteB2bCompany · assignCompanyAdmin · removeCompanyEmployee · deleteDevice · deleteSubDevice · linkDeviceToTenant · deleteUserAccount · deleteTenantCascade · updateTenantFeeTerms

Error Handling & Status Codes

Rukovanje greškama i statusni kodovi

HTTP gatewayPing / gatewayStream

HTTP gatewayPing / gatewayStream

HTTPS Callable (onCall)

HTTPS Callable (onCall)

Errors throw Firebase HttpsError. Client SDKs expose code and message.

Greške bacaju Firebase HttpsError. Client SDK vraća code i message.

code Typical meaning Značenje
unauthenticatedMissing / expired ID tokenNedostaje / istekao ID token
permission-deniedWrong role or wrong company/tenant scopePogrešna uloga ili scope kompanije/tenanta
invalid-argumentMissing / invalid fieldsNedostaju / neispravna polja
not-foundUser, company, device, or wallet missingNedostaje user, kompanija, uređaj ili novčanik
failed-preconditionBusiness rule (hours, guest password, etc.)Poslovno pravilo (radno vrijeme, gost lozinka…)
already-existsTenant ID or email already takenTenant ID ili email već postoji
resource-exhaustedDaily B2B limit / too many attemptsDnevni B2B limit / previše pokušaja
internalUnexpected server failureNeočekivana greška servera