Zurück zur Startseite

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:

ScopeDescription
campaigns:readQuery campaign data, search terms, daily metrics
products:readQuery product data, details, daily metrics, feeds
accounts:readQuery account data, details, daily metrics, campaigns
search_terms:readAccount search terms, mining runs/suggestions, negative keywords
customers:readCustomer profiles and memories
customers:writeUpdate customer profiles, create/delete memories
planning:readBoard, calendar, todos
planning:writeMove board cards, update card settings, CRUD todos, set assignees
campaigns:writeSet campaign budget, status, bidding strategy
keywords:writeAdd negative keywords to campaigns
logs:readActivity logs (account + team)
logs:writeCreate 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:

ParameterTypeDefaultDescription
account_idstring-Filter by account ID
statusstring-Filter by status: ENABLED, PAUSED, REMOVED
channel_typestring-Filter by channel: SEARCH, SHOPPING, DISPLAY, PERFORMANCE_MAX
daysinteger30Performance data for last N days (max: 365)
min_costfloat-Minimum cost in EUR
max_costfloat-Maximum cost in EUR
min_roasfloat-Minimum ROAS
max_roasfloat-Maximum ROAS
sortstring-costSort field: cost, roas, conversions, clicks, impressions, name. Prefix with - for descending
limitinteger100Results per page (max: 1000)
offsetinteger0Pagination 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:

ParameterTypeDefaultDescription
account_idstring-Filter by account ID
daysinteger30Performance data for last N days (max: 365)
min_roasfloat-Minimum ROAS
max_roasfloat-Maximum ROAS
price_vs_benchmarkstring-Price comparison: above, below, equal
min_price_diff_percentfloat-Minimum price difference % vs benchmark
availabilitystring-Filter: in_stock, out_of_stock
brandstring-Filter by brand name
has_impressionsboolean-Only products with impressions (true)
sortstring-costSort field: cost, roas, price_diff, price
limitinteger100Results per page (max: 1000)
offsetinteger0Pagination 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:

ParameterTypeDefaultDescription
mcc_idstring-Filter sub-accounts by MCC account ID
searchstring-Search accounts by name (case-insensitive, partial match)
daysinteger30Performance data for last N days (max: 365)
min_roasfloat-Minimum ROAS
sortstring-costSort field: cost, roas, conversions, name
limitinteger100Results per page (max: 1000)
offsetinteger0Pagination 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:

ParameterTypeRequiredDescription
start_datestringYesStart date (YYYY-MM-DD)
end_datestringYesEnd date (YYYY-MM-DD)

GET/query/accounts/{accountID}/campaigns

List campaigns for an account. Optionally include metrics summary.

Required Scope: accounts:read

Query Parameters:

ParameterTypeRequiredDescription
start_datestringNoStart date for metrics summary
end_datestringNoEnd date for metrics summary

GET/query/campaigns/{campaignID}/search-terms

Get search term insights for a campaign.

Required Scope: campaigns:read

Query Parameters:

ParameterTypeDefaultDescription
daysinteger30Analysis period (max: 90)

GET/query/campaigns/{campaignID}/metrics/daily

Get daily metrics for a campaign.

Required Scope: campaigns:read

Query Parameters:

ParameterTypeRequiredDescription
start_datestringNoStart date (YYYY-MM-DD)
end_datestringNoEnd date (YYYY-MM-DD)
daysinteger30Fallback 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:

ParameterTypeRequiredDescription
account_idstringYesGoogle 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:

ParameterTypeRequiredDescription
account_idstringYesThe Google Ads account ID

GET/query/products/{offerID}/metrics/daily

Get daily metrics for a product.

Required Scope: products:read

Query Parameters:

ParameterTypeRequiredDescription
account_idstringYesThe Google Ads account ID
start_datestringYesStart date (YYYY-MM-DD)
end_datestringYesEnd 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:

ParameterTypeDefaultDescription
daysinteger30Analysis 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:

ParameterTypeRequiredDescription
categorystringNoFilter 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:

ParameterTypeRequiredDescription
startstringYesStart date (YYYY-MM-DD)
endstringYesEnd 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:

ParameterTypeDefaultDescription
limitinteger0Results per page
offsetinteger0Pagination offset
log_typestring-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:

ParameterTypeDefaultDescription
limitinteger0Results per page
offsetinteger0Pagination offset
log_typestring-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 be ENABLED or PAUSED

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 be TARGET_ROAS, MAXIMIZE_CONVERSIONS, MAXIMIZE_CLICKS, or MANUAL_CPC
  • target_roas: Required when strategy is TARGET_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 required
  • keywords: At least one keyword required, maximum 5000 per request
  • match_type: Must be BROAD, PHRASE, or EXACT

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, or sonstige
  • entryDate: Optional, defaults to now
  • userId: 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, error
  • metadata: Optional JSON object
  • userId: Optional
  • eventDate: Optional date (YYYY-MM-DD) when the event occurred. Used for chart positioning instead of createdAt.

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, rueckfragen
  • position: 0-based position within the status column
  • userId: 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"
}
  • accountId and title are required
  • description: Optional text description
  • dueDate: Optional due date (YYYY-MM-DD)
  • assignedTo: Optional user UUID for assignment
  • userId: Optional user UUID for tracking who created the todo (if omitted, createdBy will 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 to null to clear)
  • assignedTo: User UUID (set to null to 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 StatusCodeDescription
400bad_requestInvalid request parameters
401api_key_requiredMissing X-API-Key header
401invalid_api_keyAPI key not found or invalid
401expired_api_keyAPI key has expired
403insufficient_scopeAPI key lacks required scope
403forbiddenUser lacks permission
404not_foundResource not found
500internalInternal 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

ToolTypeRequired ScopeDescription
query_campaignsReadcampaigns:readQuery campaigns with performance metrics
query_productsReadproducts:readQuery products with price benchmarks
query_accountsReadaccounts:readQuery accounts with aggregated metrics
query_product_status_summaryReadproducts:readProduct feed health overview
query_disapproved_productsReadproducts:readDisapproved products list
query_out_of_stock_productsReadproducts:readOut-of-stock products list
query_product_status_historyReadproducts:readProduct status over time
get_campaign_detailsReadcampaigns:readCampaign details incl. budget & bidding
set_campaign_budgetWritecampaigns:writeSet daily budget for a campaign
set_campaign_statusWritecampaigns:writeEnable or pause a campaign
set_campaign_biddingWritecampaigns:writeChange bidding strategy
add_negative_keywordsWritekeywords:writeAdd negative keywords to campaigns

Hinweis: Write-Tools erfordern API-Keys mit den passenden *:write Scopes. Alle Schreibvorgänge werden im api_audit_log mit 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:

ParameterTypeRequiredDescription
accountIdstringYesThe 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:

ParameterTypeRequiredDescription
accountIdstringYesThe 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:

ParameterTypeRequiredDescription
accountIdstringYesThe 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.

MethodPathDescription
POST/startStart mining run (calls Gemini, uses 1 credit)
GET/runsList mining run history
GET/runs/{runID}Get run with category breakdown
GET/runs/{runID}/suggestionsGet suggestions for a run
GET/suggestions/pendingGet all pending suggestions
PUT/suggestions/{suggestionID}Approve/reject single suggestion
POST/suggestions/bulk-reviewBulk approve/reject
POST/suggestions/applyApply approved suggestions to negative keyword lists (one per category)
GET/search-termsEnriched search terms (KPIs + relevance + keyword info)
POST/analyze-relevanceAI relevance analysis batch (uses 1 credit per batch)
GET/keywordsBooked keywords with QS + KPIs
POST/keywords/syncSync keywords from Google Ads
GET/keywords/{keywordID}/tailSearch terms triggered by a specific keyword
POST/negative-keywords/generateGenerate 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 member or kunde
  • 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 member or kunde
  • 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:

ParameterTypeDefaultDescription
upcomingstring"true"Return upcoming/active promotions (default behavior)
paststring-Set to "true" to return expired/past promotions with pagination
pageinteger1Page number (only with past=true)
pageSizeinteger10Results per page, max 100 (only with past=true)
startDatestring-Filter by date range start (YYYY-MM-DD)
endDatestring-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"
}
FieldTypeRequiredDescription
promotionIdstringNoCustom promotion ID (auto-generated if empty)
longTitlestringYesPromotion title
couponValueTypestringYesPERCENT_OFF, MONEY_OFF, or FREE_GIFT
offerTypestringYesNO_CODE or GENERIC_CODE
redemptionChannelstring[]YesONLINE and/or IN_STORE
productApplicabilitystringYesALL_PRODUCTS or SPECIFIC_PRODUCTS
percentOffintegerConditionalRequired if couponValueType is PERCENT_OFF (1-100)
moneyOffAmountnumberConditionalRequired if couponValueType is MONEY_OFF
moneyOffCurrencystringNoDefault: EUR
freeGiftDescriptionstringConditionalRequired if couponValueType is FREE_GIFT
genericRedemptionCodestringConditionalRequired if offerType is GENERIC_CODE
effectiveStartstringYesRFC3339 timestamp
effectiveEndstringYesRFC3339 timestamp (must be after start)
targetCountrystringNoDefault: DE
contentLanguagestringNoDefault: 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.

MethodPathAuthDescription
POST/chat/streamJWTStream 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.

MethodPathAuthDescription
GET/public/pricingNoneGet plan prices, limits, and features (public, cached 5min)
GET/billing/pricingJWTGet plan prices, limits, and features from plan_configs
GET/billing/statusJWTGet billing status (plan, subscription, credits)
GET/billing/invoicesJWTList paid invoices with PDF download links
POST/billing/checkoutJWT (Owner)Create Stripe Checkout session
POST/billing/portalJWT (Owner)Create Stripe Customer Portal session
PUT/billing/select-planJWT (Owner)Select plan during onboarding trial (starter/pro/agency)
PUT/billing/addonJWT (Owner)Add/change E-Commerce addon tier
DELETE/billing/addonJWT (Owner)Remove E-Commerce addon
POST/billing/cancelJWT (Owner)Cancel subscription at period end
POST/billing/credit-pack-checkoutJWT (Owner)Create one-time Stripe Checkout for AI credit pack
POST/billing/webhookNoneStripe 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 plan
  • 400 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:

  • date is a Unix timestamp
  • amountPaid is in the smallest currency unit (cents)
  • pdfUrl links 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.

MethodPathAuthDescription
GET/kpi-presetsJWTList all presets visible to user (own + shared)
POST/kpi-presetsJWTCreate a new KPI preset
PUT/kpi-presets/{presetID}JWTUpdate a preset (owner only)
DELETE/kpi-presets/{presetID}JWTDelete 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:

  • name must not be empty
  • kpiOrder must 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.

MethodPathAuthDescription
GET/accounts/{accountID}/kpi-configJWTGet KPI config (resolved with preset if linked)
PUT/accounts/{accountID}/kpi-configJWTUpdate 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.

MethodPathAuthDescription
GET/product-view-presetsJWTList all presets visible to user (own + shared)
POST/product-view-presetsJWTCreate a new product view preset
PUT/product-view-presets/{presetID}JWTUpdate a preset (owner only)
DELETE/product-view-presets/{presetID}JWTDelete 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.

MethodPathAuthDescription
GET/accounts/{accountID}/product-configJWTGet product view config (resolved with preset if linked)
PUT/accounts/{accountID}/product-configJWTUpdate 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

MethodPathDescription
GET/support/categoriesListe 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

MethodPathDescription
POST/support/ticketsNeues Ticket erstellen
GET/support/ticketsEigene Tickets auflisten
GET/support/tickets/{id}Ticket-Detail mit Replies und Anhängen
POST/support/tickets/{id}/repliesUser-Antwort hinzufügen
POST/support/tickets/{id}/attachmentsDateianhang 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 campaign
    • POST /campaigns/{campaignID}/bidding — change bidding strategy (target ROAS, target CPA, max conversions/value)
    • POST /query/negative-keywords — add shared negative keyword list to campaigns
    • GET /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 (isShared flag)
  • 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_cycles for cycle tracking with per-tenant stats
  • Extended sync_run_logs.trigger_source to include 'orchestrator'
  • Admin API: New endpoints for orchestrator control (/sync/status, /sync/history, /sync/trigger, /sync/cancel, /sync/schedule)
  • Admin API: Removed /sync-schedules and /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/status returns the same features shape, 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 with time_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}/promotions now supports ?past=true&page=1&pageSize=10 for paginated past promotions
  • New endpoint: PATCH /accounts/{accountID}/promotions/{promotionID} for updating custom fields

v1.10.0 (2026-02-14)

  • Added mcc_id filter to GET /query/accounts — filter sub-accounts by MCC
  • Added search filter to GET /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 userId field in write requests for attribution tracking (defaults to "via API-Key")
  • New repo method SetAccountAssignees for 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.tenants with 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-enabled now writes to team_mcc_visibility junction table and recomputes accounts.sync_enabled
  • Sync service now respects sync_enabled when 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 viewer role to kunde with 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