MarketerAI — Creating Rules with Analytics Filters
Table of Contents
- Overview
- Current Rule Creation Flow
- Analytics Filter Data Structure
- Available Analytics Columns
- How the Filter Is Displayed in the UI
- Expected System Behavior
- Known Issues and Required Fixes
Overview
Marketer AI allows users to create product rules based on analytics data from advertising platforms — including Google Ads, Google Analytics 4, and Microsoft Ads. For example, a user can say:
"For products where Google Ads conversions were greater than 5 in the last 30 days, set custom label 0 to 'Marketer'"
The system should translate this command into a complete rule with a correctly configured analytics filter — including the time range.
Current Rule Creation Flow
End-to-end path
User (chat)
↓
Marketer AI (LLM + MCP tools — repo: sembot_public-mcp)
↓ calls tool create_product_rule
POST /api/public/v1/projects/{project}/product-rules
↓ App\Http\Controllers\Api\PublicApi\Rules\ProductRulesController
↓ validation: App\Http\Requests\Api\Google\Merchant\Rules\StoreRuleRequest
↓ → App\Http\Requests\Api\FilteredRequest (filter validation)
↓ saved to DB: Rule (column `filters` — JSON)
↓ dispatch: BulkMassAction (queue products_mass_actions)
↓ when rule executes:
App\Services\Products\AdditionalDataSource\AdditionalDataSourceService::processFilters()
↓ fetches data from the external platform for analytics filters
↓ replaces the analytics filter with a filter by product IDsComponents involved in analytics filter processing
| Component | File | Responsibility |
|---|---|---|
AdditionalDataSourceService | app/Services/Products/AdditionalDataSource/AdditionalDataSourceService.php | Identifies analytics filters, calls external APIs |
GoogleAdsProductsDataSource | app/Services/Products/AdditionalDataSource/GoogleAdsProductsDataSource.php | Fetches data from the Google Ads API for the filtered date range |
GoogleAnalytics4ProductsDataSource | app/Services/Products/AdditionalDataSource/GoogleAnalytics4ProductsDataSource.php | Fetches data from the GA4 API |
MicrosoftAdvertisingProductsDataSource | app/Services/Products/AdditionalDataSource/MicrosoftAdvertisingProductsDataSource.php | Fetches data from the Microsoft Ads API |
FilteredRequest | app/Http/Requests/Api/FilteredRequest.php | Validates filter structure — requires project_connection_id for analytics filters |
How an analytics filter is processed when a rule executes
When processing BulkMassAction, AdditionalDataSourceService::processFilters() does the following for each analytics filter:
- Identifies that
paramcomes from an external source (e.g.google_ads_conversions) - Creates a connector (
GoogleAdsProductsDataSource) with theproject_connection_idconnection - Uses
sub_daysorstart_date/end_dateto determine the time window for the platform query - Retrieves a list of product identifiers that meet the condition from the platform
- Replaces the analytics filter with a filter
param: google_product_id, symbol: in, value: [id1, id2, ...] - Further rule processing operates only on the local product database
Analytics Filter Data Structure
JSON stored in the database (filters column of the rules table)
{
"filterGroups": [
{
"filters": [
{
"param": "google_ads_conversions",
"symbol": ">",
"value": 5,
"project_connection_id": 123,
"sub_days": 30,
"translate_key": "connection_period"
}
]
}
]
}Analytics filter field descriptions
| Field | Type | Required | Description |
|---|---|---|---|
param | string | yes | Analytics metric name (see section below) |
symbol | string | yes | Comparison operator: >, <, =, !=, >=, <=, is_empty, not_empty |
value | number/string | yes | Threshold value for comparison |
project_connection_id | integer | yes | ID of the project's connection to the platform (e.g. a specific Google Ads account) |
sub_days | integer | conditional* | Number of days back for a relative date (e.g. 30 = last 30 days) |
start_date | string (YYYY-MM-DD) | conditional* | Start date for a fixed date range |
end_date | string (YYYY-MM-DD) | conditional* | End date for a fixed date range |
translate_key | string | yes | Display mode in the UI: connection_period (relative date) or connection_range (fixed range) |
* Either
sub_daysor thestart_date+end_datepair is required. When both are present,sub_daystakes priority.
Two time range modes
Mode 1: Relative date (sub_days)
The user provides a number of days back from today. The range is calculated dynamically each time the rule executes.
{
"sub_days": 30,
"translate_key": "connection_period"
}UI displays: "Last 30 days"
When to use: whenever the user says "last N days", "over the past month", "in the last 90 days", etc.
Mode 2: Fixed date range (start_date + end_date)
The user provides specific dates — the range is fixed and does not change over time.
{
"start_date": "2025-01-01",
"end_date": "2025-03-31",
"translate_key": "connection_range"
}UI displays: "2025-01-01 – 2025-03-31"
When to use: when the user provides specific dates, e.g. "from January 1 to March 31, 2025".
Available Analytics Columns
Google Ads (project_connection_id → connection of type GOOGLE_ADS)
param | Metric | Allowed operators |
|---|---|---|
google_ads_impressions | Impressions | >, <, =, !=, >=, <=, is_empty, not_empty |
google_ads_clicks | Clicks | >, <, =, !=, >=, <=, is_empty, not_empty |
google_ads_cost | Cost | >, <, =, !=, >=, <=, is_empty, not_empty |
google_ads_avg_cpc | Average CPC | >, <, =, !=, is_empty, not_empty |
google_ads_conversions | Conversions | >, <, =, !=, is_empty, not_empty |
google_ads_conv_rate | Conversion rate | >, <, =, !=, is_empty, not_empty |
google_ads_total_conv_value | Total conversion value | >, <, =, !=, is_empty, not_empty |
google_ads_cost_per_conversion | Cost per conversion | >, <, =, !=, is_empty, not_empty |
google_ads_roas | ROAS | >, <, =, !=, is_empty, not_empty |
Google Analytics 4 (project_connection_id → connection of type GOOGLE_ANALYTICS_4)
Data available from GA4: sessions, revenue, users, cart, orders, and more — depending on the client's GA4 configuration.
Microsoft Ads (project_connection_id → connection of type BING_ADS)
Analogous metrics to Google Ads: impressions, clicks, cost, conversions, etc.
How the Filter Is Displayed in the UI
The frontend (analytics-picker.component.ts) reads sub_days, start_date, end_date from the filter and displays:
sub_days set → "Last 30 days" ← CORRECT view
sub_days = null and no start_date/end_date → "{{startDate}} - {{endDate}}" ← INCORRECT view (unresolved template variables)
start_date + end_date set → "2025-01-01 – 2025-03-31"When AI creates a rule without sub_days and without start_date/end_date — the UI shows raw translation template variables, signaling that the time range data is missing.
Expected System Behavior
Step 1 — Intent identification
When the user provides a command with an analytics filter, the AI must extract:
- Metric →
param(e.g. "Google Ads conversions" →google_ads_conversions) - Operator →
symbol(e.g. "greater than" →>, "less than" →<, "equal to" →=) - Threshold value →
value(e.g.5) - Time range →
sub_daysorstart_date/end_date(e.g. "last 30 days" →sub_days: 30) - Platform → connection type (e.g. "Google Ads" →
GOOGLE_ADS)
Step 2 — Asking for missing data
If the user did not provide a time range — the AI MUST ask before creating the rule.
Example AI question to the user:
"What time period would you like to check the data for? You can provide:— a number of days back (e.g. last 30 days, 90 days)— a specific date range (e.g. from 2025-01-01 to 2025-03-31)"
Creating a rule with a default range without asking is not allowed — the user must consciously make this decision, as the time range directly affects which products will be labeled.
Step 3 — Identifying the project connection
The AI must know the project_connection_id — the ID of the specific advertising account in the project.
If the AI does not know the ID:
- It calls a tool that lists available project connections for the given platform
- If there is exactly one connection of that type — it uses it automatically
- If there are multiple connections — it presents the list and asks the user which one to select
Step 4 — Building the complete filter
The AI calls the create_product_rule tool with the complete filter:
{
"filterGroups": [
{
"filters": [
{
"param": "google_ads_conversions",
"symbol": ">",
"value": 5,
"project_connection_id": 123,
"sub_days": 30,
"translate_key": "connection_period"
}
]
}
],
"action": {
"action": "override",
"param": "custom_label_0",
"value": "Marketer"
},
"active": true
}Correctness rules:
- For relative date:
sub_days(integer > 0) +translate_key: "connection_period" - For fixed range:
start_date+end_date(format YYYY-MM-DD) +translate_key: "connection_range" - The
symbolfield must contain one of the allowed operators — it must never be empty - The
project_connection_idfield is always required
Step 5 — Confirmation before saving
Before sending the request to the API, the AI presents the user with a summary of the rule being created and waits for confirmation:
"Creating the rule:— Condition: Google Ads conversions > 5 (last 30 days, account: Sklep PL)— Action: set custom label 0 = 'Marketer'Do you confirm?"
Known Issues and Required Fixes
Issue 1 — Missing sub_days in the created filter
Symptom: The UI displays - instead of e.g. "Last 30 days".
Cause: The MCP tool (create_product_rule in sembot_public-mcp) does not pass the sub_days field to the API, even when the user provided a relative range.
Required fix:
- Add the
sub_daysfield (integer) to the MCP tool schema in the filter definition - Add the instruction: for relative dates, use
sub_days+translate_key: "connection_period", notstart_date/end_date
Issue 2 — Missing translate_key in the filter
Symptom: The UI does not know in which mode to display the date range button.
Required fix: Always send translate_key with analytics filters (connection_period or connection_range).
Issue 3 — Missing symbol operator
Symptom: Filter created without an operator or with an incorrect field name.
Cause: The operator field in the API is called symbol (not operator). The AI may use the wrong name.
Required fix: In the MCP tool schema, the filter operator field must be named symbol with an enum of allowed values: >, <, =, !=, >=, <=, is_empty, not_empty.
Issue 4 — AI does not ask about the time range when data is missing
Symptom: The AI creates a rule without asking about the range, resulting in an incomplete filter.
Required fix: Add an instruction to the agent's system prompt or the MCP tool description: before calling create_product_rule with an analytics filter, always ensure you have the time range — if the user did not provide it, ask.