Skip to the content.

Configuration Guide

Horizon is configured through two files: a .env file for API keys and a data/config.json file for sources, AI provider, and filtering options.

AI Providers

Configure which AI model scores and summarizes your content.

Anthropic Claude:

{
  "ai": {
    "provider": "anthropic",
    "model": "claude-sonnet-4.5-20250929",
    "api_key_env": "ANTHROPIC_API_KEY"
  }
}

OpenAI:

{
  "ai": {
    "provider": "openai",
    "model": "gpt-4",
    "api_key_env": "OPENAI_API_KEY"
  }
}

MiniMax:

{
  "ai": {
    "provider": "minimax",
    "model": "MiniMax-M2.7",
    "api_key_env": "MINIMAX_API_KEY"
  }
}

Available models: MiniMax-M2.7, MiniMax-M2.7-highspeed, MiniMax-M2.5, MiniMax-M2.5-highspeed

Aliyun DashScope (OpenAI-compatible):

{
  "ai": {
    "provider": "ali",
    "model": "qwen-plus",
    "api_key_env": "DASHSCOPE_API_KEY"
  }
}

Use the DashScope compatible-mode endpoint. Set DASHSCOPE_API_KEY in your .env. Optional: set base_url to override the default https://dashscope.aliyuncs.com/compatible-mode/v1.

Custom Base URL (for proxies):

{
  "ai": {
    "provider": "anthropic",
    "base_url": "https://your-proxy.com/v1",
    ...
  }
}

Information Sources

All sources are configured under the top-level sources key in config.json.

GitHub

{
  "sources": {
    "github": [
      {
        "type": "user_events",
        "username": "gvanrossum",
        "enabled": true
      },
      {
        "type": "repo_releases",
        "owner": "python",
        "repo": "cpython",
        "enabled": true
      }
    ]
  }
}

Hacker News

{
  "sources": {
    "hackernews": {
      "enabled": true,
      "fetch_top_stories": 30,
      "min_score": 100
    }
  }
}

RSS Feeds

{
  "sources": {
    "rss": [
      {
        "name": "Blog Name",
        "url": "https://example.com/feed.xml",
        "enabled": true,
        "category": "ai-ml"
      }
    ]
  }
}

Reddit

{
  "sources": {
    "reddit": {
      "enabled": true,
      "fetch_comments": 5,
      "subreddits": [
        {
          "subreddit": "MachineLearning",
          "sort": "hot",
          "fetch_limit": 25,
          "min_score": 10
        }
      ],
      "users": [
        {
          "username": "spez",
          "sort": "new",
          "fetch_limit": 10
        }
      ]
    }
  }
}

Filtering

Content is scored 0-10:

{
  "filtering": {
    "ai_score_threshold": 7.0,
    "time_window_hours": 24
  }
}

Environment Variable Substitution

RSS feed URLs support ${VAR_NAME} syntax for secrets. The variable is expanded at runtime from environment variables (or .env file):

{
  "name": "LWN.net",
  "url": "https://lwn.net/headlines/full_text?key=${LWN_KEY}",
  "enabled": true
}

This way config.json can be committed to a public repo without leaking tokens.

Email Subscription

Email delivery is optional and disabled unless email.enabled is true. Horizon uses SMTP to send daily summaries and IMAP to check subscribe/unsubscribe requests.

{
  "email": {
    "enabled": true,
    "smtp_server": "smtp.qq.com",
    "smtp_port": 465,
    "imap_server": "imap.qq.com",
    "imap_port": 993,
    "email_address": "xxx@qq.com",
    "password_env": "EMAIL_PASSWORD",
    "sender_name": "Horizon Daily",
    "subscribe_keyword": "SUBSCRIBE",
    "unsubscribe_keyword": "UNSUBSCRIBE"
  }
}

Webhook Notification

Webhook notification is optional and disabled unless webhook.enabled is true. Horizon can call Feishu/Lark, DingTalk, Slack, Discord, or any custom webhook endpoint when the pipeline succeeds or fails.

{
  "webhook": {
    "enabled": true,
    "url_env": "HORIZON_WEBHOOK_URL",
    "delivery": "summary",
    "languages": null,
    "request_body": {
      "text": "#{message_title}\n#{summary}"
    },
    "headers": ""
  }
}

When request_body is a JSON object or array, Horizon renders placeholders and serializes it as JSON. When it is a string, Horizon renders it directly and detects JSON if the rendered string is valid JSON.

Webhook Templates

Available variables:

Variable Description
#{date} Report date, for example 2026-04-24
#{language} Language code, such as en or zh
#{important_items} Number of items that passed the score threshold
#{all_items} Total number of fetched items
#{result} success or failed
#{timestamp} Unix timestamp
#{message_title} Message title, such as the daily title, overview title, or item title
#{message_kind} Message kind: summary, overview, item, failure, or manual
#{summary} Message Markdown. In summary_and_items mode this is the overview or one item body, depending on the message

When delivery is summary_and_items, item messages also include:

Variable Description
#{item_index} 1-based item number
#{item_count} Total number of item messages
#{item_title} Current item title
#{item_url} Current item URL
#{item_score} Current item AI score

For webhook delivery, Horizon flattens HTML disclosure blocks such as <details><summary>...</summary> in #{summary} into plain Markdown link lists. This makes the generated summary easier to render in chat products. Saved Markdown files, GitHub Pages, and email content are unchanged.

Use #{key?limit=N&split=DELIM} to truncate long values by splitting on DELIM and keeping segments until the total character count reaches N.

#{summary?limit=3000&split=---}

DingTalk

In DingTalk, create a custom group robot and use a custom keyword such as Horizon. The keyword must appear in the body content.

{
  "msgtype": "markdown",
  "markdown": {
    "title": "Horizon #{date} Daily",
    "text": "Horizon result: #{result}\n\nHorizon important items: #{important_items}/#{all_items}\n\n#{summary}"
  }
}

Feishu / Lark

In Feishu or Lark, create a custom group robot and use a custom keyword such as Horizon. The keyword must appear in the body content.

Use Card JSON 2.0 for Markdown rendering. The card must include "schema": "2.0" and put rich-text Markdown components under card.body.elements.

{
  "msg_type": "interactive",
  "card": {
    "schema": "2.0",
    "config": {
      "wide_screen_mode": true
    },
    "header": {
      "title": {
        "tag": "plain_text",
        "content": "#{message_title}"
      },
      "template": "blue"
    },
    "body": {
      "elements": [
        {
          "tag": "markdown",
          "content": "Horizon result: #{result}\nHorizon important items: #{important_items}/#{all_items}"
        },
        {
          "tag": "hr"
        },
        {
          "tag": "markdown",
          "content": "#{summary}"
        }
      ]
    }
  }
}

Static Site

Horizon writes generated summaries to data/summaries/ and copies publishable Markdown into docs/ for the GitHub Pages site. The repository includes a ready-to-use workflow at .github/workflows/daily-summary.yml.

To use GitHub Pages, enable Pages for the repository and run the scheduled workflow or trigger it manually. The generated site is built from the docs/ directory.

MCP Server

Horizon includes an MCP server for AI assistants and MCP-compatible clients.

uv run horizon-mcp

Available tools include hz_validate_config, hz_fetch_items, hz_score_items, hz_filter_items, hz_enrich_items, hz_generate_summary, and hz_run_pipeline.

See src/mcp/README.md for the full tool reference and src/mcp/integration.md for client setup.