Skip to main content

70maiclient

hacs_badge Version Downloads buy me a coffee

A reverse-engineered Python client for the 70mai dashcam cloud API (eu-api.70mai.com), built from a Postman-exported HTTPS capture of the official iOS app and cross-checked against a decompiled Android APK.

Disclaimer

It's not affiliated with, endorsed by, or supported by 70mai. The underlying API is undocumented and can change without notice. If someone from 70mai would like to contribute or collaborate please contact me at me@lorenzodeluca.dev

Used by homeassistant-70mai, a Home Assistant integration built on top of this client.

Install

PyPI project name is 70maiclient (PyPI names can't start with a digit in a way that's also a valid Python identifier, so the distribution name differs from the import name below):

pip install 70maiclient

Usage

import uuid

from client70mai import MaiClient

client = MaiClient(uuid=str(uuid.uuid4()).upper())

client.login(email="your-email", plain_password="your-password")
devices = client.get_all_device_for_user()
print(devices)

detail = client.get_dashcam_detail(device_id="your-device-id")
print(detail)

client.logout()

See example.py for the same script standalone, and console.py for an interactive rich-based CLI that exercises every endpoint (Italian-language prompts):

pip install "70maiclient[console]"
python3 console.py

Layout

  • client70mai/client.py — MaiClient, the only class consumers use. Holds the session, the device uuid, the auth token, and one method per API endpoint.
  • client70mai/signing.py — SignatureProvider protocol plus BanyacKeyMd5SignatureProvider, the verified implementation of the server's request-signing scheme.
  • client70mai/exceptions.py — MaiError and subclasses for transport, HTTP, response-parsing, auth, and signing failures.
  • client70mai/__init__.py — public re-exports.
  • example.py — minimal login → list devices → detail → logout script.
  • console.py — interactive rich-based CLI over MaiClient for manually exercising every endpoint (Italian-language prompts).
  • re/ — the APK, extracted dex/native libs, and Ghidra/pyghidra scripts used to reverse the signing algorithm. Not imported by client70mai, and not shipped in the PyPI package.
  • api-refs/ — captured traffic used as ground truth: Postman exports (request-only, no response bodies) and *.proxymanlogv2 archives (a zip of one JSON file per request, request/response bodyData fields base64-encoded — these DO carry real response bodies, unlike the Postman exports, and are how most "CONFIRMED response" notes in client.py were derived). Not shipped in the PyPI package.

Authentication model

There is no Authorization header or cookie anywhere in the captured traffic. Every authenticated call instead sends, inside its JSON body:

  • uuid — per-install device UUID (caller-supplied, any valid UUID string).
  • channel — constant 3001013 (CHANNEL_ID in client.py) on every request, including unauthenticated ones.
  • ts — client epoch-millisecond timestamp, unique per request.
  • token — opaque session token from userLoginPwd/login(), reused verbatim until logout().
  • sig — md5(token + uuid + channel + ts + SECRET), SECRET recovered from the Android app's native libbanyackey.so (see signing below).

MaiClient._signed_body() assembles this envelope for every method except login() and get_ts(). Calling any endpoint before login() raises MaiNotAuthenticatedError.

login() uses a different, separately-verified construction for the passWord field (md5("dashcam" + plain_password + SECRET)) and its own sig (md5(deviceType + email + "null" + passWord + ts + SECRET)) — see the signing.py module docstring for the full decompilation trail.

Response envelope

Every call returns the raw parsed JSON body as Dict[str, Any], matching the confirmed MaiCommonResult<T> shape:

{"error": bool, "errorCode": int, "errorMessage": str, "resultBodyObject": T}

resultBodyObject's shape (T) is not unwrapped by the client. The original Postman captures had no successful response bodies at all, so every schema was originally a best-effort guess; a 2026-09 Proxyman capture (api-refs/*.proxymanlogv2) since confirmed the real resultBodyObject shape for many endpoints (each such method's docstring in client.py says "CONFIRMED response" and gives the shape). Endpoints without that note are still unconfirmed — treat any field access on their response as unverified until you observe real traffic. login() is the one method that always reads a specific field (resultBodyObject.token), confirmed, because the client cannot function without it.

Extending the client

  • New endpoints follow the existing pattern: build an extra dict of endpoint-specific fields, pass it to self._signed_body(extra), POST via self._post(path, body).
  • Do not invent response-field extraction beyond what a real captured response confirms — raise/return the raw dict instead, as every existing method does.
  • If you reverse-engineer a new signing variant, add it to SignatureProvider (protocol) and BanyacKeyMd5SignatureProvider (implementation) rather than hardcoding it in client.py.
  • Live view (P2P streaming) is not implemented — static analysis found getTutkToken/living endpoints backed by the ThroughTek/TUTK SDK, but no request/response shape was captured or confirmed.

Releasing

Tagging a GitHub Release publishes to PyPI automatically via .github/workflows/publish.yml (trusted publishing, no stored token). Bump version in pyproject.toml before tagging.

License

GNU AGPLv3 © Lorenzo De Luca

Metadata

Release files for 70maiclient 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for 70maiclient 0.3.0
File Size Uploaded Provenance
70maiclient-0.3.0.tar.gz 34.2 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for 70maiclient 0.3.0
File Interpreter ABI Platform
70maiclient-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 64.9 kB

Release files / 70maiclient-0.3.0.tar.gz

Download URL 70maiclient-0.3.0.tar.gz
Size 34.2 kB
Tags Source
SHA-256 checksum
How to use checksums
c7ddf6062dcf72f4be7c7bc6544d45a73eeeb5d1e6198587c88bcc329d6e4bd8
BLAKE2b-256 checksum
How to use checksums
73ce54b227f4570016012c42a40d7535620bdea9c0f01fc49936b4d5411f0942
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

Release files / 70maiclient-0.3.0-py3-none-any.whl

Download URL 70maiclient-0.3.0-py3-none-any.whl
Size 30.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bd651ff7bdbfc1087ec9427bfeea25ee02c75b95d7b4d66e6b5d2f20c36af0fb
BLAKE2b-256 checksum
How to use checksums
79e35e89ac12b58c9bb588cf42c5a764cf4ad572d86cc1d1615d2d887d84ead1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

Security for 70maiclient 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for 70maiclient-0.3.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page