Tracking endpoints live under /api/tracking/* and use session authentication — they're the same endpoints the dashboard calls.
Trigger a tracking run
POST /api/tracking/check
Content-Type: application/json
{
"brandId": "uuid",
"platforms": ["chatgpt", "gemini", "perplexity"]
}
Enqueues a tracking job on the queue. Returns:
{
"success": true,
"jobId": "job-uuid"
}
Quota errors return 402 (no active subscription, cloud only) or 429 (run cap / cooldown) with an explanatory body.
Poll job status
GET /api/tracking/status?jobId={jobId}
GET /api/tracking/job/{jobId}
Returns:
{
"status": "active",
"progress": { "phase": "querying-platforms", "completed": 12, "total": 30 },
"result": null,
"failedReason": null
}
status values: waiting, active, completed, failed, cancelled.
Cancel a running job with DELETE /api/tracking/job/{jobId} — the worker checks for cancellation cooperatively between platform queries.
Results
GET /api/tracking/results?brandId=<uuid>&platform=chatgpt
Returns the most recent stored responses per (prompt, platform) with parsed mentions, citations, sentiment, and visibility scores. The dashboard's analytics pages read pre-aggregated SQL functions on top of the same data.
Realtime completion events
When Laravel Reverb is configured, a tracking.complete event broadcasts on the private brands.{id} channel as each job finishes — the dashboard uses this to refresh without polling. Without Reverb, poll the status endpoint (the dashboard polls every 3 seconds).
Scheduled tracking
The built-in scheduler runs daily tracking for every brand due for refresh (per-platform check_frequency in brand settings), on the cron expression in DAILY_CRON_SCHEDULE.
To trigger the same flow out-of-band (e.g. from an external cron), use the internal endpoints with the cron secret:
POST /api/internal/daily-tracking
POST /api/internal/trigger-tracking
Authorization: Bearer <CRON_SECRET>
Continue: Webhooks →