Skip to content

v0.6.25

Choose a tag to compare

@mikeckennedy mikeckennedy released this 07 Jun 23:23
· 14 commits to main since this release

[0.6.25] - 2026-06-07

Added

  • distinct_id support in new_event, new_page_view, and new_revenue_event payloads
    (sync and async), sent to Umami as payload field id
  • Umami Cloud API-key authentication via set_cloud_api_key(key, region=None) and
    clear_cloud_api_key(). In Cloud mode, data/management calls route to
    https://api.umami.is/v1[/region] with an x-umami-api-key header, and events are sent to
    https://cloud.umami.is/api/send — no set_url_base() or login() required. Existing
    self-hosted (login() + token) usage is byte-for-byte unchanged. Added urls.me for Cloud
    key validation in verify_token/verify_token_async. In Cloud mode, heartbeat() /
    heartbeat_async() check liveness via the authenticated /me endpoint (Cloud has no
    /api/heartbeat), and login() / login_async() raise OperationNotAllowedError to fail
    fast (the API key is the credential — there is no username/password login on Cloud).

Changed

  • validate_state error messages now mention set_cloud_api_key() alongside set_url_base() /
    login(). Same exception type (OperationNotAllowedError) and trigger conditions; text only.
  • Response models are more tolerant of variant/partial responses: WebsiteStats.comparison and
    Website.user are now optional, and Website accepts createUser (returned by team-website
    listings, where userId is null). Successful responses today are unaffected.
  • Internal: expanded the unit test suite (now 128 tests) following a suite review — added
    behavioral coverage for set_url_base validation, the validate_state guards, the
    login/login_async happy path and response-model deserialization (LoginResponse,
    WebsitesResponse), websites/websites_async, the disable() no-op (asserting no HTTP
    request is made), ip_address payload inclusion/omission, explicit-arg-overrides-defaults
    precedence, heartbeat_async self-hosted, and the verify_token/heartbeat failure branches.
    Added a sync/async parity harness and consolidated the per-file HTTP mock builders into a shared
    tests/_mocks.py (net ~210 fewer lines). No production behavior changed.

Fixed

  • website_stats_async() sent its date range as snake_case start_at/end_at query
    params, which the Umami API ignores — it silently returned all-time stats regardless
    of the requested window. Now sends camelCase startAt/endAt, matching the sync
    website_stats(). (#18)
  • active_users() and active_users_async() read the active-visitor count from a
    non-existent x key, so they always returned 0. They now read Umami's current
    visitors key (falling back to the legacy x key for compatibility). (#19)
  • website_stats() / website_stats_async() sent their url and host filters under
    the old query-param names, which Umami renamed on 2025-10-07 (urlpath,
    hosthostname) — so filtering stats by URL or hostname was a silent no-op. The
    public url/host keyword arguments are unchanged; they now map to the current
    path/hostname wire names. (#20)
  • heartbeat() / heartbeat_async() issued a POST to /api/heartbeat, which current Umami
    answers with 405 Method Not Allowed; the broad exception handler swallowed the error so the
    call always returned False even against a healthy server. They now issue a GET (the endpoint
    returns {"ok": true}).
  • new_event() and new_revenue_event() (sync) defaulted url to '/event-api-endpoint' while
    their async twins used '/'. All four now default to '/', so the sync/async twins agree and no
    placeholder path appears in dashboards.