# infrapage > Your project's live numbers — stars, uptime, users — on one page you share. Like Linktree for your infrastructure. --- ## [Introduction](https://infra.page/docs/getting-started/introduction) **infrapage** is a widget-based infrastructure dashboard. Think of it as "Linktree for project infrastructure" — a single page that brings together GitHub repos, uptime monitors, analytics, CI/CD pipelines, and more into a beautiful, shareable dashboard. > **Tip:** Every widget is public or private on its own. Build in public without leaking the numbers you'd rather keep to yourself. ## Key Features **Widget-Based** Build dashboards from 35 widget types covering GitHub, revenue, analytics, uptime monitoring, and more. **Drag & Drop Editor** Arrange widgets with a visual grid editor. Resize, reorder, and delete with intuitive controls. **Custom Domains** Map custom domains to any page for a branded experience. **SSR + Hydration** Pages load fast with server-side rendering. Each widget hydrates independently — slow APIs never block the page. **Dark & Light Themes** Built-in theme support with smooth transitions. Respects system preferences by default. **Per-Widget Privacy** Show a growth rate without the absolute number, or hide a widget from visitors entirely. You choose per widget. ## How It Works infrapage uses a **two-phase loading** approach for optimal performance: 1. **Server-Side Render** — The page shell and widget skeletons render immediately on the server. 2. **Client Hydration** — Each widget independently fetches its data and renders as the response arrives. This means your dashboard page loads instantly, and individual widgets fill in as their APIs respond. ## Next Steps **Quick Start** Build your first page in 5 minutes. **Widget Overview** Learn how the widget system works. --- ## [Quick Start](https://infra.page/docs/getting-started/quickstart) Go from nothing to a live page with real numbers on it. No card needed to start. 1. **Create your draft** Head to [infra.page](https://infra.page) and sign in — passkey, password, or a one-time code by email. Paste your project's website and infrapage scans it, proposing widgets from what it finds: a GitHub repo, an uptime monitor, an analytics script. Accept the ones you want and it builds the page for you. Prefer to start empty? Skip the scan and name the page yourself. 2. **Connect your integrations** Widgets need somewhere to read from. Open **Dashboard → Integrations**. GitHub, Google, and X have a **Connect with …** button that runs the OAuth round-trip for you. Everything else takes an API token you paste once — each integration's page tells you exactly where to generate it. 3. **Arrange the page** Open the page and hit **Edit**. Drag widgets to reorder, pull the right edge to resize, drop one on the trash zone to remove it. Each widget also has its own visibility: fully public, growth-only (shows the trend, hides the absolute number), or private to you. 4. **Publish it** Flip the page to **Public** and it's live at `infra.page/p/your-slug` — free, no card, no time limit. List it in Discover (needs at least one verified integration) to join the weekly growth race. > **Note:** Free pages refresh live data every **12 hours**, and an account holds up to **three** of them. Claiming a page — a one-time payment, no subscription — upgrades it for good: hourly refresh, a followed (dofollow) link to your website, a permanent address whose old links keep resolving after a rename — and it frees a slot for the next free page. Once two pillars of verified metrics exist, your page earns an [Infrascore](/docs/getting-started/infrascore) — the 0–100 composite the leaderboard ranks on. ## What's Next? **Add Your First Widget** Learn how to add widgets to your dashboard page. **Embeds & Badges** Put your live numbers in a README or on your own site. --- ## [Infrascore](https://infra.page/docs/getting-started/infrascore) Every public page can earn an **Infrascore** — one number from 0 to 100, computed daily from the page's verified metrics. It's what the [leaderboard](https://infra.page/discover) ranks on, what the weekly race measures movement of, and the chip next to your page title. The one-line promise: **the score is never self-reported.** It only reads numbers that come from a connected integration (or that infra.page measures itself), so a score of 74 means the same thing on every page. ## The five pillars Your score is a weighted blend of five pillars, each scored 0–100: | Pillar | Weight | Fed by | |--------|--------|--------| | **Traction** | 25% | Visitors, stars, followers, subscribers, members, search impressions (Search Console, Bing) | | **Reliability** | 20% | Uptime and response time, measured by infra.page's own checks on your uptime monitor | | **Revenue** | 20% | MRR (Polar, Stripe) | | **Shipping** | 20% | Commits — the 30-day activity pulse and the 52-week commit graph | | **Momentum** | 15% | Week-over-week growth of your verified metrics | Within a pillar, your **best** metric counts — connecting both Polar and Stripe doesn't double-count MRR, and a strong stars number isn't diluted by a quiet Discord. ## How the curves work Raw values aren't comparable ($500 MRR vs. 99.9% uptime vs. 2k visitors), so each metric is banded on a **logarithmic curve** between anchors: growing from 100 to 1,000 visitors is worth the same step as growing from 1,000 to 10,000. Uptime is scored on its *downtime* — the "nines" — so 99.9% genuinely beats 99%. Big absolute numbers cap out; a single whale metric can't dominate the board. ## Coverage: why one widget isn't enough The overall score is the weighted mean of your **present** pillars, shrunk by a coverage factor: - **Fewer than two pillars → no score.** A stars-only page shows "–" until a second signal exists. - More pillars → higher ceiling. A page with all five pillars is scored on its full mean; a two-pillar page tops out around 82% of its. That's deliberate: the fastest way to raise your score is usually to **connect another integration**, not to grow an existing number. Connecting counts even at zero: a connected Polar with $0 MRR makes the Revenue pillar *present* — it raises your coverage without dragging your average down. Connecting can never lower a score. ## What counts (and what doesn't) A widget feeds the score only when **all** of these hold: - It's **public** and its metric is **fully disclosed** (growth-only widgets never leak their absolute through the score). - Its data is **third-party verifiable** — a connected credential (GitHub, Polar, Stripe, Google Analytics, X, Beehiiv, …) — or **app-measured** (the built-in uptime monitor, which infra.page pings itself). - Self-hosted sources (your own Umami, Uptime Kuma, Beszel) and Sentry don't count yet. Hover the score chip anywhere to see your pillar mix as percentages — shares of the score's own composition, never raw values. ## The score in the wild - **The leaderboard** ranks on it; the **This week** view ranks on its movement since Monday — that's the race, and the biggest gain wins the week's crown. - Your page header shows the chip; the tooltip carries the pillar mix. - A README badge is available at `https://infra.page/badge/your-slug/score.svg`. - Claimed pages refresh their metrics hourly instead of every 12 hours — so their score moves first. --- ## [Widget System](https://infra.page/docs/widgets/overview) Widgets are the building blocks of infrapage. Every dashboard page is composed of widgets arranged in a grid. The system is designed to be extensible — each widget type fetches data from a different source and renders it as a card. ## Three-Layer Architecture Every widget in infrapage consists of three layers: 1. **Widget Instance (Config)** Stored in MongoDB as part of the page document. Contains the widget's **kind**, **configuration parameters** (like a GitHub repo URL or an API endpoint), and **grid position** (x, y, width, height). ```json { "kind": "GitHubRepo", "config": { "owner": "hauju", "repo": "infrapage" }, "w": 1, "h": 1, "x": 0, "y": 0 } ``` 2. **Widget Data (Fetched)** The runtime data fetched from external APIs based on the widget's configuration. For a GitHub repo widget, this would include star count, language, description, and last updated time. Each widget kind has its own data struct — the `WidgetData` enum dispatches to the correct fetcher. 3. **Widget Component (Render)** A Dioxus component that takes the fetched data and renders it as a DaisyUI card. Each widget kind has a dedicated component in `src/components/widgets/`. ## Two-Phase Loading Public pages use a two-phase approach for optimal performance: 1. **SSR Phase** — `use_server_future` fetches the page metadata from MongoDB. The HTML shell renders immediately with widget skeletons (loading placeholders). 2. **Hydration Phase** — Each widget independently calls `fetch_widget_data()` via `use_resource`. Widgets render as their data arrives. > **Tip:** This architecture means a slow external API (like a rate-limited GitHub endpoint) never blocks the page shell or faster widgets from rendering. ## Widget Sizes Widgets support three size presets in the 4-column grid: | Size | Grid Columns | Description | |------|-------------|-------------| | **Normal** | 1 column | Default compact card | | **Wide** | 2 columns | Extended view with more detail | | **Large** | 4 columns | Full-width with comprehensive data | Not every widget supports all sizes — each kind defines which sizes are allowed. ## Grid Editor In edit mode, infrapage uses **GridStack.js** for drag-and-drop widget arrangement: - **Drag** widgets to reposition them - **Resize** using edge handles (east and south) - **Delete** by dragging to the trash zone - **Double-click** to edit widget configuration The grid layout is saved to MongoDB when you exit edit mode. --- ## [Available Widgets](https://infra.page/docs/widgets/available-widgets) infrapage ships with 35 widget types across several categories. Each widget fetches data from an external source and displays it as a card on your dashboard. ## Visibility at a glance Every widget has a visibility setting — **Public** (visible to everyone), **Private** (admin only), or **Hidden** (not rendered). When you add a new widget, infrapage picks a safe default based on what the widget exposes: - Widgets showing **public signals** (repo stars, uptime, deploys, community) default to **Public** so you can build in public. - Widgets showing **revenue, infra metrics, error rates, user counts, or customer feedback** default to **Private** — flip them public only after you've reviewed what they render. You can change the visibility of any widget at any time from the widget's edit modal. ## GitHub **GitHub Repo** Displays repository information including stars, forks, language, and description. Configure with owner and repo name. **Default: Public** — public GitHub metadata. **GitHub Pipeline** Shows the latest CI/CD pipeline status for a repository. Displays workflow name, status, and last run time. **Default: Public** — build status is usually shareable. **GitHub Commit Graph** Renders a commit activity graph similar to GitHub's contribution chart. Best displayed at Wide or Large size. **Default: Public** — activity counts only, no sensitive content. **GitHub Releases** Shows the latest releases of a repository with their changelog notes. **Default: Public** — release notes are already public on GitHub. **Activity Pulse** Answers "is this project actively maintained?" from recent GitHub activity. **Default: Public** — designed as a public trust signal. **Actions Usage** GitHub Actions minutes used vs. included in your plan. **Default: Private** — billing-related usage data. ## Project Management **Linear Cycle Pulse** Current Linear cycle progress and pace. **Default: Public** — shareable build-in-public progress signal. **Linear Throughput** Issues shipped per week from Linear — no cycles needed. **Default: Public** — activity counts only. ## Monitoring **Uptime Monitor** Simple HTTP uptime check. Sends a HEAD request to the configured URL and shows response time and status. **Default: Public** — status pages are typically public. **Uptime Kuma** Integrates with an Uptime Kuma instance to show monitor status, uptime percentage, and response times. **Default: Public** — intended for public status pages. **Beszel** Connects to a Beszel server monitoring instance displaying CPU, memory, and disk usage metrics. **Default: Private** — infrastructure metrics can reveal capacity and outage patterns. ## Analytics **Umami Stats** Displays website analytics from a self-hosted Umami instance — visitors, pageviews, and bounce rate. **Default: Public** — common as social proof, but set to Private if your traffic is sensitive. **Google Search Console** Shows search performance data including clicks, impressions, and average position. **Default: Private** — search queries can contain PII and reveal traffic sources. **Bing Search Performance** Clicks, impressions, and CTR from Bing Webmaster Tools. **Default: Private** — search data can reveal traffic sources. **Datafast Stats** Visitors, pageviews, and revenue from Datafast analytics. **Default: Private** — bundles revenue with traffic stats. **Ahrefs Domain Rating** Ahrefs DR authority score (0–100) via Ahrefs' free public endpoint — no credential needed. **Default: Public** — DR is publicly visible information. **Bunny CDN Stats** Bandwidth, requests, and cache hit rate from bunny.net. **Default: Public** — traffic served is a shareable scale stat. **Metrics Chart** Overlays the history of this page's other metrics on one chart, built from daily snapshots. Only charts already-public metrics on the public page. **Default: Public**. ## Revenue **Polar Overview** Displays Polar.sh monetization data — MRR, subscribers, and revenue metrics. **Default: Private** — revenue data; opt in to Public explicitly if you share it. **Stripe Overview** Displays Stripe monetization data — MRR and active subscribers. **Default: Private** — revenue data; opt in to Public explicitly if you share it. > **Note:** The **TrustMRR Verified MRR** widget is currently unavailable. TrustMRR's API terms don't permit publishing their data on public pages without written permission, which we've requested — until it's granted the widget can't be added, and existing ones are paused. Use the Polar or Stripe widget for MRR in the meantime; both read your own payment provider directly. ## Community **Discord Server** Shows Discord server info including member count, online members, and server name. **Default: Public** — server info is typically shareable. **Newsletter Subscribers** Active subscriber count plus average open and click rates from your beehiiv publication. **Default: Public** — the #2 build-in-public metric. ## Error Tracking **Sentry Overview** Integrates with Sentry to display recent error counts, unresolved issues, and project health status. **Default: Private** — error rates can signal product health negatively; review before exposing. **Thermite Overview** Integrates with a self-hosted Thermite instance to display unresolved issues, events over the last 24 hours, and the newest release's crash-free rate. **Default: Private** — error rates can signal product health negatively; review before exposing. ## Authentication **Zitadel Users** Shows user count and recent registrations from a connected Zitadel identity provider. **Default: Private** — user counts and growth trends are business-sensitive. **FerrisKey Users** Realm user count and signup trend from a connected FerrisKey instance. **Default: Private** — user counts and growth trends are business-sensitive. ## Deployments **Vercel Deployment** Displays the latest Vercel deployment status for a project, including branch, commit SHA, and preview URL. **Default: Public** — deploy status is typically public. ## Profile **Author** Avatar, name, tagline, and social links for the page owner. **Public-only** — audience-facing, so it hides itself on the private page. **Sponsor** Support links — Buy Me a Coffee, GitHub Sponsors, Ko-fi, and more. **Public-only** — audience-facing, so it hides itself on the private page. ## Custom **Custom Link** A simple link card with a title, description, and URL. Useful for linking to external dashboards or documentation. **Default: Public** — fully user-controlled content. **Status Badge** Displays a status badge from any badge URL (shields.io, custom badges, etc.). **Default: Public** — pure status indicator. ## SeggWat Integration **SeggWat Feedback** Shows recent user feedback from a SeggWat instance. **Default: Private** — customer feedback may contain names or PII. **SeggWat Ratings** Displays aggregated ratings and satisfaction scores. **Default: Private** — satisfaction metrics are usually internal. **SeggWat Summary** Overview of SeggWat feedback metrics and trends. **Default: Private** — derived from confidential feedback. **SeggWat Feature Voting** Top-voted feature requests with their status. **Default: Public** — a public roadmap signal. **SeggWat Changelog** Latest published changelog entries. **Default: Public** — changelogs are meant to be read. --- ## [Adding Widgets](https://infra.page/docs/guides/adding-widgets) This guide walks you through adding widgets to a dashboard page using the admin interface. ## Create a Page Before adding widgets, you need a page to put them on. 1. **Open the Dashboard** Navigate to `/dashboard` in your browser. If authentication is configured, you'll need to log in first. 2. **Create a New Page** Click the **"New Page"** button. Enter a title and a URL slug (e.g., `my-project`). The page will be accessible at `/p/my-project`. 3. **Open the Page Editor** Click on your new page to open the page editor. ## Add a Widget 1. **Enter Edit Mode** Click the **"Edit"** button in the page editor toolbar. The grid will switch to edit mode with dashed outlines around existing widgets. 2. **Click Add Widget** Click the **"+"** button to open the widget catalog. You'll see all available widget types organized by category. 3. **Choose a Widget Type** Select the widget type you want to add. For example, choose **"GitHub Repo"** to show repository information. 4. **Configure the Widget** Fill in the required configuration fields. For a GitHub Repo widget, enter the repository owner and name. Select a **size** for the widget (Normal, Wide, or Large) based on how much space you want it to occupy. 5. **Save** Click **"Add"** to place the widget on the grid. You can then drag it to reposition or resize it. ## Arrange Widgets In edit mode, you can: - **Drag** widgets to move them around the grid - **Resize** by pulling the edge handles - **Delete** by dragging a widget to the trash zone at the bottom - **Edit** by double-clicking a widget to change its configuration > **Tip:** Widget positions are saved automatically when you click **"Save Layout"** or exit edit mode. ## Quick Add Some widget types support **quick add** from the widget catalog. These come with sensible defaults so you can add them with a single click and configure later. > **Note:** Widgets that require integration tokens (like GitHub or Polar) need the corresponding integration to be configured in **Settings** before they can fetch data. --- ## [Embeds](https://infra.page/docs/guides/embeds) Every public page can hand out images of its own numbers. Paste one into a README, a landing page, or a tweet and it keeps itself current — the image is regenerated from your live widget data, so there is nothing to re-paste when a number changes. All embed endpoints are public and unauthenticated. They only ever serve data your page already shows publicly: a private or hidden widget has no badge, and a widget set to disclose growth only never emits its absolute value in any format. > **Note:** Find every snippet on this page in the app: open your page, click **Embed**, and pick a tab. The previews there reflect the exact options you choose. ## The five embeds | Embed | Endpoint | Best for | |---|---|---| | Social card | `/og/{slug}.png` | Link previews on X, LinkedIn, Slack, Discord | | Website badge | `/badge/{slug}.svg` | A live-stats strip in your site footer | | README badge | `/badge/{slug}/{widget_id}.svg` | A row of metric badges under a README title | | Chart | `/embed/chart/{slug}/{widget_id}.svg` | Showing a trend, not just a number | | Featured badge | `/featured/dark.svg` | A "Featured on infra.page" promo pill | ## Shared options `theme`, `color`, and `label` work the same way on every embed that renders a metric. They are named after the [shields.io](https://shields.io) parameters of the same name, so the vocabulary should already be familiar. | Parameter | Values | Default | |---|---|---| | `theme` | `dark`, `light` | `dark` | | `color` | Hex (`3fbf8f`, `#3fbf8f`, `abc`) or a name: `brightgreen`, `green`, `yellow`, `orange`, `red`, `blue`, `purple`, `pink`, `teal`, `cyan`, `lime`, `grey`, `black` | Your page's accent color | | `label` | Any short text, replacing the widget's own label | The widget's label | An unrecognised value is ignored rather than treated as an error — a typo in a README renders the default badge instead of a broken image. > **Tip:** Drop the `#` when passing a hex color in a URL. `#` starts the fragment, so `?color=#3fbf8f` sends nothing to the server. `?color=3fbf8f` works. ## README badges One badge per widget, sized and shaped like the build badges already in your README. ``` https://infra.page/badge/my-project/wgt_abc123.svg ``` Wrap it in a link back to your page so the badge sends traffic, not just information: ```markdown [![MRR](https://infra.page/badge/my-project/wgt_abc123.svg)](https://infra.page/p/my-project) ``` ### Styles `style` selects the badge shape: | Value | Result | |---|---| | `flat` | The classic 20px pill with a subtle sheen (default) | | `flat-square` | Same pill, square corners, matte | | `for-the-badge` | Taller, upper-cased, letter-spaced | | `card` | A 220×90 branded tile with the number as the hero | ``` https://infra.page/badge/my-project/wgt_abc123.svg?style=for-the-badge&color=purple ``` Use `card` where a 20px pill would disappear — a landing page hero, or next to a Product Hunt badge. Use `flat` in a README row, where matching the surrounding badges matters more than standing out. > **Tip:** The **README badges** tab lists every badge-able widget at once and has a **Copy all as Markdown** button that emits the whole row. That is almost always what you want when setting up a README for the first time. ## Charts A chart embed draws one widget's recorded history rather than its current value. ``` https://infra.page/embed/chart/my-project/wgt_abc123.svg?period=30d ``` | Parameter | Values | Default | |---|---|---| | `period` | `7d`, `30d`, `90d`, `12m` | `7d` | | `metric` | A series key the widget publishes, e.g. `stars`, `mrr_cents`, `visitors` | The widget's first series | Charts are built from the daily snapshots infrapage records for every widget, so a chart can only reach as far back as your page has been running. > **Note:** Free pages render the last 7 days whatever `period` asks for. **Pro** unlocks the full window. ### Three formats The `.svg` form is the one that matters, because GitHub strips ` ``` `.png` renders at 2× for the surfaces that refuse SVG — mainly social crawlers. ### What can be charted A widget needs recorded numeric history and a fully-disclosed public metric. Widgets set to show growth only cannot be charted at all: a chart *is* a sequence of absolute values, so there is no honest way to draw one without publishing the numbers you chose to withhold. Those widgets return the not-found card rather than a degraded chart. ## Social card The 1200×630 image shown when someone shares your page. It carries the metrics you picked in the **Featured metrics** list, up to three. ```markdown [![My Project](https://infra.page/og/my-project.png)](https://infra.page/p/my-project) ``` Set `width="1200" height="630"` when embedding as HTML — without them the card reflows the host page as it loads. `/og/w/{slug}/{widget_id}.png` produces the same card for a single widget's number. ## Website badge A 28px strip for a site footer: a liveness pip, your featured metrics, and the infra.page mark. ```html My Project — live stats ``` The mark is removed on **Pro** with de-branding enabled. ## Caching and freshness | Embed | Cache | |---|---| | Badges, charts | 5 minutes | | Social cards | 10 minutes | | Featured badge | 24 hours (it carries no live data) | Every embed shows a `live` or `idle` marker earned from when your page last fetched fresh upstream data — not a decoration. A page that has gone quiet reads `idle` rather than claiming a freshness it can't prove. ## Where your embeds are showing up Open **Embed** on any page you own and the top of the modal reports how many third-party sites rendered your embeds in the last 30 days, and which ones. This is owner-facing only. It never feeds the /discover ranking, and there is no public "embedded on N sites" counter — that number mostly measures the traffic of whoever embedded you, which is a poor proxy for whether a project is worth looking at. Loads referred from infra.page itself (your own page, the editor previews) are not counted. --- ## [Custom Domains](https://infra.page/docs/guides/custom-domains) Map your page to your own domain so visitors see `status.myproject.com` instead of `infrapage.io/p/my-project`. SSL is provisioned automatically — there's nothing to configure beyond a single DNS record. > **Note:** Custom domains are a **Pro** feature. Upgrade your account before adding one. ## Setup 1. **Add your domain** Open the **page editor** for the page you want to map and find the **Custom Domains** section. Enter your domain and click **Add**. Use a subdomain such as `status.yourdomain.com` — apex domains (e.g. `yourdomain.com` with no subdomain) aren't supported. The domain starts in the **Pending Verification** state. 2. **Add the CNAME record** After adding the domain, infrapage shows you a **CNAME target**. Go to your DNS provider and create a CNAME record pointing your subdomain at that target: ``` status.yourdomain.com CNAME ``` The exact target is unique to this instance, so always copy it from the page editor rather than guessing. 3. **Verify DNS** Back in the editor, click **Verify DNS**. infrapage checks that your CNAME is live, then requests an SSL certificate for the domain. The status moves to **Provisioning SSL** and, once the certificate is issued, to **Active**. From that point your page is served on your domain over HTTPS. > **Warning:** DNS changes can take up to 24 hours to propagate. If **Verify DNS** doesn't succeed immediately, wait for propagation and try again — the domain stays pending until the CNAME is live. ## Status reference | Status | Meaning | |--------|---------| | **Pending Verification** | Domain added; waiting for you to point the CNAME and verify. | | **Provisioning SSL** | DNS verified; the SSL certificate is being issued. | | **Active** | Live and serving your page over HTTPS. | | **Failed** | Something went wrong — re-check the CNAME record and verify again. | ## Multiple domains Each page can have one custom domain. To run several branded pages, set a different domain on each page. ## Removing a custom domain Delete the domain in the page editor's **Custom Domains** section. The page stays reachable at its original `/p/slug` URL. --- ## [Integrations](https://infra.page/docs/guides/integrations) infrapage connects to external services through integrations. Manage them under **Dashboard → Integrations**. There are two ways to connect: - **One-click connect** — GitHub, Google, and X have a **Connect with …** button that runs an OAuth round-trip and stores the resulting token for you. No copying secrets by hand. - **Paste a token** — every other integration takes an API key or token you create at the provider. Either way the credential is encrypted at rest and never reaches the browser. ## GitHub Used by: **Repository**, **Pipeline**, **Commit Graph**, **Releases**, **Actions Usage**, and **Activity Pulse** widgets. The quickest path is **Dashboard → Integrations → GitHub → Connect with GitHub**. You'll land on GitHub's app-installation screen, where you choose exactly which repositories to expose — **including private ones**. infrapage never receives your code; it mints a short-lived, scoped token per fetch and reads only the metadata the widgets display. > **Tip:** Private repositories are the main reason to use Connect rather than a token. A classic OAuth scope broad enough to read a private repo would grant far more than infrapage needs, so the connect flow uses a GitHub App and you pick the repos. If you'd rather manage the credential yourself, paste a token instead: 1. **Create a fine-grained token** Go to [GitHub Settings → Developer settings → Personal access tokens](https://github.com/settings/tokens?type=beta) and create a **fine-grained token** with read access to the repositories you want to display. Repository permissions: **Metadata**, **Contents**, and **Actions** (read). The **Actions Usage** widget additionally needs the account-level **Plan** (read) permission, and only works for accounts where you're a billing manager. 2. **Add to infrapage** Open **Dashboard → Integrations → GitHub** and paste the token. ## Polar Used by: **Polar Overview** widget. Configure your Polar API token to display monetization data like MRR and subscriber counts. 1. **Create an Organization Access Token** In the [Polar dashboard](https://polar.sh/dashboard), open the organization you want to track and go to **Settings → General → Developers**, then click **Create token**. Tokens are scoped to a single organization — you'll need one token per Polar org. 2. **Add to infrapage** Open **Dashboard → Integrations → Polar** and paste your Polar token. To track multiple orgs, add one credential per organization. ## Discord Used by: **Discord Server** widget. The Discord widget uses public server invite data — no bot token is required. Just configure the widget with your server's invite code or ID. ## X (Twitter) Used by: **Followers** widget. Go to **Dashboard → Integrations → X (Twitter) → Connect with X**. infrapage reads your own account through `GET /2/users/me`, which is what proves the follower count is *yours* — there's no handle to type, and no way to display someone else's numbers as your own. Follower counts move slowly and every lookup costs money, so a connected account is read **once every 24 hours**, or **once every 72 hours** when the page owner isn't on Pro. That holds no matter how many pages, embeds, or visitors use it — they all share one cached read. **Pro** connects several X accounts at once, each with its own credential, so a maker with a handle per product can chart them separately. Otherwise you hold one connected account: connecting a different handle replaces it rather than adding a second, since each account is its own billed read on every cycle. ## beehiiv Used by: **Newsletter Subscribers** widget. 1. **Create an API key** Sign in at [app.beehiiv.com](https://app.beehiiv.com/) and go to **Settings → Workspace → API**. Create a new API key and copy your publication ID (starts with `pub_`) from the same page. Read API access is included on all beehiiv plans, including the free Launch tier. **Identity verification:** beehiiv gates API key creation behind a Stripe Identity check (they quote under 5 minutes). If you see "Verify your identity to create an API key", complete that first. The publication ID is shown on the same page *without* verification — so getting the `pub_…` ID but no key is expected, not a bug. 2. **Add to infrapage** Open **Dashboard → Integrations → beehiiv** and paste the API key. When adding the widget, enter the `pub_…` publication ID. ## Umami Analytics Used by: **Umami Stats** widget. Configure the widget with: - Your Umami instance URL - Website ID - API credentials (if authentication is enabled on your Umami instance) ## Sentry Used by: **Sentry Overview** widget. Configure with your Sentry organization slug, project slug, and an auth token with `project:read` scope. ## Thermite Used by: **Thermite Overview** widget. Configure with your Thermite instance URL as the API host and an `oat_` API key minted in the instance's Settings → API keys. The widget picker lists the instance's projects. ## Uptime Kuma Used by: **Uptime Kuma** widget. Configure with your Uptime Kuma instance URL. The widget reads from Uptime Kuma's public status page API. ## Beszel Used by: **Beszel** widget. Configure with your Beszel server URL and authentication credentials to display system metrics. ## Google Search Console Used by: **Search Console** widget. Go to **Dashboard → Integrations → Google Search Console → Connect with Google** and grant read-only access to your Search Console data. infrapage asks for `webmasters.readonly` and nothing else — it can read your search performance, but not change anything in your account. Once connected, adding the widget lets you pick which verified property to display from a dropdown of the properties you own. The widget shows clicks, impressions, and top queries over a period you choose (7, 28, 90, or 180 days; 28 by default). If you'd rather not connect your Google account, you can paste a **service-account JSON key** instead: 1. **Create a service account** In the [Google Cloud console](https://console.cloud.google.com/), enable the **Search Console API**, create a service account, and download a JSON key for it. 2. **Grant it access to your property** In [Search Console](https://search.google.com/search-console), open **Settings → Users and permissions** and add the service account's email address as a user with at least **Restricted** access. 3. **Add to infrapage** Open **Dashboard → Integrations → Google Search Console** and paste the entire JSON key. > **Note:** Credentials are stored encrypted in the `credentials` table, scoped to the user who added them, and are never sent to the browser. You can hold more than one per integration — several Polar organizations, or several connected X accounts — and pick which one a widget uses when you add it.