ruLog in to Senler

RefLink: plugin example

RefLink is an open-source, full-featured referral campaign plugin for Senler. One repository contains the frontend, backend, shared contracts, migrations, user documentation, and self-hosting files.

This guide explains the application in the order in which real work happens: how the embedded page opens, how the backend gains access to a project, how AI calls actions through MCP, and how an automation step runs. You can then move on to the specific directories and files.

How To Use The Example

RefLink is useful when a new plugin needs several capabilities at once:

  • an embedded page inside the cabinet;
  • OAuth access to the Senler API for a project;
  • a custom automation step and its configuration form;
  • backend actions available to AI through MCP;
  • webhooks with signature verification and duplicate-processing protection;
  • shared types between the frontend and backend;
  • public documentation in Russian and English.

Do not copy the entire application. Select the required flow first, then reuse the corresponding layer and adapt permissions, data, and session lifetimes to your product.

RefLink does not implement an agent tool configurator. That context follows the same Senler Bridge approach, but its backend operation is marked with AgentToolConfigurator and its frontend handles a tool_configurator launch.

Technologies And Their Roles

PartTechnologyPurpose
FrontendReact and Senler UIThe embedded page interface, step configuration form, and cabinet communication through Senler Bridge.
BackendNestJSHTTP controllers, DTO validation, application modules, OAuth, sessions, and webhook handlers.
API contractOpenAPI and NestJS SwaggerRegular endpoint descriptions and selected MCP application actions.
Senler API@senlerio/apiTyped calls for channels, leads, variables, agents, segments, and automations.
StoragePostgreSQL and TypeORMCampaigns, OAuth connections, processed events, and technical reward-delivery state.
Local runtimeDocker ComposeReproducible startup of the frontend, backend, and PostgreSQL.

How A Request Flows

RefLink has three primary flows. They share one backend but use different entry points and permissions.

StepRepository sectionWhat it explains
1README.mdApplication purpose, campaign modes, startup, and documentation publication.
2docs/architecture.mdWhere data lives and why referral statistics are not duplicated in PostgreSQL.
3docs/senler-app-setup.mdWhich URLs, events, OAuth permissions, and result fields belong in application settings.
4contracts/src/index.tsShared types, campaign modes, and step results used by both sides of the application.
5frontend/srcBridge connection, code-to-session exchange, and switching between the main page and configurator.
6backend/src/core/sessionOne-time code verification, project-scoped sessions, and endpoint protection.
7backend/src/resources/oauthOAuth callback, connection storage, and token refresh.
8backend/src/integrations/senlerThe isolated Senler API adapter.
9backend/src/resources/campaignsCampaign CRUD, statistics, automation creation, and OpenAPI actions.
10backend/src/resources/referralsWebhook signatures, deduplication, attribution, and reward delivery.
11docs/public and assets/catalogKeeping user documentation and application catalog assets beside the code.
12docker-compose.ymlHow the frontend, backend, database, health check, and environment variables fit together.

Key Architectural Decisions

The Project Comes From Trusted Context

A project_id in a query parameter, request body, or AI argument is not proof of access. RefLink determines the project only after verifying a launch_code, application session, management token, OAuth subject, or webhook signature. Services then receive the confirmed ID through @EmbeddedProject().

This prevents a user or AI from substituting another project's ID in an ordinary parameter.

Authorization Is Split By Purpose

ValueIssuerPurpose
launch_codeSenlerConfirms a page or management-session launch once and carries signed project context.
session_tokenRefLink backendLets the opened frontend call the RefLink backend within one project.
management_tokenRefLink backendLets Senler call a particular application action through MCP.
OAuth access/refresh tokenSenler OAuthLets the RefLink backend call the Senler API with user-approved project permissions.

Never use one of these tokens in place of another. OAuth secrets are encrypted through SecretVaultService, and the new access/refresh pair is persisted together while holding a connection lock in OAuthConnectionService.

The SDK Is Separated From Domain Logic

Campaign controllers and services do not construct Senler HTTP requests directly. All such calls are collected in SenlerApiClient, which uses the official SDK.

This makes SDK upgrades and response testing easier and provides one place to isolate operations that are not yet available in a published client version.

Data Ownership Is Chosen Up Front

RefLink stores campaign settings, OAuth connections, and technical state in PostgreSQL. Referral relationships and counters remain in Senler lead variables. The application does not maintain a second copy of the same statistics or synchronize two sources of truth.

This is not a universal data model, but it demonstrates a universal principle: decide who owns each data type before implementation and store locally only the data that belongs to the application.

What To Reuse In A New Application

  1. Create separate packages or directories for shared contracts, frontend, backend, and the integration adapter.
  2. Implement launch_code verification and a project-scoped session before building business forms.
  3. Add OAuth with only the required permissions, encrypt tokens, and persist the rotated token pair atomically after refresh.
  4. Connect Senler UI and Senler Bridge, then handle every required context.launch.type explicitly.
  5. Keep business endpoints as regular DTO-based controllers, then publish selected operations with AppAction, AgentToolConfigurator, or AutomationStepConfigurator.
  6. Validate generated OpenAPI with senler-app validate-openapi before saving its URL in application settings.
  7. For webhooks, implement signature verification, event IDs, idempotency, and stalled-work recovery before adding the domain use case.
  8. Keep user documentation and catalog assets beside the source and check the Russian and English versions in CI.

What Not To Copy Without Review

  • RefLink OAuth permissions: a new application may require fewer or different permissions.
  • Session lifetime: choose it according to risk, relaunch behavior, and product requirements.
  • Lead variable names, webhook events, result fields, and reward rules: they belong to the referral application's domain model.
  • Endpoint URLs and environment variable names: they must belong to your domain and infrastructure.
  • Every MCP action at once: publish only operations that AI must call, and set read_only, destructive, and idempotent accurately.