Ads-Cockpit API Documentation
Overview
The Ads-Cockpit API provides two authentication methods:
- JWT Authentication - For the web application (user sessions)
- API Key Authentication - For external integrations (MCP, AI agents, scripts)
Base URL: https://app.firemetrix.io/api/v1
For local development: https://app.firemetrix.io/api/v1
Authentication
JWT Authentication
Used by the web application for user sessions.
Authorization: Bearer <jwt_token>
Obtain a token via Google OAuth at POST /auth/google.
API Key Authentication
Used for external integrations. API keys are created in Settings → API Keys.
X-API-Key: acs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Key Format: acs_ + 32 random characters
Available Scopes:
| Scope | Description |
|---|---|
campaigns:read | Query campaign data, search terms, daily metrics |
products:read | Query product data, details, daily metrics, feeds |
accounts:read | Query account data, details, daily metrics, campaigns |
search_terms:read | Account search terms, mining runs/suggestions, negative keywords |
customers:read | Customer profiles and memories |
customers:write | Update customer profiles, create/delete memories |
planning:read | Board, calendar, todos |
planning:write | Move board cards, update card settings, CRUD todos, set assignees |
campaigns:write | Set campaign budget, status, bidding strategy |
keywords:write | Add negative keywords to campaigns |
logs:read | Activity logs (account + team) |
logs:write | Create activity logs (account + team) |
External Query API
Read and write endpoints for external integrations (MCP, AI agents, scripts). Requires API key authentication.
GET/query/campaigns
Query campaigns with performance metrics.
Required Scope: campaigns:read
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
account_id | string | - | Filter by account ID |
status | string | - | Filter by status: ENABLED, PAUSED, REMOVED |
channel_type | string | - | Filter by channel: SEARCH, SHOPPING, DISPLAY, PERFORMANCE_MAX |
days | integer | 30 | Performance data for last N days (max: 365) |
min_cost | float | - | Minimum cost in EUR |
max_cost | float | - | Maximum cost in EUR |
min_roas | float | - | Minimum ROAS |
max_roas | float | - | Maximum ROAS |
sort | string | -cost | Sort field: cost, roas, conversions, clicks, impressions, name. Prefix with - for descending |
limit | integer | 100 | Results per page (max: 1000) |
offset | integer | 0 | Pagination offset |
Example Request:
curl -H "X-API-Key: acs_xxx" \
"https://app.firemetrix.io/api/v1/query/campaigns?status=ENABLED&days=30&sort=-roas&limit=10"
Example Response:
{
"data": [
{
"id": "17539449348",
"accountId": "7664254989",
"accountName": "Example Account",
"name": "Campaign Name",
"status": "ENABLED",
"channelType": "PERFORMANCE_MAX",
"metrics": {
"periodDays": 30,
"cost": 1234.56,
"impressions": 50000,
"clicks": 2500,
"conversions": 125,
"conversionValue": 8750.00,
"roas": 7.08,
"ctr": 0.05,
"cpc": 0.49
}
}
],
"meta": {
"total": 207,
"limit": 10,
"offset": 0
}
}
GET/query/products
Query products with performance and price data.
Required Scope: products:read
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
account_id | string | - | Filter by account ID |
days | integer | 30 | Performance data for last N days (max: 365) |
min_roas | float | - | Minimum ROAS |
max_roas | float | - | Maximum ROAS |
price_vs_benchmark | string | - | Price comparison: above, below, equal |
min_price_diff_percent | float | - | Minimum price difference % vs benchmark |
availability | string | - | Filter: in_stock, out_of_stock |
brand | string | - | Filter by brand name |
has_impressions | boolean | - | Only products with impressions (true) |
sort | string | -cost | Sort field: cost, roas, price_diff, price |
limit | integer | 100 | Results per page (max: 1000) |
offset | integer | 0 | Pagination offset |
Example Request:
curl -H "X-API-Key: acs_xxx" \
"https://app.firemetrix.io/api/v1/query/products?price_vs_benchmark=above&max_roas=2&limit=5"
Example Response:
{
"data": [
{
"offerId": "SKU123",
"title": "Product Name",
"brand": "Brand X",
"price": 49.99,
"benchmarkPrice": 39.99,
"priceDiffPercent": 25.0,
"availability": "in_stock",
"metrics": {
"periodDays": 30,
"impressions": 1200,
"clicks": 45,
"cost": 22.50,
"conversionValue": 149.97,
"roas": 6.66
}
}
],
"meta": {
"total": 156,
"limit": 5,
"offset": 0
}
}
GET/query/accounts
Query accounts with aggregated performance metrics.
Required Scope: accounts:read
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
mcc_id | string | - | Filter sub-accounts by MCC account ID |
search | string | - | Search accounts by name (case-insensitive, partial match) |
days | integer | 30 | Performance data for last N days (max: 365) |
min_roas | float | - | Minimum ROAS |
sort | string | -cost | Sort field: cost, roas, conversions, name |
limit | integer | 100 | Results per page (max: 1000) |
offset | integer | 0 | Pagination offset |
Example Requests:
# All accounts sorted by ROAS
curl -H "X-API-Key: acs_xxx" \
"https://app.firemetrix.io/api/v1/query/accounts?days=30&sort=-roas"
# Sub-accounts of a specific MCC
curl -H "X-API-Key: acs_xxx" \
"https://app.firemetrix.io/api/v1/query/accounts?mcc_id=8427925949"
# Search by name
curl -H "X-API-Key: acs_xxx" \
"https://app.firemetrix.io/api/v1/query/accounts?search=bodenverkauf"
Example Response:
{
"data": [
{
"id": "7664254989",
"name": "Example Account",
"currency": "EUR",
"status": "active",
"isMcc": false,
"metrics": {
"periodDays": 30,
"cost": 24804.95,
"impressions": 2776311,
"clicks": 46702,
"conversions": 1278.17,
"conversionValue": 151336.90,
"roas": 6.10
}
}
],
"meta": {
"total": 66,
"limit": 100,
"offset": 0
}
}
GET/query/team/members
List all team members with their IDs, roles, and contact info. Useful for getting user UUIDs needed for todo assignments and log attribution.
Required Scope: accounts:read
Example Request:
curl -H "X-API-Key: acs_xxx" \
"https://app.firemetrix.io/api/v1/query/team/members"
Example Response:
{
"data": [
{
"id": "c78b0c33-1234-5678-abcd-ef1234567890",
"email": "user@example.com",
"name": "Max Mustermann",
"avatarUrl": "https://lh3.googleusercontent.com/...",
"role": "owner",
"joinedAt": "2025-12-01T10:00:00Z"
}
],
"count": 1
}
GET/query/accounts/{accountID}
Get account details with metadata.
Required Scope: accounts:read
Example Request:
curl -H "X-API-Key: acs_xxx" \
"https://app.firemetrix.io/api/v1/query/accounts/7664254989"
GET/query/accounts/{accountID}/metrics/daily
Get daily aggregated metrics for an account.
Required Scope: accounts:read
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
start_date | string | Yes | Start date (YYYY-MM-DD) |
end_date | string | Yes | End date (YYYY-MM-DD) |
GET/query/accounts/{accountID}/campaigns
List campaigns for an account. Optionally include metrics summary.
Required Scope: accounts:read
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
start_date | string | No | Start date for metrics summary |
end_date | string | No | End date for metrics summary |
GET/query/campaigns/{campaignID}/search-terms
Get search term insights for a campaign.
Required Scope: campaigns:read
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
days | integer | 30 | Analysis period (max: 90) |
GET/query/campaigns/{campaignID}/metrics/daily
Get daily metrics for a campaign.
Required Scope: campaigns:read
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
start_date | string | No | Start date (YYYY-MM-DD) |
end_date | string | No | End date (YYYY-MM-DD) |
days | integer | 30 | Fallback if dates not provided (max: 365) |
GET/query/campaign/{campaignId}
Get detailed campaign information including budget, bidding strategy, and resource names. Useful for inspecting campaign state before making write operations.
Required Scope: campaigns:read
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
account_id | string | Yes | Google Ads account ID |
Example Request:
curl -H "X-API-Key: acs_xxx" \
"https://app.firemetrix.io/api/v1/query/campaign/17539449348?account_id=7664254989"
Example Response:
{
"campaignId": "17539449348",
"campaignName": "Brand Search",
"campaignResourceName": "customers/7664254989/campaigns/17539449348",
"status": "ENABLED",
"channelType": "SEARCH",
"biddingStrategyType": "TARGET_ROAS",
"targetRoas": 4.0,
"budgetResourceName": "customers/7664254989/campaignBudgets/12345678",
"dailyBudgetEur": 50.0,
"budgetMicros": 50000000
}
GET/query/products/{offerID}
Get product details.
Required Scope: products:read
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
account_id | string | Yes | The Google Ads account ID |
GET/query/products/{offerID}/metrics/daily
Get daily metrics for a product.
Required Scope: products:read
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
account_id | string | Yes | The Google Ads account ID |
start_date | string | Yes | Start date (YYYY-MM-DD) |
end_date | string | Yes | End date (YYYY-MM-DD) |
GET/query/accounts/{accountID}/feeds
List product feeds for an account.
Required Scope: products:read
GET/query/accounts/{accountID}/search-terms
Get aggregated search terms for an account.
Required Scope: search_terms:read
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
days | integer | 30 | Analysis period (max: 365) |
GET/query/accounts/{accountID}/mining/runs
List keyword mining runs for an account.
Required Scope: search_terms:read
GET/query/accounts/{accountID}/mining/runs/{runID}
Get a specific mining run with category breakdown.
Required Scope: search_terms:read
GET/query/accounts/{accountID}/mining/runs/{runID}/suggestions
Get suggestions for a specific mining run.
Required Scope: search_terms:read
GET/query/accounts/{accountID}/mining/suggestions/pending
Get all pending (unreviewed) suggestions for an account.
Required Scope: search_terms:read
GET/query/accounts/{accountID}/negative-keywords
List negative keyword lists for an account.
Required Scope: search_terms:read
GET/query/accounts/{accountID}/negative-keywords/{listID}
Get a specific negative keyword list with all keywords.
Required Scope: search_terms:read
GET/query/accounts/{accountID}/customer-profile
Get customer profile for an account.
Required Scope: customers:read
GET/query/accounts/{accountID}/memories
Get customer memories for an account.
Required Scope: customers:read
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
category | string | No | Filter by category: allgemein, kundengespraeche, sonstige |
GET/query/planning/board
Get the planning board with all cards.
Required Scope: planning:read
GET/query/planning/status-config
Get board status configuration (labels and colors).
Required Scope: planning:read
GET/query/planning/calendar
Get calendar events in a date range.
Required Scope: planning:read
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
start | string | Yes | Start date (YYYY-MM-DD) |
end | string | Yes | End date (YYYY-MM-DD) |
GET/query/accounts/{accountID}/todos
List todos for an account.
Required Scope: planning:read
Response: Array of PlanningTodoWithAssignee objects:
[
{
"id": "uuid",
"accountId": "7664254989",
"title": "Budget-Optimierung durchführen",
"description": "ROAS-Ziele überprüfen und Budgets anpassen",
"dueDate": "2026-02-20",
"assignedTo": "uuid",
"isCompleted": false,
"assigneeName": "Max Müller",
"assigneeAvatarUrl": "https://..."
}
]
Account Events (Internal API)
CRUD endpoints for event markers on the KPI chart. Requires JWT auth.
GET/api/v1/accounts/{accountID}/events?start={date}&end={date}
List events in date range.
POST/api/v1/accounts/{accountID}/events
Create event. Body: { eventDate, title, description?, color, icon? }
PUT/api/v1/accounts/{accountID}/events/{eventID}
Update event. Body: partial fields.
DELETE/api/v1/accounts/{accountID}/events/{eventID}
Delete event. Returns 204.
GET/query/accounts/{accountID}/logs
Get activity logs for an account.
Required Scope: logs:read
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 0 | Results per page |
offset | integer | 0 | Pagination offset |
log_type | string | - | Filter: manual, ai_insight, google_ads, system, user_action, sync |
GET/query/logs
Get all activity logs for the team.
Required Scope: logs:read
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 0 | Results per page |
offset | integer | 0 | Pagination offset |
log_type | string | - | Filter by log type |
External Write API
Write endpoints for external integrations. Requires API key with the corresponding write scope.
POST/query/campaign/{campaignId}/budget
Set the daily budget for a campaign. This immediately changes the budget in Google Ads.
Required Scope: campaigns:write
Request Body:
{
"account_id": "7664254989",
"daily_budget_eur": 50.0
}
daily_budget_eur: Must be between 0 and 1,000,000 EUR
Example Request:
curl -X POST -H "X-API-Key: acs_xxx" -H "Content-Type: application/json" \
"https://app.firemetrix.io/api/v1/query/campaign/17539449348/budget" \
-d '{"account_id":"7664254989","daily_budget_eur":50.0}'
Example Response:
{
"success": true,
"previous_budget_eur": 30.0,
"new_budget_eur": 50.0
}
POST/query/campaign/{campaignId}/status
Enable or pause a campaign. This immediately changes the status in Google Ads.
Required Scope: campaigns:write
Request Body:
{
"account_id": "7664254989",
"status": "PAUSED"
}
status: Must beENABLEDorPAUSED
Example Request:
curl -X POST -H "X-API-Key: acs_xxx" -H "Content-Type: application/json" \
"https://app.firemetrix.io/api/v1/query/campaign/17539449348/status" \
-d '{"account_id":"7664254989","status":"PAUSED"}'
Example Response:
{
"success": true,
"previous_status": "ENABLED",
"new_status": "PAUSED"
}
POST/query/campaign/{campaignId}/bidding
Change the bidding strategy for a campaign. This immediately changes the strategy in Google Ads.
Required Scope: campaigns:write
Request Body:
{
"account_id": "7664254989",
"strategy": "TARGET_ROAS",
"target_roas": 4.0
}
strategy: Must beTARGET_ROAS,MAXIMIZE_CONVERSIONS,MAXIMIZE_CLICKS, orMANUAL_CPCtarget_roas: Required when strategy isTARGET_ROAS, must be > 0
Example Request:
curl -X POST -H "X-API-Key: acs_xxx" -H "Content-Type: application/json" \
"https://app.firemetrix.io/api/v1/query/campaign/17539449348/bidding" \
-d '{"account_id":"7664254989","strategy":"TARGET_ROAS","target_roas":4.0}'
Example Response:
{
"success": true,
"previous_strategy": "MAXIMIZE_CONVERSIONS",
"new_strategy": "TARGET_ROAS"
}
POST/query/negative-keywords
Add negative keywords to campaigns. Creates a shared negative keyword list and links it to the specified campaigns.
Required Scope: keywords:write
Request Body:
{
"account_id": "7664254989",
"campaign_ids": ["17539449348", "18234567890"],
"keywords": [
{ "text": "gratis", "match_type": "BROAD" },
{ "text": "kostenlos", "match_type": "EXACT" }
]
}
campaign_ids: At least one campaign ID requiredkeywords: At least one keyword required, maximum 5000 per requestmatch_type: Must beBROAD,PHRASE, orEXACT
Example Request:
curl -X POST -H "X-API-Key: acs_xxx" -H "Content-Type: application/json" \
"https://app.firemetrix.io/api/v1/query/negative-keywords" \
-d '{"account_id":"7664254989","campaign_ids":["17539449348"],"keywords":[{"text":"gratis","match_type":"BROAD"}]}'
Example Response:
{
"success": true,
"added_count": 1,
"shared_set_name": "MCP-Agent 2026-04-26"
}
Note: All write operations are logged in the api_audit_log table for traceability.
PUT/query/accounts/{accountID}/customer-profile
Update customer profile for an account (partial update — all fields optional).
Required Scope: customers:write
Request Body:
{
"customerType": "E-Commerce",
"servicesProducts": "Bio-Lebensmittel Online-Shop",
"industry": "Lebensmittel",
"websiteStrengths": ["Gute Produktbilder", "Schneller Versand"],
"websiteWeaknesses": ["Keine Bewertungen"]
}
Response: Updated CustomerProfile object.
POST/query/accounts/{accountID}/memories
Create a new customer memory entry.
Required Scope: customers:write
Request Body:
{
"category": "kundengespraeche",
"content": "Kunde möchte Budget auf 5000€/Monat erhöhen",
"entryDate": "2026-02-14T10:00:00Z",
"userId": "uuid"
}
category:allgemein,kundengespraeche, orsonstigeentryDate: Optional, defaults to nowuserId: Optional, for tracking who created the entry
Response: Created CustomerMemory object (201).
DELETE/query/accounts/{accountID}/memories/{memoryID}
Delete a customer memory entry.
Required Scope: customers:write
Response: {"status": "ok"}
POST/query/accounts/{accountID}/logs
Create an activity log entry for an account.
Required Scope: logs:write
Request Body:
{
"logType": "ai_insight",
"message": "ROAS um 15% gestiegen nach Budget-Anpassung",
"metadata": {"roas_before": 3.2, "roas_after": 3.7},
"userId": "uuid",
"eventDate": "2026-01-30"
}
logType:manual,ai_insight,google_ads,system,user_action,sync,errormetadata: Optional JSON objectuserId: OptionaleventDate: Optional date (YYYY-MM-DD) when the event occurred. Used for chart positioning instead ofcreatedAt.
Response: Created ActivityLog object (201).
POST/query/logs
Create a team-level activity log entry (not tied to a specific account).
Required Scope: logs:write
Request Body:
{
"logType": "system",
"message": "Automatische Budget-Analyse abgeschlossen",
"accountId": "system"
}
accountId: Optional, defaults to"system"
Response: Created ActivityLog object (201).
PUT/query/planning/board/{accountID}/move
Move a board card to a new status and position.
Required Scope: planning:write
Request Body:
{
"status": "zu_bearbeiten",
"position": 0,
"userId": "uuid"
}
status:onboarding,setup,wartend,zu_bearbeiten,rueckfragenposition: 0-based position within the status columnuserId: Optional, for tracking who moved the card
Response: {"status": "ok"}
PUT/query/planning/board/{accountID}/settings
Update card settings (cycle duration, due date).
Required Scope: planning:write
Request Body:
{
"cycleDurationDays": 14,
"dueDate": "2026-03-01",
"clearDueDate": false
}
Response: {"status": "ok"}
POST/query/planning/todos
Create a new planning todo.
Required Scope: planning:write
Request Body:
{
"accountId": "7664254989",
"title": "Budget-Optimierung durchführen",
"description": "ROAS-Ziele überprüfen und Budgets anpassen",
"dueDate": "2026-02-20",
"assignedTo": "uuid",
"userId": "uuid"
}
accountIdandtitleare requireddescription: Optional text descriptiondueDate: Optional due date (YYYY-MM-DD)assignedTo: Optional user UUID for assignmentuserId: Optional user UUID for tracking who created the todo (if omitted,createdBywill be null)
Response: Created PlanningTodoWithAssignee object (201).
PATCH/query/planning/todos/{todoID}
Update a planning todo (partial update).
Required Scope: planning:write
Request Body:
{
"title": "Updated title",
"description": "Detailed description of the task",
"dueDate": "2026-02-20",
"assignedTo": "uuid",
"isCompleted": true,
"userId": "uuid"
}
- All fields optional
description: Text description (set tonullto clear)assignedTo: User UUID (set tonullto unassign)userId: Optional, for tracking who completed/updated the todo
Response: Updated PlanningTodoWithAssignee object.
DELETE/query/planning/todos/{todoID}
Delete a planning todo.
Required Scope: planning:write
Response: {"status": "ok"}
PUT/query/accounts/{accountID}/assignees
Set the user assignments for an account (replaces all existing assignments).
Required Scope: planning:write
Request Body:
{
"userIds": ["uuid-1", "uuid-2"]
}
- Pass empty array to remove all assignments
Response: {"status": "ok"}
API Key Management
API keys are managed via JWT-authenticated endpoints.
GET/teams/{teamId}/api-keys
List all API keys for a team.
Required Role: Any team member
Response:
[
{
"id": "uuid",
"teamId": "uuid",
"name": "MCP Integration",
"keyPrefix": "acs_LCLf",
"scopes": ["campaigns:read", "products:read", "accounts:read"],
"lastUsedAt": "2026-01-30T18:00:00Z",
"expiresAt": null,
"createdAt": "2026-01-30T17:00:00Z"
}
]
POST/teams/{teamId}/api-keys
Create a new API key.
Required Role: Admin or Owner
Request Body:
{
"name": "MCP Integration",
"scopes": ["campaigns:read", "products:read", "accounts:read"],
"expiresAt": "2027-01-30T00:00:00Z" // optional
}
Response:
{
"id": "uuid",
"teamId": "uuid",
"name": "MCP Integration",
"keyPrefix": "acs_LCLf",
"scopes": ["campaigns:read", "products:read", "accounts:read"],
"key": "acs_LCLfhM4D2EMMTN3yPIMrePHYV4aUPMwA", // Only returned once!
"createdAt": "2026-01-30T17:00:00Z"
}
⚠️ Important: The full API key is only returned once upon creation. Store it securely.
DELETE/teams/{teamId}/api-keys/{keyId}
Delete an API key.
Required Role: Admin or Owner
Response:
{
"message": "API key deleted"
}
Error Responses
All endpoints return errors in this format:
{
"code": "error_code",
"message": "Human-readable error message"
}
Common Error Codes:
| HTTP Status | Code | Description |
|---|---|---|
| 400 | bad_request | Invalid request parameters |
| 401 | api_key_required | Missing X-API-Key header |
| 401 | invalid_api_key | API key not found or invalid |
| 401 | expired_api_key | API key has expired |
| 403 | insufficient_scope | API key lacks required scope |
| 403 | forbidden | User lacks permission |
| 404 | not_found | Resource not found |
| 500 | internal | Internal server error |
MCP Integration
FireMetrix provides an official MCP (Model Context Protocol) server that exposes the External API as native tools to AI assistants like Claude. Once configured, you can ask Claude things like "Pause all campaigns with ROAS below 1" or "Add 'gratis' as negative keyword to campaign 12345", and Claude will call the right tools automatically.
Available MCP Tools
| Tool | Type | Required Scope | Description |
|---|---|---|---|
query_campaigns | Read | campaigns:read | Query campaigns with performance metrics |
query_products | Read | products:read | Query products with price benchmarks |
query_accounts | Read | accounts:read | Query accounts with aggregated metrics |
query_product_status_summary | Read | products:read | Product feed health overview |
query_disapproved_products | Read | products:read | Disapproved products list |
query_out_of_stock_products | Read | products:read | Out-of-stock products list |
query_product_status_history | Read | products:read | Product status over time |
get_campaign_details | Read | campaigns:read | Campaign details incl. budget & bidding |
set_campaign_budget | Write | campaigns:write | Set daily budget for a campaign |
set_campaign_status | Write | campaigns:write | Enable or pause a campaign |
set_campaign_bidding | Write | campaigns:write | Change bidding strategy |
add_negative_keywords | Write | keywords:write | Add negative keywords to campaigns |
Hinweis: Write-Tools erfordern API-Keys mit den passenden
*:writeScopes. Alle Schreibvorgänge werden imapi_audit_logmit User, Tool und Payload protokolliert.
Tutorial: Setup für Claude Desktop
1. API-Key erstellen
In FireMetrix unter Einstellungen → API-Keys einen neuen Key erstellen und die benötigten Scopes vergeben (Empfehlung für Vollzugriff: alle *:read plus campaigns:write, keywords:write). Der Key (acs_…) wird nur einmal angezeigt — sofort kopieren.
2. MCP-Server installieren
git clone https://github.com/skalar-marketing/ads-cockpit.git
cd ads-cockpit/mcp
npm install
npm run build
Der gebaute Server liegt danach unter mcp/dist/index.js.
3. Claude Desktop konfigurieren
Die Config-Datei öffnen:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Folgenden Eintrag unter mcpServers ergänzen (Pfad und API-Key anpassen):
{
"mcpServers": {
"firemetrix": {
"command": "node",
"args": ["/absoluter/pfad/zu/ads-cockpit/mcp/dist/index.js"],
"env": {
"ADS_COCKPIT_API_URL": "https://app.firemetrix.io",
"ADS_COCKPIT_API_KEY": "acs_dein_api_key_hier"
}
}
}
}
4. Claude Desktop neu starten
Nach dem Neustart erscheint unten im Eingabefeld ein Werkzeug-Symbol mit den 12 FireMetrix-Tools. Test-Prompt: "Welche Konten habe ich, und wie war der ROAS in den letzten 30 Tagen?"
Tutorial: Setup für Claude Code (CLI)
Claude Code unterstützt MCP-Server über claude mcp add. Nach Installation des MCP-Servers (Schritte 1-2 oben) den Server registrieren:
claude mcp add firemetrix \
--env ADS_COCKPIT_API_URL=https://app.firemetrix.io \
--env ADS_COCKPIT_API_KEY=acs_dein_api_key_hier \
-- node /absoluter/pfad/zu/ads-cockpit/mcp/dist/index.js
Verfügbarkeit prüfen mit claude mcp list. Die Tools tauchen in Claude Code automatisch unter dem Präfix mcp__firemetrix__* auf.
Beispiel-Prompts
Sobald der MCP-Server eingerichtet ist:
- "Zeig mir die Top-5-Kampagnen nach ROAS für Account 1234567890."
- "Welche Produkte haben Status DISAPPROVED?"
- "Pausiere die Kampagne 17539449348 (Account 1234567890)."
- "Setze das Tagesbudget der Kampagne 17539449348 auf 50 EUR."
- "Füge 'gratis' und 'kostenlos' als Negative Keywords zu Kampagne 17539449348 hinzu."
Beispiel-Tool-Definitionen
Read Tool:
{
"name": "query_campaigns",
"description": "Query Google Ads campaigns with performance metrics",
"parameters": {
"days": { "type": "integer", "default": 30 },
"status": { "type": "string", "enum": ["ENABLED", "PAUSED"] },
"min_roas": { "type": "number" },
"limit": { "type": "integer", "default": 10 }
}
}
Write Tool:
{
"name": "set_campaign_budget",
"description": "Set the daily budget for a campaign. Budget must be between 0 and 1,000,000 EUR.",
"parameters": {
"account_id": { "type": "string", "required": true },
"campaign_id": { "type": "string", "required": true },
"daily_budget_eur": { "type": "number", "required": true }
}
}
Rate Limits
Currently no rate limits are enforced. This may change in future versions.
Campaign Details API (JWT)
Endpoints for fetching detailed campaign structure data. Requires JWT authentication.
GET/campaigns/{campaignId}/details
Fetch full campaign details including PMax asset groups, listing groups, and settings.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
accountId | string | Yes | The Google Ads account ID |
Example Request:
curl -H "Authorization: Bearer <jwt_token>" \
"https://app.firemetrix.io/api/v1/campaigns/17539449348/details?accountId=7664254989"
Example Response (PMax Campaign):
{
"settings": {
"id": "17539449348",
"name": "PMax Campaign",
"status": "ENABLED",
"channelType": "PERFORMANCE_MAX",
"biddingStrategy": "MAXIMIZE_CONVERSION_VALUE",
"targetRoas": 4.0,
"dailyBudget": 100.0,
"startDate": "2025-01-01",
"endDate": null
},
"campaignType": "PERFORMANCE_MAX",
"pmax": {
"assetGroups": [
{
"id": "123456",
"name": "Asset Group 1",
"status": "ENABLED",
"adStrength": "GOOD",
"finalUrls": ["https://example.com"],
"assets": [
{
"id": "789",
"type": "TEXT",
"fieldType": "HEADLINE",
"performanceLabel": "BEST",
"text": "Great Product"
}
]
}
],
"listingGroups": [
{
"id": "456",
"assetGroupId": "123456",
"type": "UNIT_INCLUDED",
"caseValue": { "dimension": "BRAND", "value": "MyBrand" }
}
],
"lowPerformanceAssets": []
},
"geoTargets": [
{ "id": "2276", "name": "Germany", "countryCode": "DE", "targetType": "LOCATION" }
]
}
GET/campaigns/{campaignId}/settings
Fetch campaign settings only (lightweight endpoint).
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
accountId | string | Yes | The Google Ads account ID |
Example Response:
{
"id": "17539449348",
"name": "PMax Campaign",
"status": "ENABLED",
"channelType": "PERFORMANCE_MAX",
"biddingStrategy": "MAXIMIZE_CONVERSION_VALUE",
"targetRoas": 4.0,
"dailyBudget": 100.0
}
GET/campaigns/{campaignId}/ai-context
Fetch enriched campaign data optimized for AI analysis.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
accountId | string | Yes | The Google Ads account ID |
Example Response:
{
"campaignType": "PERFORMANCE_MAX",
"settings": { ... },
"performance": {
"cost": 1234.56,
"conversions": 45.5,
"conversionValue": 5500.00,
"roas": 4.45
},
"typeSpecific": {
"assetGroupCount": 3,
"lowPerformanceAssetCount": 2,
"listingGroupCount": 15
},
"issues": [
{
"severity": "warning",
"message": "2 assets with LOW performance rating"
}
]
}
Keyword Mining API (JWT)
AI-powered negative keyword mining with approval workflow.
All endpoints require JWT authentication and are under /api/v1/accounts/{accountID}/mining.
| Method | Path | Description |
|---|---|---|
| POST | /start | Start mining run (calls Gemini, uses 1 credit) |
| GET | /runs | List mining run history |
| GET | /runs/{runID} | Get run with category breakdown |
| GET | /runs/{runID}/suggestions | Get suggestions for a run |
| GET | /suggestions/pending | Get all pending suggestions |
| PUT | /suggestions/{suggestionID} | Approve/reject single suggestion |
| POST | /suggestions/bulk-review | Bulk approve/reject |
| POST | /suggestions/apply | Apply approved suggestions to negative keyword lists (one per category) |
| GET | /search-terms | Enriched search terms (KPIs + relevance + keyword info) |
| POST | /analyze-relevance | AI relevance analysis batch (uses 1 credit per batch) |
| GET | /keywords | Booked keywords with QS + KPIs |
| POST | /keywords/sync | Sync keywords from Google Ads |
| GET | /keywords/{keywordID}/tail | Search terms triggered by a specific keyword |
| POST | /negative-keywords/generate | Generate negative keywords from irrelevant search terms |
POST/accounts/{accountID}/mining/start
Starts an AI mining run analyzing search terms to suggest negative keywords grouped by category.
Request Body (optional):
{
"dateRangeStart": "2026-01-01",
"dateRangeEnd": "2026-01-31"
}
Response: MiningRunWithCategories — run metadata + categories with suggestions.
POST/accounts/{accountID}/mining/suggestions/bulk-review
Request Body:
{
"suggestionIds": ["uuid1", "uuid2"],
"status": "approved"
}
POST/accounts/{accountID}/mining/suggestions/apply
Applies all approved suggestions. Creates one negative keyword list per category.
Response: Array of created NegativeKeywordList objects.
GET/accounts/{accountID}/mining/search-terms
Returns enriched search terms with KPIs, keyword info, and AI relevance scores.
Response:
{
"searchTerms": [{
"searchTerm": "rohrreinigung berlin",
"campaignId": "123", "campaignName": "Brand",
"keywordText": "rohrreinigung", "keywordMatchType": "BROAD",
"impressions": 1200, "clicks": 80, "cost": 45.60,
"conversions": 3.0, "costPerConversion": 15.20,
"relevanceScore": 85, "funnelPosition": "lower",
"relevanceReason": "Direkter Kauf-Intent"
}],
"totalTerms": 450,
"unanalyzedCount": 12,
"analyzedAt": "2026-02-20T..."
}
POST/accounts/{accountID}/mining/analyze-relevance
Analyzes a batch of unanalyzed search terms for relevance using AI. Uses 1 AI credit.
Request Body (optional):
{ "limit": 50 }
POST/accounts/{accountID}/mining/keywords/sync
Syncs booked keywords from Google Ads into the local keywords table.
Response: Array of Keyword objects.
GET/accounts/{accountID}/mining/keywords/{keywordID}/tail
Returns search terms triggered by a specific keyword, with relevance scores.
Public Endpoints
GET/auth/maintenance-status
Check if the system is in maintenance mode. This endpoint is always accessible, even during maintenance.
Example Request:
curl "https://app.firemetrix.io/api/v1/auth/maintenance-status"
Example Response (Maintenance Active):
{
"maintenance": true,
"message": "System-Update läuft. Bitte warten...",
"estimatedEndTime": "2026-02-05T15:30:00Z"
}
Example Response (System Online):
{
"maintenance": false,
"message": "",
"estimatedEndTime": null
}
During maintenance mode, all other API endpoints return HTTP 503:
{
"error": "maintenance",
"message": "System-Update läuft. Bitte warten...",
"maintenance": true,
"estimatedEndTime": "2026-02-05T15:30:00Z"
}
Favorites API (JWT)
Manage per-user account favorites. Requires JWT authentication.
GET/favorites
List all favorite accounts for the current user.
Example Request:
curl -H "Authorization: Bearer <jwt_token>" \
"https://app.firemetrix.io/api/v1/favorites"
Example Response:
[
{
"accountId": "7664254989",
"name": "bodenverkauf"
}
]
POST/favorites/{accountID}
Toggle favorite status for an account. Adds if not favorited, removes if already favorited.
Example Request:
curl -X POST -H "Authorization: Bearer <jwt_token>" \
"https://app.firemetrix.io/api/v1/favorites/7664254989"
Example Response:
{
"isFavorited": true
}
Team Settings API (JWT)
Manage team-level settings for kunde role permissions. Requires JWT authentication with admin or owner role.
GET/teams/{teamId}/settings
Get team settings.
Example Response:
{
"teamId": "uuid",
"kundeCanViewSearchTerms": false,
"kundeCanViewNegativeLists": false,
"kundeCanCreateFeeds": false
}
PUT/teams/{teamId}/settings
Update team settings.
Required Role: Admin or Owner
Request Body:
{
"kundeCanViewSearchTerms": true,
"kundeCanViewNegativeLists": true,
"kundeCanCreateFeeds": false
}
Response: Updated team settings object.
Account Assignments API (JWT)
Manage account assignments for team members. Allows admins to restrict member access to specific accounts. Requires JWT authentication with admin or owner role.
GET/teams/{teamID}/members/{memberID}/assignments
Get the list of accounts assigned to a specific team member.
Required Role: Admin or Owner
Example Request:
curl -H "Authorization: Bearer <jwt_token>" \
"https://app.firemetrix.io/api/v1/teams/{teamID}/members/{memberID}/assignments"
Example Response:
{
"accountIds": ["7664254989", "8427925949"]
}
Notes:
- Returns empty array if member has no assignments (meaning they have access to all accounts)
- Only applies to members with role
memberorkunde - Admins and owners always have access to all accounts regardless of assignments
PUT/teams/{teamID}/members/{memberID}/assignments
Update the account assignments for a team member. Replaces all existing assignments.
Required Role: Admin or Owner
Request Body:
{
"accountIds": ["7664254989", "8427925949"]
}
Response:
{
"message": "Account assignments updated"
}
Notes:
- Pass an empty array
[]to remove all assignments (grants access to all accounts) - Only valid for members with role
memberorkunde - Returns error if trying to assign accounts to admin or owner roles
- Validates that all account IDs exist and belong to the team
Planning API (JWT)
Board, Calendar, and Todo management for account planning. Requires JWT authentication.
GET/planning/status-config
Returns the 5 board status configurations (label and color per status).
Response: BoardStatusConfig[]
[
{ "status": "onboarding", "label": "Onboarding", "color": "blue", "updatedAt": "..." },
{ "status": "setup", "label": "Setup", "color": "purple", "updatedAt": "..." },
...
]
PUT/planning/status-config/{status}
Update label and color for a board status.
Body: { "label": "Neukunden", "color": "green" }
Valid status keys: onboarding, setup, wartend, zu_bearbeiten, rueckfragen
Valid colors: blue, purple, amber, red, orange, green, emerald, teal, cyan, indigo, pink, rose, sky, lime, yellow, slate
GET/planning/board
Returns all board cards with account names. Automatically creates cards for new accounts and moves overdue "wartend" cards to "zu_bearbeiten".
Response: BoardCardWithAccount[]
PUT/planning/board/{accountID}/move
Move a card to a new status column.
Body: { "status": "wartend", "position": 0 }
Status values: onboarding, setup, wartend, zu_bearbeiten, rueckfragen
When moving to "wartend" with cycleDurationDays set, dueDate is auto-calculated.
PUT/planning/board/{accountID}/settings
Update card settings (cycle duration).
Body: { "cycleDurationDays": 14 }
GET/planning/calendar?start=2026-02-01&end=2026-02-28
Returns merged calendar events: manual entries + automatic due dates.
Response: CalendarEvent[] with source: "manual" | "due_date" | "todo", isEcommerce, logoUrl
POST/planning/calendar
Create a manual calendar entry.
Body: { "accountId": "1234567890", "scheduledDate": "2026-02-15", "note": "Kundengespräch" }
PATCH/planning/calendar/{entryID}
Update the scheduled date of a manual calendar entry (used by drag & drop).
Body: { "scheduledDate": "2026-02-20" }
DELETE/planning/calendar/{entryID}
Delete a manual calendar entry.
GET/planning/accounts/{accountID}/todos
List all todos for an account with assignee names.
POST/planning/todos
Create a new todo.
Body: { "accountId": "1234567890", "title": "Budget prüfen", "dueDate": "2026-02-20", "assignedTo": "uuid" }
PUT/planning/todos/{todoID}
Update a todo (title, description, dueDate, assignedTo, isCompleted).
DELETE/planning/todos/{todoID}
Delete a todo.
Promotions API (JWT)
Merchant Center promotions management. Requires JWT authentication.
GET/accounts/{accountID}/promotions
List promotions for an account. By default returns upcoming/active promotions.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
upcoming | string | "true" | Return upcoming/active promotions (default behavior) |
past | string | - | Set to "true" to return expired/past promotions with pagination |
page | integer | 1 | Page number (only with past=true) |
pageSize | integer | 10 | Results per page, max 100 (only with past=true) |
startDate | string | - | Filter by date range start (YYYY-MM-DD) |
endDate | string | - | Filter by date range end (YYYY-MM-DD) |
Response (default / upcoming): Plain array of MerchantPromotion objects.
Response (past=true): Paginated response:
{
"data": [
{
"id": "uuid",
"accountId": "7664254989",
"promotionId": "PROMO_123",
"title": "Summer Sale",
"couponValueType": "PERCENT_OFF",
"percentOff": 20,
"effectiveStart": "2026-06-01T00:00:00Z",
"effectiveEnd": "2026-06-30T23:59:59Z",
"overallStatus": "EXPIRED",
"customTitle": null,
"customNotes": null
}
],
"total": 42,
"page": 1,
"pageSize": 10
}
POST/accounts/{accountID}/promotions
Create a new promotion in Google Merchant Center. Requires the E-Commerce plan feature.
Request Body:
{
"promotionId": "OSTERN2026",
"longTitle": "Ostern 2026 - 20% Rabatt",
"couponValueType": "PERCENT_OFF",
"offerType": "GENERIC_CODE",
"redemptionChannel": ["ONLINE"],
"productApplicability": "SPECIFIC_PRODUCTS",
"percentOff": 20,
"genericRedemptionCode": "HASE20",
"effectiveStart": "2026-04-01T00:00:00Z",
"effectiveEnd": "2026-04-15T23:59:59Z",
"targetCountry": "DE",
"contentLanguage": "de"
}
| Field | Type | Required | Description |
|---|---|---|---|
promotionId | string | No | Custom promotion ID (auto-generated if empty) |
longTitle | string | Yes | Promotion title |
couponValueType | string | Yes | PERCENT_OFF, MONEY_OFF, or FREE_GIFT |
offerType | string | Yes | NO_CODE or GENERIC_CODE |
redemptionChannel | string[] | Yes | ONLINE and/or IN_STORE |
productApplicability | string | Yes | ALL_PRODUCTS or SPECIFIC_PRODUCTS |
percentOff | integer | Conditional | Required if couponValueType is PERCENT_OFF (1-100) |
moneyOffAmount | number | Conditional | Required if couponValueType is MONEY_OFF |
moneyOffCurrency | string | No | Default: EUR |
freeGiftDescription | string | Conditional | Required if couponValueType is FREE_GIFT |
genericRedemptionCode | string | Conditional | Required if offerType is GENERIC_CODE |
effectiveStart | string | Yes | RFC3339 timestamp |
effectiveEnd | string | Yes | RFC3339 timestamp (must be after start) |
targetCountry | string | No | Default: DE |
contentLanguage | string | No | Default: de |
Response: 201 Created with the created MerchantPromotion object.
PATCH/accounts/{accountID}/promotions/{promotionID}
Update custom fields for a promotion. All fields are optional. Send empty string "" to clear a field.
Request Body:
{
"customTitle": "My Custom Title",
"customEffectiveStart": "2026-03-01T00:00:00Z",
"customEffectiveEnd": "2026-03-31T23:59:59Z",
"customNotes": "Internal note about this promotion"
}
customTitle: Override the synced title (send""to clear)customEffectiveStart: Override the effective start date (RFC 3339 format, send""to clear)customEffectiveEnd: Override the effective end date (RFC 3339 format, send""to clear)customNotes: Free-text notes (send""to clear)
Response: Updated MerchantPromotion object.
Landing Pages API (JWT)
Landing page URLs extracted from Google Ads campaigns, top-performing products (E-Commerce), or manually added. Used for AI website analysis and keyword mining relevance on all account types.
List Landing Pages
GET /api/v1/accounts/{accountID}/landing-pages
Response: Array of AccountLandingPage objects:
[
{
"id": "uuid",
"accountId": "1234567890",
"url": "https://example.com/service",
"campaignIds": ["123", "456"],
"campaignNames": ["Brand Campaign", "Service Campaign"],
"isManuallyAdded": false,
"source": "campaign",
"createdAt": "2025-01-01T00:00:00Z",
"updatedAt": "2025-01-01T00:00:00Z"
}
]
source values: campaign (from Google Ads), manual (manually added), product (from top-performing products).
Refresh Landing Pages from Google Ads
POST /api/v1/accounts/{accountID}/landing-pages/refresh
Fetches all ad final URLs from Google Ads (Search + PMax campaigns), deduplicates, and stores them. Only removes stale campaign-sourced pages; manual and product pages are preserved.
Response: Array of all AccountLandingPage objects after refresh.
Refresh Product Landing Pages (E-Commerce)
POST /api/v1/accounts/{accountID}/landing-pages/refresh-products
Fetches product detail URLs from top-performing products (by ROAS, last 30 days) and stores them as landing pages with source=product. Uses the account's productRoasBoundary threshold (default: 2.0). Returns up to 10 product URLs.
Response: Array of all AccountLandingPage objects after refresh.
Add Landing Page (Manual)
POST /api/v1/accounts/{accountID}/landing-pages
Request Body:
{ "url": "https://example.com/page" }
Response (201): Created AccountLandingPage object with source=manual.
Update Landing Page
PUT /api/v1/accounts/{accountID}/landing-pages/{landingPageID}
Request Body:
{ "url": "https://example.com/updated-page" }
Response: Updated AccountLandingPage object.
Delete Landing Page
DELETE /api/v1/accounts/{accountID}/landing-pages/{landingPageID}
Response:
{ "deleted": true }
Billing API (JWT)
AI Chat
Streaming AI chat with account context. Requires JWT authentication.
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /chat/stream | JWT | Stream AI chat response via SSE |
POST/chat/stream
Streams a Gemini-powered chat response as Server-Sent Events. Deducts 1 AI credit per message.
Request Body:
{
"message": "Wie performen meine Kampagnen?",
"history": [
{ "role": "user", "content": "Hallo" },
{ "role": "model", "content": "Hallo! Wie kann ich helfen?" }
],
"accountId": "8427925949"
}
Response: SSE stream with data: {"text":"..."} chunks, terminated by data: [DONE].
Errors: 402 (credits exhausted), 503 (Gemini not configured).
Stripe billing management. Requires JWT authentication.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /public/pricing | None | Get plan prices, limits, and features (public, cached 5min) |
| GET | /billing/pricing | JWT | Get plan prices, limits, and features from plan_configs |
| GET | /billing/status | JWT | Get billing status (plan, subscription, credits) |
| GET | /billing/invoices | JWT | List paid invoices with PDF download links |
| POST | /billing/checkout | JWT (Owner) | Create Stripe Checkout session |
| POST | /billing/portal | JWT (Owner) | Create Stripe Customer Portal session |
| PUT | /billing/select-plan | JWT (Owner) | Select plan during onboarding trial (starter/pro/agency) |
| PUT | /billing/addon | JWT (Owner) | Add/change E-Commerce addon tier |
| DELETE | /billing/addon | JWT (Owner) | Remove E-Commerce addon |
| POST | /billing/cancel | JWT (Owner) | Cancel subscription at period end |
| POST | /billing/credit-pack-checkout | JWT (Owner) | Create one-time Stripe Checkout for AI credit pack |
| POST | /billing/webhook | None | Stripe webhook endpoint |
POST/billing/cancel
Cancel the active Stripe subscription at period end. Stores cancellation feedback for churn analytics. Clears on resubscribe.
Request:
{ "reason": "Sonstiges", "reasonDetail": "optional free text" }
Response:
{ "status": "ok" }
PUT/billing/select-plan
Select a plan during trial onboarding. Updates tenant plan, max accounts, and AI credit limits. No Stripe payment required during trial.
Request:
{ "plan": "starter" | "pro" | "agency" }
Response: Same as GET /billing/status (updated BillingStatusResponse).
Errors:
403 FORBIDDEN— Only tenant owner can select a plan400 BAD_REQUEST— Invalid plan name
GET/billing/invoices
List all paid Stripe invoices for the tenant. Any team member can access this endpoint.
Example Response:
[
{
"id": "in_1234567890",
"number": "INV-0001",
"date": 1707350400,
"amountPaid": 4900,
"currency": "eur",
"status": "paid",
"pdfUrl": "https://pay.stripe.com/invoice/...",
"hostedUrl": "https://invoice.stripe.com/i/..."
}
]
Notes:
dateis a Unix timestampamountPaidis in the smallest currency unit (cents)pdfUrllinks to the Stripe-generated PDF invoice- Returns empty array if tenant has no Stripe customer
KPI Presets API (JWT)
Manage reusable KPI selection presets that can be linked to multiple accounts. Requires JWT authentication.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /kpi-presets | JWT | List all presets visible to user (own + shared) |
| POST | /kpi-presets | JWT | Create a new KPI preset |
| PUT | /kpi-presets/{presetID} | JWT | Update a preset (owner only) |
| DELETE | /kpi-presets/{presetID} | JWT | Delete a preset (owner only) |
GET/kpi-presets
Returns all KPI presets the user can see: their own presets plus team-shared presets.
Example Response:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"userId": "...",
"name": "E-Commerce Standard",
"kpiOrder": ["cost", "conversions", "conversionValue", "roas"],
"isShared": true,
"createdAt": "2026-02-20T10:00:00Z",
"updatedAt": "2026-02-20T10:00:00Z"
}
]
POST/kpi-presets
Create a new KPI preset.
Request Body:
{
"name": "E-Commerce Standard",
"kpiOrder": ["cost", "conversions", "conversionValue", "roas"]
}
Validation:
namemust not be emptykpiOrdermust contain 1–16 valid KPI keys, no duplicates
Response: Created preset object (201).
PUT/kpi-presets/{presetID}
Update name, KPI order, or sharing status. Only the preset owner can update.
Request Body:
{
"name": "Updated Name",
"kpiOrder": ["cost", "roas", "clicks"],
"isShared": true
}
Response: Updated preset object.
DELETE/kpi-presets/{presetID}
Delete a preset. Only the preset owner can delete. All linked accounts are automatically unlinked (preset_id set to NULL).
Response: 204 No Content.
KPI Config API (JWT)
Per-account KPI configuration with optional preset linking.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /accounts/{accountID}/kpi-config | JWT | Get KPI config (resolved with preset if linked) |
| PUT | /accounts/{accountID}/kpi-config | JWT | Update KPI config / link preset |
GET/accounts/{accountID}/kpi-config
Returns the user's KPI configuration for the given account. If linked to a preset, returns the preset's KPI order and name.
Example Response:
{
"kpiOrder": ["cost", "conversions", "conversionValue", "roas"],
"presetId": "550e8400-e29b-41d4-a716-446655440000",
"presetName": "E-Commerce Standard"
}
If no preset is linked, presetId and presetName are omitted.
PUT/accounts/{accountID}/kpi-config
Update the KPI order and/or link a preset.
Request Body:
{
"kpiOrder": ["cost", "conversions", "roas"],
"presetId": "550e8400-e29b-41d4-a716-446655440000"
}
When presetId is provided, the preset's KPI order is used instead of the kpiOrder field.
Response: 204 No Content.
Product View Presets API (JWT)
Manage reusable product view configuration presets. Requires JWT authentication.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /product-view-presets | JWT | List all presets visible to user (own + shared) |
| POST | /product-view-presets | JWT | Create a new product view preset |
| PUT | /product-view-presets/{presetID} | JWT | Update a preset (owner only) |
| DELETE | /product-view-presets/{presetID} | JWT | Delete a preset (owner only) |
GET/product-view-presets
Returns all product view presets the user can see.
Example Response:
[
{
"id": "660e8400-e29b-41d4-a716-446655440000",
"userId": "...",
"name": "Detailansicht",
"config": {
"columns": ["title", "price", "status", "clicks", "impressions"],
"chartMetrics": ["clicks", "impressions"],
"sortKey": "title",
"sortOrder": "asc",
"viewMode": "table"
},
"isShared": false,
"createdAt": "2026-02-20T10:00:00Z",
"updatedAt": "2026-02-20T10:00:00Z"
}
]
POST/product-view-presets
Request Body:
{
"name": "Detailansicht",
"config": {
"columns": ["title", "price", "status"],
"chartMetrics": ["clicks"],
"sortKey": "title",
"sortOrder": "asc",
"viewMode": "table"
}
}
Validation: Config must contain valid column names, sort keys, and view mode (table, chart, or pricefinder).
Response: Created preset object (201).
PUT/product-view-presets/{presetID}
Request Body:
{
"name": "Updated Name",
"config": { ... },
"isShared": true
}
Response: Updated preset object.
DELETE/product-view-presets/{presetID}
Response: 204 No Content.
Product Config API (JWT)
Per-account product view configuration with optional preset linking.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /accounts/{accountID}/product-config | JWT | Get product view config (resolved with preset if linked) |
| PUT | /accounts/{accountID}/product-config | JWT | Update product view config / link preset |
GET/accounts/{accountID}/product-config
Returns the user's product view configuration for the given account. If linked to a preset, returns the preset's config and name.
Example Response:
{
"presetId": "660e8400-e29b-41d4-a716-446655440000",
"presetName": "Detailansicht",
"config": {
"columns": ["title", "price", "status"],
"chartMetrics": ["clicks"],
"sortKey": "title",
"sortOrder": "asc",
"viewMode": "table"
}
}
PUT/accounts/{accountID}/product-config
Request Body:
{
"presetId": "660e8400-e29b-41d4-a716-446655440000",
"config": { ... }
}
Response: 200 with updated config.
Support API (JWT)
Endpoints for the support ticket system. Requires JWT authentication.
Base path: /api/v1/support
FAQ
| Method | Path | Description |
|---|---|---|
| GET | /support/categories | Liste aller aktiven FAQ-Kategorien |
| GET | /support/faqs/{categoryKey} | FAQs einer Kategorie |
GET /support/categories — Response:
[{"key": "billing", "label_de": "Abrechnung", "sort_order": 1}]
GET /support/faqs/{categoryKey} — Response:
[{"id": 1, "question_de": "Wie kündige ich?", "answer_de": "...", "sort_order": 1}]
Tickets
| Method | Path | Description |
|---|---|---|
| POST | /support/tickets | Neues Ticket erstellen |
| GET | /support/tickets | Eigene Tickets auflisten |
| GET | /support/tickets/{id} | Ticket-Detail mit Replies und Anhängen |
| POST | /support/tickets/{id}/replies | User-Antwort hinzufügen |
| POST | /support/tickets/{id}/attachments | Dateianhang hochladen (multipart) |
| GET | /support/attachments/{id} | Dateianhang herunterladen (supports ?token= query param) |
POST /support/tickets — Body:
{"categoryKey": "billing", "subject": "Betreff", "message": "Nachricht"}
POST /support/tickets/{id}/replies — Body:
{"message": "Antworttext"}
Ticket-Status-Werte: offen | in_bearbeitung | geschlossen
Changelog
v1.16.0 (2026-04-26)
- Campaign Write API + MCP Write Tools: Mutate Google Ads campaigns directly from the API/MCP
- New scopes:
campaigns:write,keywords:write - New endpoints:
POST /campaigns/{campaignID}/budget— set daily budget (EUR)POST /campaigns/{campaignID}/status— enable/pause campaignPOST /campaigns/{campaignID}/bidding— change bidding strategy (target ROAS, target CPA, max conversions/value)POST /query/negative-keywords— add shared negative keyword list to campaignsGET /campaigns/{campaignID}/details— inspect campaign state (incl. resource names) before mutating
- New MCP tools:
set_campaign_budget,set_campaign_status,set_campaign_bidding,add_negative_keywords,get_campaign_details - New tenant table:
api_audit_log— every write call logs user, tool, payload, and result for traceability - Added Claude Desktop and Claude Code setup tutorial in the MCP Integration section
v1.15.0 (2026-03-15)
- Added Support Ticket System API
- New endpoints under
/support/*: FAQ categories, FAQs, ticket CRUD, replies, attachments - New public schema tables:
support_faq_categories,support_faqs,support_tickets,support_ticket_replies,support_ticket_attachments - Migration:
053_support_tickets.sql
v1.14.0 (2026-02-20)
- Preset Management System: KPI presets and product view presets
- New CRUD endpoints:
/kpi-presets,/product-view-presets - New per-account config endpoints:
/accounts/{id}/kpi-config(enhanced),/accounts/{id}/product-config - New tables:
kpi_presets,product_view_presets,user_account_product_configs - New column:
user_account_kpi_configs.preset_id - Presets can be shared with the team (
isSharedflag) - Accounts can be linked to presets; manual changes unlink automatically
- Fixed widget display mode (matrix/graph) and legend toggle not responding (optimistic UI update)
v1.13.0 (2026-02-17)
- Replaced per-tenant slot-based scheduler with Global Sync Orchestrator
- Sequential phased sync: Account Discovery → Campaigns (14d) → Products (30d) → Search Terms (3d) → Change History (7d)
- Round-robin interleaving of accounts across tenants for fair distribution
- New table:
public.global_sync_cyclesfor cycle tracking with per-tenant stats - Extended
sync_run_logs.trigger_sourceto include'orchestrator' - Admin API: New endpoints for orchestrator control (
/sync/status,/sync/history,/sync/trigger,/sync/cancel,/sync/schedule) - Admin API: Removed
/sync-schedulesand/tenants/{id}/trigger-sync - Admin Frontend: Rewritten SyncDashboard with orchestrator control panel, cycle history, and auto-refresh
v1.12.0 (2026-02-17)
- Plan features are now configurable via Admin Panel (previously hardcoded in Go)
- New tables:
public.plan_configs(plan defaults),public.tenant_feature_overrides(per-tenant overrides) - Feature resolution: plan default → tenant override → merged
PlanFeatures - No API changes —
GET /billing/statusreturns the samefeaturesshape, now resolved from DB
v1.13.0 (2026-03-05)
- Meta Ads Integration (Phase 1+2): Full Meta Ads platform support
- New endpoints:
POST /teams/{teamID}/connect/meta/callback— Meta OAuth callback - New endpoints:
GET /teams/{teamID}/connect/meta/status— Meta connection status - New endpoints:
DELETE /teams/{teamID}/connect/meta— Disconnect Meta - New endpoints:
GET /teams/{teamID}/connect/meta/ad-accounts— List Meta Ad Accounts - New columns:
accounts.platform(google_ads/meta_ads),campaigns.objective(Meta objectives) - Meta campaign sync:
effective_status, daily insights withtime_increment=1 - Meta campaigns use statuses: ACTIVE/PAUSED/DELETED/ARCHIVED (vs Google: ENABLED/PAUSED/REMOVED)
- Sync orchestrator extended with Meta phases (account_discovery + campaign sync)
- Frontend: Meta accounts in sidebar with Meta logo, platform-aware campaign display
- Known limitation: Meta API conversion counts may differ from Ads Manager due to event deduplication
v1.11.0 (2026-02-16)
- Added custom fields to
merchant_promotions:custom_title,custom_effective_start,custom_effective_end,custom_notes GET /accounts/{accountID}/promotionsnow supports?past=true&page=1&pageSize=10for paginated past promotions- New endpoint:
PATCH /accounts/{accountID}/promotions/{promotionID}for updating custom fields
v1.10.0 (2026-02-14)
- Added
mcc_idfilter toGET /query/accounts— filter sub-accounts by MCC - Added
searchfilter toGET /query/accounts— search accounts by name (case-insensitive) - New endpoint:
GET /query/team/members— list team members with UUIDs (scope:accounts:read)
v1.9.0 (2026-02-14)
- Added write scopes to External API:
customers:write,planning:write,logs:write - 11 new write endpoints for customer profiles, memories, activity logs, board cards, todos, and account assignees
- Optional
userIdfield in write requests for attribution tracking (defaults to "via API-Key") - New repo method
SetAccountAssigneesfor account-centric assignment management
v1.9.0 (2026-02-20)
- Added Product Daily Stats tracking with alerts
- New endpoint:
GET /accounts/{id}/products/status-stats?days=30— daily product stats + alerts - New table:
product_daily_stats(tenant schema) - New column:
account_metadata.product_alert_threshold - Fixed OAuth token pool-rotation bug with transaction-based search_path pinning
- Dropped 27 legacy tenant tables from public schema (migration 040)
- Fixed admin SyncDashboard auto-refresh on sync completion
v1.8.0 (2026-02-14)
- Extended External Query API to full read access (~25 new endpoints)
- New scopes:
search_terms:read,customers:read,planning:read,logs:read - Added search_path middleware for API key routes (proper tenant isolation)
- New endpoints: account details/daily metrics/campaigns, campaign search terms/daily metrics, product details/daily metrics/feeds, search terms/mining/negative keywords, customer profiles/memories, planning board/calendar/todos, activity logs
v1.7.0 (2026-02-13)
- Added Planning feature API: Board, Calendar, Todos
- New endpoints under
/planning/*
v1.6.0 (2026-02-08)
- Added Stripe Billing API with checkout, portal, addons, and invoices
- New endpoints:
/billing/status,/billing/invoices,/billing/checkout,/billing/portal,/billing/addon,/billing/webhook - New tables:
public.stripe_webhook_events,public.subscription_addons - Extended
public.tenantswith subscription fields
v1.5.0 (2026-02-07)
- Added per-team MCC visibility: each team can independently control which MCCs are visible and synced
- New endpoint:
GET /teams/{teamID}/mcc-visibility— returns visibility map for a team PUT /teams/{teamID}/mcc-sync-enablednow writes toteam_mcc_visibilityjunction table and recomputesaccounts.sync_enabled- Sync service now respects
sync_enabledwhen fetching MCCs from Google Ads API - New table:
team_mcc_visibility
v1.4.0 (2026-02-07)
- Added Account Assignments API for restricting member access to specific accounts
- New endpoints:
GET /teams/{teamID}/members/{memberID}/assignments,PUT /teams/{teamID}/members/{memberID}/assignments - New table:
user_account_assignments
v1.3.0 (2026-02-07)
- Added Favorites API for per-user account favorites
- Added Team Settings API for kunde role opt-in permissions
- RBAC: Renamed
viewerrole tokundewith configurable permissions - New tables:
user_account_favorites,team_settings
v1.2.0 (2026-02-05)
- Added maintenance mode with user notification
- New endpoint:
/auth/maintenance-status - All endpoints return 503 during maintenance (except health and maintenance-status)
v1.1.0 (2026-02-04)
- Added Campaign Details API endpoints for PMax deep-dive
- New endpoints:
/campaigns/{id}/details,/campaigns/{id}/settings,/campaigns/{id}/ai-context - PMax support: asset groups, assets with performance labels, listing groups
v1.0.0 (2026-01-30)
- Initial release of External Query API
- API key authentication with scopes
- Query endpoints for campaigns, products, accounts