Skip to main content

Tracking Links (Tracking Token / Redirect)

Overview

When a user taps a link the platform sent them, the system needs to record who tapped which link and from where before forwarding them to the destination. This module owns the link-creation side; the actual redirect happens in client-api and client-web.

There are two mechanisms, chosen by content type:

Content typeMechanismOwner
Rich MenuToken-based via TrackingTokenServicecms-api (this module)
Lead Generation FormToken-basedcms-api (this module)
CampaignExternal RedirectServiceworker

The internal/modules/tracking module exposes no HTTP routes at all, mirroring the upstream NestJS module which had no controller. It acts purely as a provider, exporting TrackingTokenService for the LINE Message API and Rich Menu modules to call.

Business Flow

  1. A user creates a rich menu with a link button, and richmenu/tracking_adapter.go calls TrackingTokenService.

  2. The service generates a self-contained token and records it in the tracking_token table.

  3. The operating mode is selected by the TRACKING_REDIRECT_MODE environment variable:

    • builtin uses our own encrypted token implementation in internal/core/trackinglink
    • anything else, defaulting to legacy, uses the original redirect service in internal/externals/redirects
  4. Token structure in builtin mode, defined in internal/core/trackinglink/token.go:

    bytes = keyId(1) ‖ nonce(12) ‖ ciphertext ‖ tag(16) // AES-256-GCM, AAD "tracking-link-v1"
    token = base64url_nopad(bytes)

    The payload carries a ct field set to either "rich_menu" or "lead_gen", with a fixed key order so the byte-level output stays stable.

  5. The link embedded in the rich menu is therefore a URL containing this token.

  6. When the user taps it, client-api decrypts the token, records an event in tracking_line_users, and redirects to the destination.

  7. Results are reviewed through the Tracking LINE Users module and the rich menu report pages.

warning

The encoder in cms-api and the decoder in client-api are pinned against the same set of reference tokens. If either side drifts, every link already sent out stops working.

Key Files & Functions

FileRole
internal/modules/tracking/service.goTrackingTokenService — the port of tracking-token.service.ts
internal/modules/tracking/repository.goReads and writes the tracking_token table
internal/modules/tracking/controller.goRegisterRoutes is a no-op, registering nothing
internal/core/trackinglink/token.goBuilds the AES-256-GCM token for builtin mode
internal/core/trackinglink/config.goMode() and IsBuiltin() read TRACKING_REDIRECT_MODE
internal/externals/redirects/redirects.goClient for the original redirect service (legacy mode)
internal/modules/richmenu/tracking_adapter.goWhere the rich menu module calls in

Endpoints — none; this module registers no routes.

Connections to Other Services

  • Permissions — with no routes there are no guards; access control happens in the calling modules.
  • Tablestracking_token, tracking_line_users and content_page_utm.
  • EnvironmentTRACKING_REDIRECT_MODE selects between builtin and legacy; the AES key is referenced by the keyId embedded in each token.
  • Cross-service — line-management-client-api-go decrypts the tokens, and the worker uses RedirectService for campaign links.
  • Design specdocs/superpowers/specs/2026-07-06-builtin-tracking-redirect-design.md
  • Related modules — Rich Menu, LINE Message API, Tracking LINE Users and Campaign Management.