TingPush Camera AI

Tài liệu tích hợp

Hướng dẫn tích hợp dữ liệu TingPush cho lập trình viên

English version

Trang bắt đầu dành cho đơn vị làm phần mềm (bãi xe, tòa nhà, nhà máy, nhân sự – chấm công, cửa hàng…) muốn đưa dữ liệu từ camera TingPush vào hệ thống của mình: biển số, sự kiện AI (xâm nhập, khuôn mặt, đếm người / xe, quá đông, model tùy chỉnh), chấm công, và điều khiển (mở barie, danh sách xe, danh sách nhân viên). Đọc trang này trước, rồi đi vào tài liệu chi tiết của từng phần.


1. Chọn cách tích hợp

Camera TingPush gửi dữ liệu theo một trong hai đường (chủ camera chọn trên app TingPush Camera → Cài đặt camera → Gửi dữ liệu biển số → Nơi nhận dữ liệu):

 (A) Camera ──đẩy ngay (HTTP POST)──────────────────────────────►  Máy chủ của bạn  (API riêng)

 (B) Camera ──► Máy chủ TingPush (lưu theo gói) ◄──kéo về (API v1, khóa API)──  Phần mềm của bạn
                       │
                       └──► app / web / phần mềm Windows xem, xuất Excel, cảnh báo điện thoại
(A) Camera đẩy về API riêng (B) Kéo từ TingPush qua API v1
Dữ liệu Biển số + chấm công Biển số, sự kiện AI, số đếm, chấm công, bảng công, nhân viên, thiết bị, danh sách xe
Độ trễ Tức thì (dưới 1 giây sau khi đọc) Theo chu kỳ bạn gọi (thường 5–30 giây)
Bạn cần Một endpoint HTTP camera gọi tới được (Internet hoặc mạng nội bộ bãi xe) Gọi ra Internet; không cần mở cổng
Lưu trữ Bạn tự lưu TingPush lưu theo gói (xem, xuất file, cảnh báo trên app vẫn dùng được)
Mất mạng Camera giữ hàng đợi (tối đa 300 bản), gửi bù Camera gửi bù về TingPush; bạn kéo tiếp từ since_id cũ — không mất
Tài liệu Hướng dẫn viết API nhận kết quả đọc biển số từ camera TingPush, Chấm công bằng khuôn mặt — camera TingPush làm máy chấm công mục 5 Hướng dẫn API công khai TingPush — lấy dữ liệu biển số đã lưu và trang này

Gợi ý chọn:

  • Phần mềm bãi xe chạy tại chỗ, cần mở barie ngay theo biển số → (A). Hoặc để camera tự mở barie theo danh sách xe (không cần phần mềm của bạn quyết định — Thiết bị ngoại vi — mở barie, cửa cuốn, còi, đèn từ camera TingPush), còn dữ liệu đi (B).
  • Báo cáo, đối soát, kế toán, nhân sự – tính lương, dashboard → (B): không phải dựng máy chủ nhận, không lo mất dữ liệu, khách hàng vẫn xem được trên app.
  • Sự kiện AI và số đếm chỉ đi đường (B) — camera gửi về TingPush để báo điện thoại.
  • Một camera chỉ chọn một Nơi nhận cho biển số / chấm công. Cần cả hai (vừa nhận tức thì vừa lưu trên TingPush): chọn (B) và gọi API v1 mỗi 5 giây, hoặc liên hệ TingPush.

Ngoài ra không cần lập trình: xuất Excel (CSV) trên app / web, hoặc Excel / Power BI Lấy dữ liệu từ web (mục 9).


2. Bắt đầu nhanh (10 phút)

  1. Nhận camera vào tài khoản (app → Thêm camera), để Nơi nhận dữ liệu = Máy chủ TingPush.
  2. Tài khoản cần gói có quyền API (app → Tài khoản → Gói dịch vụ).
  3. Tạo khóa API: app → Tài khoản → Khóa API → Tạo khóa, hoặc web https://webrtc.tingpush.com/account. Khóa dạng tp_ + 40 ký tự, chỉ hiện một lần. Tích Điều khiển thiết bị & sửa danh sách xe nếu phần mềm cần mở barie, sửa danh sách xe hoặc nhân viên.
  4. Gọi thử:
curl -H "X-Api-Key: tp_xxx" https://apiwebrtc.tingpush.com/api/v1/cameras
curl -H "X-Api-Key: tp_xxx" "https://apiwebrtc.tingpush.com/api/v1/plates?limit=5"
  1. Tạo dữ liệu thử: cho xe đi qua camera, hoặc trên app bấm Gửi thử (biển mẫu 30A-123.45) / Thử cảnh báo (sự kiện AI mẫu, test: true).
  2. Viết vòng đồng bộ theo mục 6.

3. Xác thực, giới hạn, lỗi

Địa chỉ gốc:  https://apiwebrtc.tingpush.com/api/v1
Xác thực:     X-Api-Key: tp_…          (hoặc Authorization: Bearer tp_…)
Giới hạn:     120 yêu cầu / phút / khóa → vượt: 429, chờ tới phút sau
Định dạng:    JSON UTF-8, tên trường snake_case
  • Khóa thấy mọi camera tài khoản xem được (camera mình là chủ + camera được chia sẻ). Khóa thường chỉ đọc; khóa có quyền điều khiển mới gọi được các lệnh thay đổi (mục 5, cột Quyền).
  • Mỗi tài khoản tối đa 10 khóa. Mỗi phần mềm / mỗi nơi lắp đặt nên dùng một khóa riêng — xóa khóa là vô hiệu ngay, không ảnh hưởng phần mềm khác. Tên khóa hiện trong nhật ký đóng / mở thiết bị (API: <tên khóa>).
  • Gọi từ máy chủ của bạn, không nhúng khóa vào trang web / app phát cho người dùng (lộ khóa = lộ toàn bộ dữ liệu). API không mở CORS cho website khác.
  • Ảnh (*_image_url, photo_url) cũng cần header khóa API — tải về máy chủ của bạn rồi phục vụ lại, đừng đưa thẳng URL cho trình duyệt.
Mã Ý nghĩa Nên làm
200 / 204 Thành công (204: xóa xong, không có nội dung)
400 Tham số / dữ liệu sai — {"error":"bad_request","message":"…"} Sửa dữ liệu, không gửi lại y nguyên
401 Thiếu / sai khóa, khóa đã xóa, hoặc gói không có quyền API — {"error":"unauthorized",…} Dừng, báo người quản trị
403 Khóa chỉ đọc gọi lệnh thay đổi, không có quyền với camera, hoặc gói không cho xuất file Dùng khóa có quyền điều khiển / nâng gói
404 Không có bản ghi / ảnh (hoặc không có quyền xem camera đó)
429 Vượt 120 yêu cầu / phút Chờ 60 giây rồi gọi tiếp
503 Camera mất kết nối / thiết bị báo lỗi (lệnh tới camera: mở barie…) Thử lại sau, message ghi lý do
5xx khác Lỗi tạm thời phía máy chủ Thử lại với thời gian chờ tăng dần

Thông báo lỗi (message / title) là tiếng Việt, hiển thị được cho người dùng.


4. Quy ước dữ liệu

Thời gian time (ISO 8601 giờ Việt Nam, VD 2026-10-16T10:02:54+07:00) + timestamp (giây UNIX, nên dùng để lưu / so sánh). received_at = lúc máy chủ nhận — camera mất mạng gửi bù thì received_at muộn hơn time.
Tham số thời gian from, to dạng ISO 8601, khoảng [from, to). Mã hóa dấu + trong URL: from=2026-10-16T00:00:00%2B07:00 (để nguyên + sẽ thành dấu cách → 400). Bảng công dùng ngày yyyy-MM-dd.
Mã camera LPR- + 12 ký tự (VD LPR-9MHK902C8J5Z), không đổi suốt đời camera — dùng làm khóa camera. Tên camera (camera_name) đổi được.
id bản ghi Số nguyên tăng dần theo thứ tự máy chủ nhận, không đổi — khóa đồng bộ. Mỗi loại (biển số, sự kiện, chấm công) có dãy id riêng.
Phân trang Không có since_id: mới nhất trước, trang sau dùng before_id = id nhỏ nhất vừa nhận. Có since_id: cũ nhất trước (đồng bộ), trả next_since_id + has_more. limit 1–1000 (mặc định 100).
Biển số plate đã chuẩn hóa (chữ in hoa + số, không dấu, VD 51G12345) để lưu / so khớp; plate_display để hiển thị (51G-123.45). Tham số lọc chấp nhận mọi cách viết.
Thời gian lưu Bản ghi + ảnh quá hạn của gói bị xóa tự động (chấm công có hạn riêng, mặc định 400 ngày) — đồng bộ về hệ thống của bạn nếu cần giữ lâu.
Trường mới Bản sau có thể thêm trường — bỏ qua trường không biết, đừng kiểm tra cứng danh sách trường.
Dữ liệu thử test: true (sự kiện AI) — vẫn trả qua API, nên bỏ qua khi tính báo cáo. Biển số Gửi thử không lưu trên TingPush.

5. Danh mục API v1

Tất cả dưới https://apiwebrtc.tingpush.com/api/v1. Quyền: Đ = khóa chỉ đọc là đủ, ĐK = cần khóa có quyền điều khiển.

Camera & biển số — Hướng dẫn API công khai TingPush — lấy dữ liệu biển số đã lưu

Lệnh Quyền
GET /cameras Đ Camera của tài khoản: id, name, role (owner / viewer), online, last_seen
GET /plates Đ Biển số: lọc camera, plate, from, to; đồng bộ since_id; before_id, limit
GET /plates/{id} Đ Một bản ghi
GET /plates/{id}/image/plate · /image/scene Đ Ảnh cắt biển / ảnh toàn cảnh (JPEG)
GET /export?format=csv (hoặc json) Đ Xuất file (tối đa 200 000 dòng), cùng bộ lọc với /plates; gói phải cho xuất file

Sự kiện AI — Sự kiện AI & cảnh báo — đếm người / xe, xâm nhập vùng cấm, nhận diện khuôn mặt

Lệnh Quyền
GET /events Đ Sự kiện: lọc camera, type (intrusion, face, occupancy, count, custom, io; nhiều loại cách dấu phẩy), from, to; since_id, before_id, limit
GET /events/{id}/image/scene · /image/crop Đ Ảnh toàn cảnh / ảnh cắt đối tượng
GET /counts Đ Số đếm qua vạch: camera, from, to (mặc định 24 giờ, tối đa 400 ngày), bucket = hour / day / month, by_line=true
GET /occupancy Đ Số đang ở bên trong (vào − ra) từng camera

Chấm công & nhân viên — Chấm công bằng khuôn mặt — camera TingPush làm máy chấm công

Lệnh Quyền
GET /attendance Đ Lần chấm công: lọc camera, person, from, to; since_id, before_id, limit
GET /attendance/{id}/image/face · /image/scene Đ Ảnh mặt / ảnh toàn cảnh của lần chấm
GET /attendance/timesheet?from=&to=&department= Đ Bảng công đã tính (tối đa 62 ngày, ngày yyyy-MM-dd)
GET /employees Đ Nhân viên: id, code, name, department, note, photo_url, cameras[]
GET /employees/{id}/photo Đ Ảnh chân dung đã đăng ký
POST /employees ĐK Thêm nhân viên kèm ảnh (JPEG base64) và camera
PUT /employees/{id} ĐK Sửa (chỉ trường gửi lên); photo = đổi ảnh; cameras = đặt lại camera
DELETE /employees/{id} ĐK Xóa (lịch sử chấm công vẫn giữ)

Thiết bị ngoại vi & danh sách xe — Thiết bị ngoại vi — mở barie, cửa cuốn, còi, đèn từ camera TingPush mục 6

Lệnh Quyền
GET /cameras/{id}/outputs Đ Thiết bị gắn camera (barie, cửa cuốn, còi…) + trạng thái
POST /cameras/{id}/outputs/{index}/trigger ĐK Mở (pulse) / bật (on) / tắt (off)
GET /platelists Đ Danh sách xe cho vào
GET /platelists/{id}/entries?q=&offset=&limit= Đ Xe trong danh sách
POST /platelists/{id}/entries ĐK Thêm / cập nhật 1 xe (hạn dùng valid_from, valid_to)
DELETE /platelists/{id}/entries/{biển} ĐK Xóa 1 xe
POST /platelists/{id}/import ĐK Gộp / thay cả danh sách từ CSV

6. Đồng bộ dữ liệu về hệ thống của bạn

Cách chuẩn cho mọi loại có since_id (/plates, /events, /attendance):

với mỗi loại (plates, events, attendance):
    last_id = đọc từ CSDL của bạn (lần đầu = 0)
    lặp:
        GET /<loại>?since_id={last_id}&limit=500
        lưu từng bản ghi — UNIQUE theo id (gặp id đã có thì bỏ qua)
        (tùy chọn) tải ảnh về kho của bạn
        last_id = next_since_id     ← ghi CÙNG giao dịch với các bản ghi
        has_more = true → gọi lại ngay; false → chờ 5–30 giây
  • Không sót, không trùng: id tăng theo thứ tự máy chủ nhận, kể cả bản ghi camera gửi bù sau khi mất mạng.
  • Lưu last_id cùng giao dịch với bản ghi: phần mềm tắt giữa chừng thì lần sau đọc lại từ chỗ cũ, UNIQUE theo id chặn trùng.
  • Ba loại × 1 lần / 10 giây = 18 yêu cầu / phút, cộng tải ảnh — còn dư nhiều trong hạn mức 120 / phút. Tải ảnh nhiều thì giãn ra hoặc chỉ tải ảnh khi người dùng mở xem.
  • Gặp 429 → chờ 60 giây; 5xx / mất mạng → chờ 5 → 10 → 20 → 60 giây rồi thử lại; 401 → dừng và báo người quản trị.
  • Sau khi đồng bộ, nên lưu time / timestamp (giờ sự việc) để báo cáo, received_at để soát độ trễ.

Python — đồng bộ đầy đủ (biển số, sự kiện, chấm công + ảnh)

Chạy được ngay (Python 3.9+, pip install requests), lưu vào SQLite — thay phần lưu bằng CSDL của bạn.

"""Đồng bộ dữ liệu TingPush về CSDL: biển số, sự kiện AI, chấm công (kèm ảnh)."""
import json, os, sqlite3, time
import requests

API = "https://apiwebrtc.tingpush.com/api/v1"
KEY = os.environ["TINGPUSH_KEY"]          # tp_…
IMG_DIR = "anh"                             # thư mục lưu ảnh; None = không tải ảnh
s = requests.Session()
s.headers["X-Api-Key"] = KEY

db = sqlite3.connect("tingpush.db")
db.executescript("""
CREATE TABLE IF NOT EXISTS sync_state (kind TEXT PRIMARY KEY, last_id INTEGER NOT NULL);
CREATE TABLE IF NOT EXISTS plates (id INTEGER PRIMARY KEY, camera_id TEXT, plate TEXT, plate_display TEXT,
    ts INTEGER, allowed INTEGER, raw TEXT);
CREATE TABLE IF NOT EXISTS events (id INTEGER PRIMARY KEY, camera_id TEXT, type TEXT, summary TEXT, ts INTEGER,
    test INTEGER, raw TEXT);
CREATE TABLE IF NOT EXISTS attendance (id INTEGER PRIMARY KEY, camera_id TEXT, person_id TEXT, employee_code TEXT,
    name TEXT, ts INTEGER, source TEXT, raw TEXT);
""")

def get(path, **params):
    """GET có thử lại: 429 chờ 60 s, lỗi mạng / 5xx chờ tăng dần."""
    delay = 5
    while True:
        try:
            r = s.get(f"{API}{path}", params=params, timeout=30)
            if r.status_code == 429:
                time.sleep(60); continue
            if r.status_code >= 500:
                raise requests.HTTPError(f"HTTP {r.status_code}")
            r.raise_for_status()           # 400 / 401 / 403 / 404: lỗi cần người xem, không thử lại
            return r
        except (requests.ConnectionError, requests.Timeout, requests.HTTPError) as e:
            if isinstance(e, requests.HTTPError) and e.response is not None and e.response.status_code < 500:
                raise
            print("Lỗi tạm thời:", e, "- thử lại sau", delay, "giây")
            time.sleep(delay); delay = min(delay * 2, 60)

def save_image(url, name):
    if not url or not IMG_DIR:
        return None
    path = os.path.join(IMG_DIR, name)
    if not os.path.exists(path):
        os.makedirs(IMG_DIR, exist_ok=True)
        with open(path, "wb") as f:
            f.write(get(url.replace(API, "")).content)
    return path

def store(kind, x):
    raw = json.dumps(x, ensure_ascii=False)
    if kind == "plates":
        db.execute("INSERT OR IGNORE INTO plates VALUES (?,?,?,?,?,?,?)",
                   (x["id"], x["camera_id"], x["plate"], x["plate_display"], x["timestamp"],
                    None if x.get("allowed") is None else int(x["allowed"]), raw))
        save_image(x.get("plate_image_url"), f"bien_{x['id']}.jpg")
    elif kind == "events":
        db.execute("INSERT OR IGNORE INTO events VALUES (?,?,?,?,?,?,?)",
                   (x["id"], x["camera_id"], x["type"], x["summary"], x["timestamp"], int(x.get("test", False)), raw))
        save_image(x.get("crop_image_url"), f"su_kien_{x['id']}.jpg")
    elif kind == "attendance":
        db.execute("INSERT OR IGNORE INTO attendance VALUES (?,?,?,?,?,?,?,?)",
                   (x["id"], x.get("camera_id"), x.get("person_id"), x.get("employee_code"), x["name"],
                    x["timestamp"], x["source"], raw))
        save_image(x.get("face_image_url"), f"cham_cong_{x['id']}.jpg")

def sync(kind):
    row = db.execute("SELECT last_id FROM sync_state WHERE kind=?", (kind,)).fetchone()
    last_id = row[0] if row else 0
    while True:
        page = get(f"/{kind}", since_id=last_id, limit=500).json()
        with db:                                   # 1 giao dịch: bản ghi + vị trí đồng bộ
            for x in page["data"]:
                store(kind, x)
            last_id = page["next_since_id"]
            db.execute("INSERT OR REPLACE INTO sync_state VALUES (?,?)", (kind, last_id))
        if page["data"]:
            print(kind, "+", len(page["data"]), "→ last_id", last_id)
        if not page["has_more"]:
            return

while True:
    for kind in ("plates", "events", "attendance"):
        sync(kind)
    time.sleep(10)

Không dùng loại nào thì bỏ khỏi danh sách ("plates", "events", "attendance"). Gói không có chấm công: /attendance trả danh sách rỗng.

C# (.NET 8+) — dịch vụ nền đồng bộ

using System.Net;
using System.Net.Http.Json;
using System.Text.Json;

var http = new HttpClient { BaseAddress = new Uri("https://apiwebrtc.tingpush.com/api/v1/"), Timeout = TimeSpan.FromSeconds(30) };
http.DefaultRequestHeaders.Add("X-Api-Key", Environment.GetEnvironmentVariable("TINGPUSH_KEY"));

var lastIds = new Dictionary<string, long> { ["plates"] = 0, ["events"] = 0, ["attendance"] = 0 }; // đọc từ CSDL của bạn

while (true)
{
    foreach (var kind in lastIds.Keys.ToList())
    {
        bool more;
        do
        {
            using var res = await http.GetAsync($"{kind}?since_id={lastIds[kind]}&limit=500");
            if (res.StatusCode == HttpStatusCode.TooManyRequests) { await Task.Delay(TimeSpan.FromMinutes(1)); more = true; continue; }
            res.EnsureSuccessStatusCode();
            var page = await res.Content.ReadFromJsonAsync<JsonElement>();
            foreach (var x in page.GetProperty("data").EnumerateArray())
            {
                // lưu vào CSDL của bạn — UNIQUE theo id (INSERT … WHERE NOT EXISTS / MERGE)
                Console.WriteLine($"{kind} #{x.GetProperty("id")} {x.GetProperty("time")}");
            }
            lastIds[kind] = page.GetProperty("next_since_id").GetInt64(); // lưu cùng giao dịch với bản ghi
            more = page.GetProperty("has_more").GetBoolean();
        } while (more);
    }
    await Task.Delay(TimeSpan.FromSeconds(10));
}

Tải ảnh: await http.GetByteArrayAsync(x.GetProperty("plate_image_url").GetString()) (cùng HttpClient đã có header khóa).

Node.js (18+)

const API = 'https://apiwebrtc.tingpush.com/api/v1';
const H = { 'X-Api-Key': process.env.TINGPUSH_KEY };
const last = { plates: 0, events: 0, attendance: 0 }; // đọc từ CSDL của bạn
const sleep = ms => new Promise(ok => setTimeout(ok, ms));

for (;;) {
  for (const kind of Object.keys(last)) {
    for (;;) {
      const r = await fetch(`${API}/${kind}?since_id=${last[kind]}&limit=500`, { headers: H });
      if (r.status === 429) { await sleep(60000); continue; }
      if (!r.ok) throw new Error(`${kind}: HTTP ${r.status} ${await r.text()}`);
      const page = await r.json();
      for (const x of page.data) console.log(kind, x.id, x.time); // lưu vào CSDL — UNIQUE theo id
      last[kind] = page.next_since_id;                           // lưu cùng giao dịch
      if (!page.has_more) break;
    }
  }
  await sleep(10000);
}

PHP, Excel / Power BI: Hướng dẫn API công khai TingPush — lấy dữ liệu biển số đã lưu mục 4.

Dữ liệu không có since_id

Dữ liệu Cách lấy
Số đếm theo giờ / ngày GET /counts?camera=…&from=…&to=…&bucket=hour — gọi riêng từng camera (không có camera = cộng mọi camera), mỗi lần lấy lại khoảng gần nhất (VD 2 giờ qua) và ghi đè theo (camera, line, start): số của giờ đang chạy còn tăng dần
Số đang ở bên trong GET /occupancy mỗi 10–60 giây (camera cập nhật theo chu kỳ đếm)
Bảng công GET /attendance/timesheet?from=…&to=… — tính lại toàn bộ khoảng mỗi lần (bổ sung tay, sửa ca làm việc làm thay đổi kết quả cũ)
Danh sách nhân viên, danh sách xe Đọc khi cần, hoặc mỗi đêm

7. Nhận dữ liệu đẩy thẳng từ camera (API riêng)

Khi camera đặt Nơi nhận dữ liệu = API riêng, camera POST JSON tới URL của bạn ngay khi đọc được biển hoặc chấm công thành công — cùng URL, phân biệt bằng type:

# FastAPI — 1 endpoint nhận cả biển số và chấm công. pip install fastapi uvicorn
from fastapi import FastAPI, Header, HTTPException, Request

app = FastAPI()
SEEN = set()   # thay bằng bảng có khóa UNIQUE(event_id) trong CSDL

@app.post("/api/tingpush")
async def receive(req: Request, authorization: str | None = Header(None)):
    if authorization != "Bearer bi-mat-cua-ban":          # header do chủ camera khai báo trên app
        raise HTTPException(401)                          # 4xx: camera bỏ bản ghi, không gửi lại
    x = await req.json()
    if x.get("test"):
        return {"ok": True}                               # nút "Gửi thử": trả 200, không lưu
    if x["event_id"] in SEEN:
        return {"ok": True, "duplicate": True}            # camera gửi lại do mất trả lời: vẫn 200
    if x["type"] == "plate":
        print("Biển", x["plate_display"], "camera", x["camera_id"], "cho vào:", x.get("allowed"))
    elif x["type"] == "attendance":
        print("Chấm công", x["employee_code"], x["name"], x["time"])
    # type khác (bản sau có thể thêm): bỏ qua, vẫn trả 200
    SEEN.add(x["event_id"])
    return {"ok": True}                                   # trả trong < 8 giây; việc chậm đưa vào hàng đợi

Quy tắc bắt buộc (chi tiết: Hướng dẫn viết API nhận kết quả đọc biển số từ camera TingPush mục 3, 6):

  • 2xx = đã nhận. 4xx = camera bỏ bản ghi. 5xx / quá giờ / mất kết nối = camera giữ lại và gửi lại. Lỗi tạm thời (CSDL bận…) phải trả 503, đừng trả 4xx.
  • Bỏ trùng theo event_id (khóa UNIQUE), gặp trùng vẫn trả 200.
  • Trả lời nhanh (mặc định camera chờ 8 giây); ghi nhận rồi xử lý nặng (gọi hệ thống khác, gửi thông báo) sau.
  • Không chuyển hướng (3xx); khai báo đúng URL cuối cùng.

Mã mẫu đầy đủ C#, Node.js, PHP, Python và công cụ tự kiểm tra API: tải bộ mã mẫu (công cụ kiểm tra: kiem_tra_api.py, JSON mẫu: sample_request.json).


8. Ghi dữ liệu và điều khiển

Cần khóa có quyền Điều khiển thiết bị & sửa danh sách xe.

Mở barie từ phần mềm bãi xe (VD xe vé lượt đã thanh toán):

curl -X POST -H "X-Api-Key: tp_xxx" -H "Content-Type: application/json" -d '{"action":"pulse"}' \
     https://apiwebrtc.tingpush.com/api/v1/cameras/LPR-9MHK902C8J5Z/outputs/0/trigger

Đồng bộ xe tháng mỗi đêm từ phần mềm quản lý vé — thay cả danh sách (camera tự tải bản mới, mất mạng vẫn mở cho xe trong danh sách):

curl -X POST -H "X-Api-Key: tp_xxx" -H "Content-Type: application/json" \
     -d '{"text":"51G12345,Nguyễn Văn A,01/10/2026,31/10/2026\n30A99999,Xe giám đốc","replace":true}' \
     https://apiwebrtc.tingpush.com/api/v1/platelists/3/import

Đồng bộ nhân viên từ phần mềm nhân sự (chấm công khuôn mặt):

import base64, requests
H = {"X-Api-Key": "tp_xxx"}
API = "https://apiwebrtc.tingpush.com/api/v1"
body = {"name": "Trần Thị B", "code": "NV002", "department": "Kho",
        "photo": base64.b64encode(open("NV002.jpg", "rb").read()).decode(),   # ảnh thẻ JPEG, nhìn thẳng
        "cameras": ["LPR-9MHK902C8J5Z"]}
r = requests.post(f"{API}/employees", json=body, headers=H, timeout=120)
emp = r.json()
bad = [c for c in emp.get("cameras", []) if c["state"] == "error"]   # VD "không thấy khuôn mặt" -> gửi ảnh khác
print(r.status_code, emp.get("id"), bad)
  • Mã nhân viên (code) không trùng trong tài khoản — dùng nó để khớp với hệ thống của bạn; lưu id TingPush trả về để sửa / xóa sau (PUT / DELETE /employees/{id}).
  • Nghỉ việc → DELETE /employees/{id}: camera thôi nhận diện ngay, lịch sử chấm công vẫn giữ.
  • Thêm nhân viên chờ camera nạp ảnh (vài giây mỗi camera): đặt thời gian chờ HTTP ≥ 60 giây.

9. Kịch bản mẫu

Bãi xe có phần mềm quản lý vé

  1. Xe tháng: đồng bộ vào danh sách xe cho vào (mục 8) → camera tự mở barie, mất mạng vẫn chạy.
  2. Xe vé lượt: phần mềm đọc biển (/plates mỗi 2–5 giây, hoặc camera đẩy API riêng), tính tiền, gọi trigger mở barie.
  3. Đối soát: lưu plate, time, ảnh biển; allowed cho biết camera đã nhận xe trong danh sách.
  4. Nhật ký mở barie (ai mở, vì sao): GET /events?type=io.

Nhân sự – tính lương (chấm công khuôn mặt)

  1. Nhân viên: tạo / sửa / xóa qua /employees theo hệ thống nhân sự.
  2. Lần chấm: /attendance?since_id= → tự tính công theo ca của công ty (nhiều ca, tăng ca…).
  3. Hoặc dùng luôn bảng công TingPush tính: /attendance/timesheet (một ca chung, đi muộn / về sớm / vắng).

Cửa hàng, tòa nhà — dashboard lưu lượng

  • GET /counts?bucket=hour cho biểu đồ, GET /occupancy cho số đang ở bên trong, GET /events?type=occupancy cho cảnh báo quá đông.

Trung tâm giám sát — gom cảnh báo

  • GET /events?type=intrusion,face,custom&since_id= mỗi 5 giây; ảnh: crop_image_url. Điện thoại của chủ camera đã được báo / gọi qua app — phần mềm của bạn dùng cho trực ban, lưu vết.

Excel / Power BI / Google Sheets — không lập trình

  • Excel: Data → From Web → Advanced, URL https://apiwebrtc.tingpush.com/api/v1/export?format=csv&from=2026-10-01T00:00:00%2B07:00, thêm header X-Api-Key.
  • Hoặc tải CSV trên app / web: Biển số → Xuất Excel, Chấm công → Xuất CSV.

10. Kiểm tra trước khi bàn giao

  • Khóa API riêng cho phần mềm này, lưu trong cấu hình máy chủ (không trong mã nguồn, không trong ứng dụng phát cho người dùng).
  • last_id lưu bền (CSDL), cùng giao dịch với bản ghi; bảng có khóa UNIQUE theo id (API v1) / event_id (API riêng).
  • Xử lý 429 (chờ 60 giây), 5xx / mất mạng (thử lại tăng dần), 401 (dừng, báo người quản trị).
  • Tham số from / to mã hóa + thành %2B.
  • Bỏ qua trường không biết và type không biết; bỏ qua test: true khi tính báo cáo.
  • Ảnh cần giữ lâu hơn thời gian lưu của gói đã tải về kho của bạn.
  • Thử mất mạng: tắt mạng camera 10 phút rồi bật lại — dữ liệu gửi bù về đủ, không trùng.
  • API riêng: chạy công cụ kiểm tra kiem_tra_api.py (đúng mã trả lời, bỏ trùng, chịu được ảnh lớn).
  • Thông báo cho người bị ghi hình / chấm công theo quy định bảo vệ dữ liệu cá nhân (ảnh khuôn mặt là dữ liệu nhạy cảm).

11. Câu hỏi thường gặp

Có webhook từ TingPush tới máy chủ của tôi không? Hiện chưa — dùng API riêng (camera đẩy thẳng, tức thì) hoặc gọi API v1 với since_id mỗi vài giây (độ trễ vài giây, không mất dữ liệu).

Dữ liệu của camera được chia sẻ cho tôi có qua API không? Có — khóa thấy mọi camera tài khoản xem được. Điều khiển thiết bị còn cần quyền điều khiển trên camera đó.

Lấy ảnh nhân viên / ảnh lần chấm công? photo_url trong /employees, face_image_url / image_url trong /attendance — tải bằng cùng header khóa API.

Đổi chủ camera thì sao? Dữ liệu cũ thuộc chủ cũ; chủ mới chỉ thấy từ lúc nhận camera.

Môi trường thử nghiệm? Dùng một camera + tài khoản riêng cho thử nghiệm; nút Gửi thử / Thử cảnh báo trên app tạo dữ liệu mẫu không cần xe / người thật.

Cần hỗ trợ? Liên hệ TingPush qua trang Liên hệ trên website — gửi kèm mã camera (LPR-…), thời điểm, yêu cầu và mã lỗi.