Installation
TailStats is a native macOS menu bar app that displays real-time metrics from multiple data sources.
Getting Started
- Download and open TailStats
- The app lives in your menu bar — click the icon to open
- Create your first card in Settings → Cards
- Optionally enable Launch at Login in Settings → General
Keyboard Shortcut: Press ⌘⇧T (Cmd+Shift+T) to toggle the window. Customize this in Settings → General.
Try It Now
Test TailStats with our live example endpoint:
https://www.tailstats.com/example1.json
Card Configuration
Every card requires basic configuration regardless of which data method you use.
Required Settings
| Setting | Description |
|---|---|
| Name | Display name for the card |
| URL | API endpoint URL |
| Refresh Interval | How often to fetch new data |
Refresh Intervals
TailStats automatically polls your API at the configured interval:
| Interval | Use Case |
|---|---|
| 30 seconds | Real-time dashboards, live metrics |
| 1 minute | Active monitoring |
| 5 minutes | Standard dashboards |
| 15 minutes | Slow-changing data |
| 30 minutes | Daily summaries |
| 1 hour | Infrequent updates |
Note: Data is also refreshed immediately when the app launches, you click the refresh button, or the device comes back online.
Data Sources
TailStats supports four ways to get data into cards:
Remote API
Poll any REST API at configurable intervals. Extract data with JSONPath or use structured responses.
Local Push
Push data via CLI, scripts, or webhooks. Perfect for CI/CD pipelines, cron jobs, and custom integrations.
File Watch
Monitor local files for changes. Display log files, config values, or any file content as live cards.
AI Agents
Track AI coding assistant usage. Built-in support for Claude Code and Codex CLI.
Authentication
TailStats supports multiple authentication methods for accessing protected APIs.
No Authentication
For public APIs, select "None".
Basic Authentication
HTTP Basic Auth with username and password.
| Field | Description |
|---|---|
| Username | Your username or API user |
| Password | Your password or API key |
Credentials are sent as Authorization: Basic <base64> header.
Bearer Token
For APIs using bearer tokens (JWT, API keys, etc).
| Field | Description |
|---|---|
| Token | Your bearer token or API key |
Sent as Authorization: Bearer <token> header.
OAuth 2.0
For services using OAuth 2.0 client credentials flow.
| Field | Description |
|---|---|
| Client ID | OAuth client identifier |
| Client Secret | OAuth client secret |
| Token URL | Token endpoint URL |
| Scope | Optional OAuth scopes (space-separated) |
TailStats handles token refresh automatically.
Custom Headers
Add any custom HTTP headers for APIs requiring special authentication or configuration.
| Field | Description |
|---|---|
| Header Name | HTTP header name (e.g., X-API-Key) |
| Header Value | Header value |
You can add multiple custom headers per card.
Three Ways to Display Data
TailStats supports three methods for getting data into cards:
| Method | Best For |
|---|---|
| Simple Value | Display whatever the endpoint returns — plain text, number, or raw response |
| Structured Response | APIs you control — return data in TailStats format |
| JSONPath Extraction | Any JSON API — extract specific values with JSONPath |
Simple Value
The simplest method — whatever your endpoint returns is displayed directly as the card value. No parsing, no extraction.
How It Works
- Point TailStats at any URL
- The raw response body is displayed as the value
- Works with plain text, numbers, or any string response
Example Responses
42
Online
$1,234.56
Tip: Simple Value is perfect for quick integrations, status endpoints, or when you just need to display a single metric without building a full API response.
Structured Response
Build an API endpoint that returns data in TailStats format for full control over the display.
Response Structure
Your endpoint should return a JSON object with an items array and optional columns:
{
"items": [ ... ],
"columns": 2
}
| Field | Type | Required | Description |
|---|---|---|---|
| items | CardData[] | Yes | Array of card items to display |
| columns | number | No | Grid columns (1-4, default: 1) |
Field Reference
Each item in the items array has the following structure:
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | No | Unique identifier (auto-generated if omitted) |
| title | string | Yes | Main title text |
| subtitle | string | No | Secondary text below title |
| type | string | Yes | Value type (see Value Types) |
| value | varies | Yes | The value to display (type depends on type field) |
| icon | string | No | SF Symbol name (Apple platforms only) |
| emoji | string | No | Emoji icon (cross-platform) |
| color | string | No | Value text color |
| iconColor | string | No | Icon/emoji background tint color |
| menuBar | boolean | No | Show this value in macOS menu bar (default: false) |
| menuBarBackground | string | No | Menu bar badge background color |
| rightLabel | string | No | Text displayed on the right side |
| format | string | No | Number formatting (see Value Formatting) |
Value Types
The type field determines how value is interpreted and displayed.
string
Plain text value.
{ "title": "Status", "type": "string", "value": "Online" }
number
Numeric value (integer or decimal). Displays with thousands separator.
{ "title": "Users", "type": "number", "value": 1234 }
42 → "42"1234 → "1,234"3.14 → "3.14"1234.56 → "1,234.56"array
Array of numbers, displayed as a sparkline chart.
{ "title": "Traffic", "type": "array", "value": [10, 25, 15, 30, 20] }
progress
Progress bar (0.0 to 1.0 range).
{ "title": "Storage", "type": "progress", "value": 0.75 }
0.75 → 75% progress bar0.5 → 50% progress barCard Types (15)
TailStats supports 15 different card types for displaying your data.
Basic Types
| Type | Description | Example |
|---|---|---|
| string | Text display with optional formatting | "value": "Hello" |
| number | Numeric with formatting (currency, compact, percent) | "value": 1234.56 |
| progress | Horizontal progress bar (0-100% or 0-1 range) | "value": 0.75 |
| status | Auto-colored indicator (success/warning/error/info) | "value": "Online" |
| array | Sparkline or bar chart visualization | "value": [10, 20, 15, 30] |
Rich Media Types
image
URL or base64-encoded image.
{ "type": "image", "value": "https://example.com/avatar.png" }
message
Chat bubble with sent/received modes.
{ "type": "message", "value": "Hello!", "displayMode": "received" }
map
Interactive map with markers.
{ "type": "map", "value": { "markers": [{ "lat": 37.7, "lng": -122.4, "label": "SF" }] } }
Advanced Types
timer
Live countdown/elapsed/duration display
gauge
Circular dial indicator
link
Clickable URL (opens browser)
list
Vertical item list (max 10 visible)
qrcode
Generated QR code from string
code
Syntax-highlighted code with copy button
log
Scrollable log entries with level coloring (info, warn, error, debug)
Display Rules
Conditional formatting engine that transforms card values based on rules. Up to 10 rules per card, 5 conditions per rule.
Condition Sources
| Source | Description |
|---|---|
| rawResponse | Raw HTTP response body (before parsing) |
| extractedValue | Value after JSONPath extraction |
| httpStatus | HTTP status code (remote API cards only) |
| itemValue | Individual item's display value |
Operators
String Operators
contains/notContainsequals/notEqualsstartsWith/endsWithmatchesRegexisEmpty/isNotEmpty
Numeric Operators
greaterThan(>)lessThan(<)greaterOrEqual(>=)lessOrEqual(<=)
Rule Actions
| Action | Description |
|---|---|
| overrideValue | Replace displayed value |
| overrideIcon | Change SF Symbol icon |
| overrideEmoji | Change emoji |
| overrideColor | Change color (name or hex) |
| overrideType | Change card value type |
Example Rule
{
"conditions": [
{ "source": "extractedValue", "op": "greaterThan", "value": "80" }
],
"conditionMode": "all",
"action": {
"overrideValue": "Critical",
"overrideColor": "red",
"overrideIcon": "exclamationmark.triangle.fill"
},
"enabled": true
}
Logic: Use conditionMode: "all" for AND logic, "any" for OR. First matching rule wins.
JSONPath Extraction
Extract specific values from any JSON API using JSONPath expressions. No API modifications required.
What is JSONPath?
JSONPath is a query language for JSON data. It lets you pinpoint exactly which value to extract from an API response.
Example: If an API returns:
{
"data": {
"users": 1250,
"revenue": 45000
}
}
Use $.data.users to extract 1250.
JSONPath Syntax
All paths start with $ (the root element).
Basic Navigation
| Syntax | Description | Example |
|---|---|---|
| $.key | Access a property | $.data |
| $['key'] | Property with special chars | $['my-key'] |
| [0] | First array item | $.items[0] |
| [-1] | Last array item | $.items[-1] |
| [*] | All array items | $.items[*].name |
| .. | Search anywhere | $..price |
Advanced Features
| Syntax | Description | Example |
|---|---|---|
| [0:5] | Items 0 through 4 | $.items[0:5] |
| [1,3,5] | Specific items | $.items[1,3,5] |
| [?(@.active)] | Filter by field exists | $.users[?(@.active)] |
| [?(@.age > 18)] | Filter by condition | $.users[?(@.age > 18)] |
Filter Operators
| Operator | Description |
|---|---|
| == | Equal |
| != | Not equal |
| < <= > >= | Comparisons |
| && | AND |
| || | OR |
Filter examples:
$.users[?(@.active == true)]— Active users only$.items[?(@.price < 100)]— Items under $100$.users[?(@.age > 18 && @.verified)]— Verified adults
Extraction Configuration
Each JSONPath extraction can be configured with:
| Field | Description |
|---|---|
| Path | JSONPath expression (e.g., $.data.value) |
| Title | Label shown above the value |
| Format | Format string (see Value Formatting) |
| Icon | SF Symbol name (e.g., person.fill) |
| Emoji | Unicode emoji (e.g., 💰) |
| Color | Icon/value color |
| Show in Menu Bar | Display in macOS menu bar |
| Menu Bar Text Color | Color of menu bar text |
| Menu Bar Background | Badge background color |
Multiple Extractions
Add multiple JSONPath extractions to show several values from the same API in one card.
Example: From a dashboard API, extract:
- Total users →
$.metrics.users - Revenue →
$.metrics.revenue - Active sessions →
$.metrics.active
All values display together in a grid layout. Choose 1-4 columns.
Value Formatting
Control how numeric values are displayed using format strings.
Format String Syntax
Format strings follow this pattern:
type:option:modifier
| Part | Required | Description |
|---|---|---|
| type | Yes | number, currency, or percent |
| option | Sometimes | Currency code or display option |
| modifier | No | Additional modifiers like cents |
Number Formats
| Format String | Input | Output | Description |
|---|---|---|---|
| number | 1234 | 1,234 | Standard with thousands separator |
| number:compact | 1234567 | 1.2M | Compact notation (K, M, B) |
Currency Formats
Format: currency:CODE or currency:CODE:cents
| Format String | Input | Output |
|---|---|---|
| currency:USD | 1234 | $1,234.00 |
| currency:EUR | 1234 | €1,234.00 |
| currency:GBP | 1234 | £1,234.00 |
| currency:JPY | 1234 | ¥1,234 |
| currency:CAD | 1234 | CA$1,234.00 |
| currency:AUD | 1234 | A$1,234.00 |
Currency from Cents
For APIs returning amounts in cents, add :cents modifier to divide by 100:
| Format String | Input | Output |
|---|---|---|
| currency:USD:cents | 12345 | $123.45 |
| currency:EUR:cents | 12345 | €123.45 |
| currency:GBP:cents | 12345 | £123.45 |
Percentage Formats
| Format String | Input | Output | Description |
|---|---|---|---|
| percent | 75 | 75% | Whole number percentage |
| percent:decimal | 0.75 | 75% | Decimal (multiplied by 100) |
Icons
Two icon options for cross-platform support.
Icon Resolution Order
TailStats resolves icons in this order:
- macOS/iOS: Use
icon(SF Symbol) if provided and valid - macOS/iOS fallback: Use
emojiificonis missing or invalid - Android: Always use
emoji(SF Symbols not supported) - No icon: If neither is provided, no icon is displayed
| Field | Platform | Description |
|---|---|---|
| icon | Apple only | SF Symbol name (e.g., "heart.fill") |
| emoji | All platforms | Emoji character (e.g., "❤️") |
Recommendation: Always provide both icon and emoji for cross-platform compatibility.
Common SF Symbols
Full list: SF Symbols App
Colors
Color fields (color, iconColor, menuBarBackground) accept hex colors or named colors.
Hex Colors
Named Colors
CLI Tool
Push data to TailStats cards from the command line, scripts, or automation.
Installation
curl -fsSL https://www.tailstats.com/install | sh
This installs the tailstats command to /usr/local/bin.
Push Command
Send data to a TailStats card:
tailstats push <card-id> [options]
Value Options
| Option | Description |
|---|---|
| --value "42" | Simple value (string or number) |
| --json '{...}' | JSON object with full card data |
| --file ~/data.json | Read JSON from file |
Display Options
| Option | Description |
|---|---|
| --title "CPU Usage" | Set item title |
| --type number | Value type: number, string, progress, status, array |
| --format percent | Format: percent, currency:USD, number:compact |
| --icon "cpu" | SF Symbol name |
| --emoji "📊" | Emoji icon |
| --color red | Value color (name or #hex) |
| --columns 2 | Grid columns (1-4) |
Menu Bar Options
| Option | Description |
|---|---|
| --menubar | Show value in menu bar |
| --menubarBackground "#FF0000" | Menu bar badge background color |
Examples
tailstats push abc123 --value "42" --title "Active Users"
tailstats push abc123 --value "0.85" --type progress --title "CPU" --menubar
tailstats push abc123 --json '{"items":[{"title":"Revenue","type":"number","value":1234,"format":"currency:USD"}]}'
Notify Command
Send macOS notifications:
tailstats notify --message "Hello" [options]
| Option | Description |
|---|---|
| --title "Title" | Notification title |
| --subtitle "Info" | Subtitle text |
| --sound default | Play notification sound |
| --open "https://..." | URL to open on click |
| --execute "open /path" | Command to run on click |
| --group "my-group" | Group notifications together |
Tip: Combine push with notify: tailstats push abc123 --value "Error" --notify "Build failed!"
Local Push Server
Three ways to push data to TailStats from local scripts and services.
HTTP Server
REST API on localhost for easy integration.
POST http://localhost:9876/push/<card-id>
Content-Type: application/json
{"items": [{"title": "Status", "type": "string", "value": "OK"}]}
WebSocket support available for persistent connections.
Unix Socket
Fastest option for local scripts. Low-latency IPC.
| Socket Path | ~/tmp/tailstats.sock |
The CLI automatically uses Unix socket when available.
File Drop
Drop JSON files to push data. Works offline, no server needed.
| Directory | ~/Library/Application Support/TailStats/push/ |
Use tailstats push --file-drop to force file-based delivery.
Protocol Selection: CLI automatically picks the fastest available: Unix Socket → HTTP → File Drop.
File Watch Cards
Monitor local files and display their contents as live cards.
Parsing Modes
Raw Content
Display the entire file contents as text. Good for log files or simple text output.
JSONPath
Extract specific values from JSON files using JSONPath expressions. Same syntax as API extractions.
Items Array
Parse file as TailStats structured response format with items array.
Sandbox-Safe: TailStats uses secure file bookmarks to access files outside the sandbox. You'll be prompted to grant access once per file.
AI Agent Tracking
Built-in monitoring for AI coding assistants. Track usage, sessions, and costs.
Claude Code CLI
| History File | ~/.claude/history.jsonl |
| Metrics | Sessions, messages, projects, token usage, costs |
Codex CLI
| Data Directory | ~/.codex/ |
| Metrics | Sessions, generations, token usage |
Displayed Metrics
- Today's activity (messages, sessions)
- Current session info
- Last 7 days sparkline chart
- Token usage and estimated costs
Enable in Settings: Go to Settings → AI Agents to enable tracking for each agent.
MCP Support
TailStats supports the Model Context Protocol (MCP), enabling AI assistants like Claude to directly interact with your dashboard cards.
What is MCP?
MCP (Model Context Protocol) is an open standard by Anthropic that allows AI assistants to use external tools. With TailStats MCP support, AI coding agents can list, read, update, and create cards.
Setup
1. Enable Local Push Server
In TailStats, go to Settings → General → Local Push Server and enable:
- Local Push Server (master toggle)
- HTTP & WebSocket
2. Configure Claude Code
Add to ~/.claude/settings.json:
3. Start Using
Ask Claude naturally:
- "List my TailStats cards"
- "Update the build-status card to 'Passing' with green color"
- "Create a card called 'Deploy Status' with identifier 'deploy'"
Available Tools
list_cards
List all TailStats cards with their current values and status.
Parameters:
None
read_card
Read detailed data from a specific card.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
| card | string | Yes | Card name or identifier |
update_card
Push new data to a Local Push card. Supports single values or multiple items.
Parameters:
| Name | Type | Description |
|---|---|---|
| card | string | Card identifier (required) |
| value | string | Value to display (single item mode) |
| title | string | Title/label for the value |
| type | string | string, number, progress, status |
| color | string | Color name or hex (red, green, #FF5500) |
| icon | string | SF Symbol name |
| items | array | Array of items (multi-item mode) |
| columns | integer | Grid columns (1-4) |
| menuBar | boolean | Show in menu bar |
Example (single item):
Example (multi-item):
create_card
Create a new Local Push card for AI/automation updates.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Display name for the card |
| identifier | string | Yes | Unique identifier for push updates |
Use Cases
AI Coding Agent Tracking
Track Claude Code, Codex, or other AI agents in real-time. Show task status, project names, and completion states.
Build Pipeline Status
Display CI/CD status on your desktop. Show build, test, and deploy states with color-coded indicators.
Project Dashboard
Create a multi-project overview with status indicators for frontend, backend, and mobile progress.
Progress Tracking
Show completion progress for migrations, deployments, or long-running tasks with progress bars.
Security: MCP endpoint only accepts connections from localhost. No authentication required for local connections. All data stays on your machine.
Error Handling
TailStats handles errors gracefully to keep your dashboard running.
Error Types
| Error | Cause | Display |
|---|---|---|
| Offline | No internet connection | Shows cached data with "offline" indicator |
| Timeout | Request took longer than 30 seconds | Shows cached data or error |
| Server Error | HTTP 500+ response | Shows error with status code |
| Not Found | HTTP 400+ response | Shows error with status code |
| Invalid URL | Malformed URL | Shows configuration error |
| Parse Error | Response isn't valid JSON | Shows parse error |
| Auth Error | Authentication failed | Shows auth error message |
| JSONPath Error | Path doesn't match data | Shows path error |
Offline Behavior
When your device goes offline:
- Cards display their last successful data
- A "stale" indicator shows data may be outdated
- Last successful update time is displayed
- Data refreshes automatically when back online
Partial Failures
For cards with multiple JSONPath extractions:
- Successfully extracted values are displayed
- Failed extractions show individual errors
- One failing extraction doesn't hide the others
Requirements
TailStats is a native macOS app built with SwiftUI.
System Requirements
- ✓ macOS 13 (Ventura) or later
- ✓ Apple Silicon or Intel Mac
Security
- ✓ Credentials stored in macOS Keychain
- ✓ Local push server binds to localhost only
- ✓ Sandboxed file access with bookmarks
Examples
Structured Response: Dashboard
{
"items": [
{
"title": "Revenue",
"subtitle": "This month",
"type": "number",
"value": 12450.00,
"icon": "dollarsign.circle",
"emoji": "💰",
"format": "currency:USD",
"color": "green",
"menuBar": true
},
{
"title": "Users",
"subtitle": "Active now",
"type": "number",
"value": 1234,
"icon": "person.fill",
"emoji": "👤",
"format": "number"
},
{
"title": "CPU Usage",
"type": "progress",
"value": 0.65,
"icon": "cpu",
"emoji": "💻"
},
{
"title": "Traffic",
"subtitle": "Last 7 days",
"type": "array",
"value": [120, 150, 180, 140, 200, 220, 190],
"icon": "chart.line.uptrend.xyaxis",
"emoji": "📈"
}
],
"columns": 2
}
JSONPath Examples
User Count
- API returns:
{ "data": { "total_users": 1234567 } } - Path:
$.data.total_users - Format:
number:compact - Result: 1.2M
Revenue from Cents
- API returns:
{ "revenue": { "total_cents": 1234500 } } - Path:
$.revenue.total_cents - Format:
currency:USD:cents - Result: $12,345.00
Filtered Count
- API returns:
{ "servers": [{"status": "online"}, {"status": "offline"}, {"status": "online"}] } - Path:
$.servers[?(@.status == 'online')] - Format:
number - Result: 2
Authentication Examples
Bearer Token API
- URL:
https://api.example.com/metrics - Auth: Bearer Token
- Token:
sk_live_abc123... - Refresh: 5 minutes
Custom Header
- URL:
https://api.example.com/stats - Auth: None
- Custom Header:
X-API-Key=your-api-key