# EvoHub Docs > Guides and reference for EvoHub: on-call, uptime monitoring, status pages, retros, boards, docs sites, changelogs, billing and the API. Every page of EvoHub Docs, as Markdown. Index: https://docs-dev.evohub.io/llms.txt --- Source: https://docs-dev.evohub.io/introduction.md # EvoHub documentation EvoHub brings the operational side of running software into one place: being paged when something breaks, knowing when a service is down, telling customers about it, and improving afterwards. Everything lives in one organization, you invite your whole team for free, and you pay only for what you use. These guides explain how each part of EvoHub works and how to set it up. ## Start here :::cards{cols=2} ::card{title="What is EvoHub" icon="compass" href="/what-is-evohub"} The products, the console and the mobile app, and how they fit together. ::card{title="Create your account" icon="rocket" href="/create-your-account"} Sign up, verify your email and set up your first organization. ::card{title="Organizations and teams" icon="users" href="/organizations-and-teams"} Invite people, create teams and switch between organizations. ::card{title="Roles and permissions" icon="shield" href="/roles-and-permissions"} Decide who can see, change and delete what. ::: ## Products :::cards{cols=3} ::card{title="On-Call" icon="bell" href="/on-call-overview"} Schedules, escalation policies, alerts and incidents, with voice, push and email notifications. ::card{title="Integrations" icon="plug" href="/integrations-overview"} Send alerts from Prometheus, Grafana, Datadog, Zabbix, email and more. ::card{title="Status pages" icon="globe" href="/status-pages-overview"} Branded public status pages on your own domain. ::card{title="Uptime" icon="zap" href="/uptime-overview"} HTTP, keyword, TCP, ping and heartbeat monitors. ::card{title="Retro and Board" icon="layers" href="/retro-boards"} Retrospectives and Kanban boards for your team. ::card{title="Docs" icon="book-open" href="/docs-overview"} Product and API documentation on your own domain. ::card{title="Changelog" icon="flag" href="/changelog-overview"} Release notes with a What's new widget, feeds and email updates. ::card{title="Billing" icon="credit-card" href="/how-billing-works"} Usage-based pricing in EvoHub tokens, with a free monthly allowance. ::card{title="API" icon="code" href="/api-overview"} Automate EvoHub with API keys and scopes. ::: ## Get help If something in these guides is unclear or missing, write to **info@evosync.io**. You can also use the feedback buttons at the bottom of every page to tell us what to improve. --- Source: https://docs-dev.evohub.io/what-is-evohub.md # What is EvoHub EvoHub brings the tools an engineering team uses around operations and communication into one place: paging and on-call, uptime monitoring, public status pages, retrospectives, task boards, documentation sites and changelogs. This page explains what each product does, how the console is organized, and how you pay for it. ## The products | Product | What it is for | | --- | --- | | **On-Call** | Receive alerts from your monitoring tools, route them through escalation policies, and page the person on call by voice call, mobile push or email. Includes schedules, rotations, overrides, incidents and maintenance windows. | | **Uptime** | Monitor websites, APIs and hosts with HTTP, keyword, TCP and ping checks, and watch scheduled jobs with heartbeat monitors. A failing monitor can page On-Call. | | **Status Page** | Publish a public status page with components, incidents and scheduled maintenance, on your own domain, with email and feed subscriptions. | | **Retro** | Run team retrospectives on a live board with cards, votes, comments and action items. | | **Board** | Track work on Kanban-style boards with lists and cards, and an optional review gate. | | **Docs** | Write and publish documentation sites like this one, with review, GitHub sync and a custom domain. | | **Changelog** | Publish product updates as a changelog with an embeddable widget, feeds and email subscribers. | All products share one account, one organization, one set of members and teams, and one bill. ## The EvoHub console You use EvoHub through the console at [https://evohub.io](https://evohub.io). Sign in at [https://evohub.io/login](https://evohub.io/login). - The top bar holds the **service switcher** (On-Call, Uptime, Retro, Status Page, Board, Docs, Changelog) and the **organization switcher**, which lists every organization you belong to. - Each product has its own left-hand navigation. - Your avatar menu in the top bar has three entries: - **My Account**: your profile (**Settings**), **Notifications**, **Authentication** (two-factor authentication and connected Google or GitHub accounts), **Active Sessions** and **API Keys**. - **Organization**: **Members**, **Teams** and **Invitations**. Administrators also see **Org Settings**, **Roles**, **Organization Keys**, **Login Policies** and **Audit Logs**. - **Usage & Billing**: what your organization has used this month and what it will cost. What you see depends on your role. A link to a page you have no permission for is hidden, and the API refuses the request. See [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md). ## Organizations and teams Everything you create in EvoHub belongs to an **organization**. When you sign up, EvoHub creates a personal organization for you, and you can create more or be invited into others. Inside an organization, **teams** group people so that schedules, monitors, boards and retros can be scoped to them and roles can be granted to them. See [Organizations and teams](https://docs-dev.evohub.io/organizations-and-teams.md). ## Mobile apps The EvoHub mobile app is an on-call companion for iOS and Android. Use it to receive push notifications for alerts, acknowledge and resolve them, take over a shift, and see who is on call. Configuration, such as schedules, escalation policies and integrations, is done in the web console. - App Store: [EvoHub](https://apps.apple.com/us/app/evohub/id6799893875) - Google Play: [EvoHub](https://play.google.com/store/apps/details?id=io.evohub) ## Usage-based billing EvoHub does not charge per seat. Inviting your whole company costs nothing. You pay for what actually happens, such as voice calls placed, alerts received, uptime checks run and custom domains served. Usage is measured in **EvoHub tokens (EHU)**. Every organization gets a free monthly allowance of EHU, and most small teams stay within or close to it. You only pay for usage above the allowance, and only after you add a card or buy credit. See [How billing works](https://docs-dev.evohub.io/how-billing-works.md) and the [pricing page](https://evohub.io/pricing). ## The API Most things you can do in the console you can also do through the REST API at `https://evohub.io/api/v1`, authenticated with an API key. See the [API overview](https://docs-dev.evohub.io/api-overview.md). ## Related - [Create your account](https://docs-dev.evohub.io/create-your-account.md) - [Organizations and teams](https://docs-dev.evohub.io/organizations-and-teams.md) - [How billing works](https://docs-dev.evohub.io/how-billing-works.md) - [On-Call overview](https://docs-dev.evohub.io/on-call-overview.md) --- Source: https://docs-dev.evohub.io/create-your-account.md # Create your account This page walks you through signing up for EvoHub, verifying your email address and signing in for the first time. Signing up is free and does not ask for a credit card. ## Sign up Go to [https://evohub.io/register](https://evohub.io/register). You can sign up in two ways. :::tabs ::tab{title="Google or GitHub"} Select **Continue with Google** or **Continue with GitHub** and approve access with that provider. EvoHub creates your account from the name and email address the provider shares, and signs you in. ::tab{title="Email and password"} Fill in your name, email address and a password of at least 8 characters, then select **Create account**. EvoHub sends a verification link to your email address. See [Verify your email address](#verify-your-email-address). ::: By signing up you accept the [Terms of Service](https://evohub.io/terms) and the [Privacy Policy](https://evohub.io/privacy). > [!NOTE] > Each email address can hold one EvoHub account. If you see "An account with this email already exists", sign in instead, or reset your password from the sign-in page. ## Verify your email address If you signed up with email and password, you must verify your address before you can sign in. :::steps ### Open the verification email Look for the email from EvoHub and select the verification link. The link is valid for 24 hours. ### Sign in After the link confirms your address, sign in at [https://evohub.io/login](https://evohub.io/login). ::: If the email does not arrive, try to sign in: the sign-in page offers **Resend verification email**. If the link has expired, the page it opens offers **Resend verification** for your email address. Check your spam folder too. ## Sign in Sign in at [https://evohub.io/login](https://evohub.io/login) with **Continue with Google**, **Continue with GitHub**, or your email and password. - If you turned on [two-factor authentication](https://docs-dev.evohub.io/two-factor-authentication.md), EvoHub asks for the 6-digit code from your authenticator app after your password. - If your organization requires two-factor authentication and you have not set it up, EvoHub walks you through setting it up before you continue. - After 5 failed password attempts, the account is locked for 15 minutes. The lock applies even to the correct password, so wait it out or reset your password. ## Forgot your password On the sign-in page, select **Forgot password?**, enter your email address and select **Send reset link**. The link in the email lets you set a new password and is valid for 15 minutes. If you signed up with Google or GitHub and never set a password, you can create one later from **My Account → Settings** (**Create Password**). ## Your first organization When you sign up, EvoHub creates a personal organization for you, named after you (for example, "Ada Lovelace's Organization"), and makes you its **Owner**. You can start using every product in it right away. - To rename it, open **Organization → Org Settings** and change **Organization Name**. - To create another organization, for example one for your company, open the organization switcher in the top bar and choose **New Organization**. - If a colleague invited you, accept the invitation to join their organization. See [Organizations and teams](https://docs-dev.evohub.io/organizations-and-teams.md#join-through-an-invitation). > [!TIP] > Your personal organization cannot be deleted. Create a separate organization for your company so that you can manage and, if needed, delete it independently. ## Next steps :::cards{cols=2} ::card{title="Invite your team" icon="users" href="/organizations-and-teams"} Bring colleagues into your organization and group them into teams. ::card{title="Set up roles" icon="shield" href="/roles-and-permissions"} Decide who can change what. ::card{title="Turn on two-factor authentication" icon="lock" href="/two-factor-authentication"} Protect your account with an authenticator app. ::card{title="Start with On-Call" icon="phone" href="/on-call-overview"} Connect a monitoring tool and get paged. ::: ## Related - [What is EvoHub](https://docs-dev.evohub.io/what-is-evohub.md) - [Organizations and teams](https://docs-dev.evohub.io/organizations-and-teams.md) - [Two-factor authentication](https://docs-dev.evohub.io/two-factor-authentication.md) --- Source: https://docs-dev.evohub.io/organizations-and-teams.md # Organizations and teams An organization is the workspace everything in EvoHub belongs to: alerts, schedules, monitors, status pages, boards, docs sites, changelogs, members and the bill. Teams are groups of people inside an organization. This page explains how to work with both. ## Organizations Every organization is separate from every other. Data created in one organization is never visible from another, and each organization has its own members, roles, teams, API keys and billing. You can belong to several organizations, with a different role in each: - **Your personal organization** is created when you sign up. You are its Owner, and it cannot be deleted. - **Organizations you create.** Open the organization switcher in the top bar, choose **New Organization**, enter an **Organization name** and select **Create**. You become its Owner. One account can own up to 5 organizations. - **Organizations you are invited to.** See [Join through an invitation](#join-through-an-invitation). **My Account → Settings** lists every organization you belong to and your role in each. ## Switch organizations Open the organization switcher in the top bar and pick an organization. The console reloads in that organization: every product, member list and bill you see is now that organization's. If the organization you switch to requires two-factor authentication and you have not set it up, EvoHub asks you to set it up first. See [Two-factor authentication](https://docs-dev.evohub.io/two-factor-authentication.md). ## Rename an organization Open **Organization → Org Settings**, change **Organization Name** and select **Save**. This needs the **Organization Settings** write permission, which Owners and Admins have. ## Invite people Inviting is free: EvoHub never charges per member. :::steps ### Open the invite dialog Go to **Organization → Members** and select **Invite Member**. ### Enter the email address Type the address of the person you want to invite. ### Choose what they can do Pick one or more roles, and optionally one or more teams. A team brings the roles that team holds. You can only give roles whose permissions you hold yourself. If you cannot see roles, you choose between **Member** and **Viewer** (and **Admin**, if you are an administrator). ### Send the invitation EvoHub emails an invitation link. The person holds the roles and teams you picked from the moment they accept. ::: Inviting needs the **Members → Invite** permission. The default Member and Admin roles include it. ### Manage pending invitations **Organization → Invitations** lists pending invitations. From there you can resend an invitation email or cancel an invitation. An invitation is valid for 7 days; after that, send a new one. ### Join through an invitation Open the link in the invitation email. - **If you already have an EvoHub account**, sign in with the email address the invitation was sent to and accept. An invitation can only be accepted by the account with that exact email address. - **If you are new to EvoHub**, create your account from the invitation page. Because the invitation already proved you own the email address, you do not need to verify it again. After you accept, the organization appears in your organization switcher. ## Remove a member Go to **Organization → Members**, select **Remove** on the member's row and confirm. They lose access to the organization immediately. Removing members needs the **Members → Write** permission, which Owners and Admins have. The Owner cannot be removed, and you cannot remove yourself this way. A removed member's personal API keys for that organization stop working at the same time. See [API keys and scopes](https://docs-dev.evohub.io/api-keys-and-scopes.md). ## Teams A team is a named group of people in an organization. Teams do two things: - **Scope work.** Schedules, monitors, boards and retros can belong to a team. Work that belongs to a team is visible to that team's members; work that belongs to no team is visible across the organization. Owners and Admins see every team's work. - **Carry roles.** Give a team a role and everyone in the team holds that role for as long as they are in it. Remove someone from the team and they lose it. ### Create a team :::steps ### Open Teams Go to **Organization → Teams** and select **Create Team**. ### Name it Enter a **Name** and, optionally, a description of what the team owns. ### Add people and roles Open the team, add people from your organization, and give the team a role if its members should gain permissions through it. ::: Creating and editing teams needs the **Teams → Write** permission, which the default Member and Admin roles include. Everyone can see the teams they are in, even without permission to read all teams. > [!TIP] > A team that holds no role gives its members nothing extra. That is fine when you only use the team to scope work; give it a role when membership should also grant permissions. ## Delete an organization Only the Owner can delete an organization, and a personal organization cannot be deleted. :::steps ### Open Org Settings Go to **Organization → Org Settings**. The **Danger Zone** section is visible to the Owner. ### Confirm Select **Delete Organization**, type the organization's name, and select **Delete**. ::: > [!DANGER] > Deleting an organization is permanent. Its members lose access immediately, and its data in every product (On-Call, Uptime, status pages, retros, boards, docs sites and changelogs) is removed. This cannot be undone. Billing records that make up invoices already issued are kept, as described in [Privacy and your data](https://docs-dev.evohub.io/privacy-and-data.md). ## Related - [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md) - [Create your account](https://docs-dev.evohub.io/create-your-account.md) - [Privacy and your data](https://docs-dev.evohub.io/privacy-and-data.md) --- Source: https://docs-dev.evohub.io/roles-and-permissions.md # Roles and permissions Access in EvoHub is decided by organization roles. A role is a named set of permissions, and you give roles to people or to teams. This page explains the default roles, how to build your own, and the full list of permissions per product. ## How access works - Every permission check is made by EvoHub's servers, not only by the console. Hiding a button is a convenience; the API refuses the same action. - A refused action returns **403 Forbidden**. You stay signed in; you are simply not allowed to do that. See [Errors](https://docs-dev.evohub.io/errors.md). - What a person can do is the sum of every role they hold, directly or through their teams. - Roles belong to the organization. The same person can be an Admin in one organization and a Viewer in another. - Docs sites and changelogs have no roles of their own. Access to them comes from organization roles like everything else. ## Owner and the default roles Every organization starts with three default roles. They are marked **(default)** in role pickers. | Role | What it is for | | --- | --- | | **Owner** | The person who created the organization. Passes every check, and is the only one who can delete the organization. | | **Admin** | Runs the organization. A person who holds Admin passes every check, except deleting the organization. | | **Member** | The day-to-day work across every product: respond to alerts, manage schedules and monitors, run status pages, retros, boards, docs and changelogs. Reads everything else. | | **Viewer** | Read-only across every product. Sees everything, changes nothing. | What the default roles allow, product by product: | | Viewer | Member | Admin | | --- | --- | --- | --- | | Read everything (including billing usage and statements, and audit logs) | Yes | Yes | Yes | | Respond to alerts, edit schedules, escalation policies, maintenance, postmortems | — | Yes | Yes | | Manage On-Call integrations and voice and update templates | — | Yes | Yes | | Create and edit monitors, silences and notification channels | — | Yes | Yes | | Create and edit status pages, components, incidents, maintenance and subscribers | — | Yes | Yes | | Create and run retros, boards, docs sites and changelogs; write, publish and approve | — | Yes | Yes | | Invite people and manage teams | — | Yes | Yes | | Edit or remove members | — | — | Yes | | Delete a retro, archive a board, delete a docs site or a changelog | — | — | Yes | | Change org settings, roles, organization keys, login policies, billing (cards, credit, commitment) | — | — | Yes | > [!NOTE] > Deleting whole things that a team works in, such as a board, a retro, a docs site or a changelog, is Admin-only by default. Members can create, rename and configure them, but not make them disappear. You can hand this permission to someone deliberately with a custom role. Administrators can change what the Member and Viewer roles (and the Admin role) grant; everyone holding the role gets the change. Default roles cannot be renamed or deleted. > [!INFO] > When the Admin role is given to a **team** rather than to a person, it grants its listed permissions: everything a Member can do plus managing people and the delete permissions above. It does not grant editing roles or issuing organization keys. Only a person whose own role is Admin passes every check. ## Custom roles Build a custom role when the default roles are too broad or too narrow, for example "Can respond to alerts but not edit schedules" or "Billing only". :::steps ### Open Roles Go to **Organization → Roles** and select **Create Role**. ### Name and describe it Enter a **Name** and a **Description** that says what the role is for. ### Choose permissions Start from a ready-made level per product (see below), or under **Or start from a default role** pick Admin, Member or Viewer to copy its permissions, then adjust individual boxes. Use the search box to find a permission, or **Everything** to select all you are allowed to grant. ### Save and assign Save the role. Then give it to people (open a member in **Organization → Members** and add the role) or to a team (open the team in **Organization → Teams** and give it the role). ::: Rules that apply to every role: - **You can only grant what you hold.** Permissions you do not hold yourself are greyed out, and the server refuses a role that would give someone more than you have. Only an administrator can give someone the Admin role. - **A write brings its read.** Selecting a write permission also selects the read it needs, so a role never ends up able to edit something it cannot open. - **Creating and editing roles** needs the **Roles → Write** permission. Owners and Admins have it; you can give it to others with a custom role. ### Ready-made roles The role editor offers ready-made levels for each product. Clicking one fills in its permissions; every box stays editable afterwards. | Product | Levels | | --- | --- | | On-Call | **Stakeholder** (sees incidents and maintenance), **Observer** (sees everything, touches nothing), **Responder** (adds acknowledging and resolving), **Manager** (everything, including integrations, settings and the audit log) | | Uptime | **Viewer**, **Operator** (adds editing and silencing monitors), **Manager** (adds notification channels and the audit log) | | Status pages | **Viewer**, **Incident Manager** (posts incidents and maintenance, sets component status), **Page Manager** (everything, including domains, design and subscribers) | | Board | **Uses boards**, **Creates boards** (adds creating boards, settings, visibility and the review gate), **Manages boards** (adds archiving) | | Docs | **Docs writer** (drafts and change requests), **Docs reviewer** (approves and publishes), **Manages docs sites** (adds site settings, domain and deleting sites) | | Changelog | **Changelog writer**, **Changelog reviewer**, **Manages changelogs** | | Retro | **Takes part in retros**, **Runs retros** (adds creating, shaping and sharing retros), **Manages retros** (adds deleting) | | Organization | **Directory Viewer**, **Inviter**, **Organization Manager** (members, teams, roles, org settings, organization keys, audit log) | | Billing | **Billing Admin** (usage, statements, payment methods, billing profile, credit and commitment) | ## Permissions reference Permissions are grouped by product, as in the role editor. Each has an identifier, which you also see on API keys. | Product | Resource | Permissions | | --- | --- | --- | | Organization | Members | `identity:user:read`, `identity:user:write`, `identity:user:invite` | | | Teams | `identity:team:read`, `identity:team:write` | | | Roles | `identity:role:read`, `identity:role:write` | | | Organization Settings | `identity:org:write` | | | Organization Keys | `identity:apikey:read`, `identity:apikey:write` | | | Audit Log | `identity:audit:read` | | On-Call | Alerts | `oncall:alert:read`, `oncall:alert:write`, `oncall:alert:respond` (acknowledge, resolve, snooze, take over, assign) | | | Incidents | `oncall:incident:read`, `oncall:incident:write` | | | Schedules | `oncall:schedule:read`, `oncall:schedule:write` | | | Escalation Policies | `oncall:escalation:read`, `oncall:escalation:write` | | | Integrations | `oncall:integration:read`, `oncall:integration:write` | | | Maintenance Windows | `oncall:maintenance:read`, `oncall:maintenance:write` | | | Postmortems | `oncall:postmortem:read`, `oncall:postmortem:write` | | | Voice & templates | `oncall:settings:write` | | | Audit Log | `oncall:audit:read` | | Uptime | Monitors | `uptime:monitor:read`, `uptime:monitor:write` | | | Incidents | `uptime:incident:read` | | | Silences | `uptime:silence:read`, `uptime:silence:write` | | | Notification Channels | `uptime:channel:read`, `uptime:channel:write` | | | Audit Log | `uptime:audit:read` | | Status pages | Pages | `status:page:read`, `status:page:write` | | | Components | `status:component:read`, `status:component:write` | | | Incidents | `status:incident:read`, `status:incident:write` | | | Maintenance | `status:maintenance:read`, `status:maintenance:write` | | | Subscribers | `status:subscriber:read`, `status:subscriber:write` | | | Audit Log | `status:audit:read` | | Retro | Boards | `retro:board:read`, `retro:board:write`, `retro:board:delete` | | | Cards | `retro:card:read`, `retro:card:write` | | | Action Items | `retro:action:read`, `retro:action:write` | | | Audit Log | `retro:audit:read` | | Board | Boards | `board:board:read`, `board:board:write`, `board:board:delete` (archive) | | | Lists | `board:list:read`, `board:list:write` | | | Cards | `board:card:read`, `board:card:write` | | | Board Members | `board:member:write` | | | Review Gate | `board:review:write` | | Docs | Sites | `docs:site:read`, `docs:site:write`, `docs:site:delete` | | | Pages | `docs:page:write`, `docs:page:publish` (publish and approve) | | Changelog | Sites | `changelog:site:read`, `changelog:site:write`, `changelog:site:delete` | | | Entries | `changelog:entry:write`, `changelog:entry:publish` (publish and approve) | | Billing | Usage | `billing:usage:read` | | | Statements | `billing:invoice:read` | | | Payment Methods | `billing:payment:read`, `billing:payment:write` | | | Billing Profile | `billing:profile:read`, `billing:profile:write` | | | Credit | `billing:credit:write` | | | Commitment | `billing:commitment:write` | | Support | Audit Log | `support:audit:read` | A few things need no permission at all: every member can see who else is in the organization, see the teams they belong to, open support tickets, and manage their own profile, notifications, sessions, two-factor authentication and personal API keys. ## Related - [Organizations and teams](https://docs-dev.evohub.io/organizations-and-teams.md) - [API keys and scopes](https://docs-dev.evohub.io/api-keys-and-scopes.md) - [Errors](https://docs-dev.evohub.io/errors.md) --- Source: https://docs-dev.evohub.io/on-call-overview.md # On-Call overview EvoHub On-Call receives alerts from your monitoring tools and makes sure the right person hears about them. This page explains the building blocks and how an alert travels from your monitoring tool to a person's phone. ## How an alert reaches someone ```mermaid flowchart LR A[Monitoring tool] -->|webhook or email| B[Integration] B --> C[Alert] C --> D[Escalation policy] D -->|step 1, 2, 3…| E[Person or schedule] E --> F[Voice call / push / email] ``` 1. **An integration receives the alert.** Each integration has its own webhook URL (or, for the email integration, its own inbound address). Your monitoring tool sends to it. 2. **EvoHub opens an alert.** The integration's parser reads the tool's payload and creates an alert with a title, severity, description and labels. If an open alert with the same fingerprint already exists, EvoHub counts a repeat on that alert instead of opening a new one. 3. **The integration's escalation policy starts.** The policy is a list of steps. Each step names a person or a schedule, how long to wait, and optionally how to notify. 4. **A schedule answers "who is on call now".** When a step targets a schedule, EvoHub looks at the schedule's layers, rotations and overrides to find the person on duty at that moment. 5. **The person is notified.** By default EvoHub uses the channels the person turned on for themselves (voice call, mobile push, email). A step can force one channel instead. 6. **Someone responds.** Acknowledging, taking over or resolving the alert stops the escalation. If nobody responds, the next step runs. 7. **The source recovers.** For most integrations, the recovery notification resolves the matching alert automatically. ## The building blocks | Concept | What it is | Where | | --- | --- | --- | | **Integration** | A connection to one monitoring tool, with its own URL and key, attached to an escalation policy. | **On-Call → Integrations** | | **Alert** | One problem reported by a tool (or created by hand). Has a status: triggered, acknowledged, resolved or suppressed. | **On-Call → Alerts** | | **Escalation policy** | Who is notified, in what order, after how long, and whether the chain repeats. | **On-Call → Escalation Policies** | | **Schedule** | Who is on call when. Made of layers; each layer is a rotation of people. | **On-Call → Schedules** | | **Override** | A temporary replacement on a schedule, for example to cover a vacation. | A schedule's **Overrides** section | | **Incident** | A human-declared, coordinated response with a timeline, which you can publish to a status page and write a postmortem for. | **On-Call → Incidents** | | **Maintenance** | A planned window during which On-Call pages nobody. | **On-Call → Maintenance** | On-Call does not have a separate "service" object. An alert is routed by the integration it arrived through, and that integration's escalation policy decides who is paged. ## Set up On-Call for the first time :::steps ### Turn on your own notifications Open **My Account → Notifications**. Turn on **Mobile Push** (sign in to the EvoHub mobile app first), **Phone Call** (verify your phone number in **Settings** first) and **Email**. See [Notifications](https://docs-dev.evohub.io/notifications.md). ### Create a schedule Go to **On-Call → Schedules → New Schedule**, choose the timezone your team works in, and add a layer with the people who rotate. See [Schedules and rotations](https://docs-dev.evohub.io/schedules-and-rotations.md). ### Create an escalation policy Go to **On-Call → Escalation Policies → New Policy**. Make step 1 target the schedule, and add a second step for a backup. See [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md). ### Connect a monitoring tool Go to **On-Call → Integrations → + Add Integration**, pick your tool, select the escalation policy and copy the webhook URL into the tool. See [Integrations overview](https://docs-dev.evohub.io/integrations-overview.md). ### Send a test alert Trigger a test from your monitoring tool, or create one with **On-Call → Alerts → New Alert** and choose your escalation policy. Check that the right person is notified and can acknowledge it. ::: > [!TIP] > Moving from Opsgenie? The built-in importer creates schedules, escalation policies and integrations from your Opsgenie account. See [Migrate from Opsgenie](https://docs-dev.evohub.io/migrate-from-opsgenie.md). ## Who can do what On-Call follows your organization roles. By default, Owners, Admins and Members can respond to alerts and configure schedules, escalation policies and integrations; Viewers can see everything but change nothing. Custom roles can grant individual On-Call permissions, such as responding to alerts without editing schedules. People who appear in rotations, escalation steps and overrides must hold the permission to respond to alerts. See [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md). ## What counts toward usage On-Call is billed by usage, not per seat. Each new alert counts as one ingested alert; repeats of an alert that is still open do not. Voice calls are metered as well. See [How billing works](https://docs-dev.evohub.io/how-billing-works.md) for current rates. ## Related - [Alerts and incidents](https://docs-dev.evohub.io/alerts-and-incidents.md) - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) - [Integrations overview](https://docs-dev.evohub.io/integrations-overview.md) - [Mobile app](https://docs-dev.evohub.io/mobile-app.md) --- Source: https://docs-dev.evohub.io/schedules-and-rotations.md # Schedules and rotations A schedule answers one question: who is on call right now? Escalation policies point at schedules, so the person paged changes automatically as the rotation moves on. This page shows how to build schedules, layers and rotations. ## How a schedule is put together - A **schedule** has a name and a **timezone**. Every hour on the schedule — handover times, shift windows, the calendar — is read on that clock. - A schedule has one or more **layers**. Each layer is a **rotation**: an ordered list of people who take turns. - Each layer has a **priority**. Lower numbers come first. When several layers cover the same moment, the layer with the lowest priority number is the one that answers "who is on call", and the others are the depth behind it (see [Layers and escalation](#layers-and-escalation)). - **Overrides** replace whoever the layers say is on call for a period of time. See [Overrides and take over](https://docs-dev.evohub.io/overrides-and-takeover.md). ## Create a schedule :::steps ### Open Schedules Go to **On-Call → Schedules** and click **New Schedule**. ### Name it and pick a timezone Enter a **Schedule Name** (for example "Platform On-Call") and choose the **Timezone** your team works in. ### Add a layer Open the schedule and click **Add Layer**. Fill in the fields described below and save. ### Add people to the rotation In the layer, use **Add member…** to add people in the order they take turns. Use the arrows to move someone earlier or later, and the remove button to take them out. ::: Only people who are allowed to respond to alerts can be added to a rotation. ## Layer settings | Field | What it does | | --- | --- | | **Layer Name** | A label, such as "Primary" or "Backup". | | **Rotation Type** | **Daily**, **Weekly**, or **Custom (every N days)**. Custom asks how many days each person holds the rota (**Rotate every** … **days**, 1 to 90). | | **Starts on** | The day the rota begins. The first person in the list holds it first. This can only be set when you add a layer. | | **When it is on call** | **All day** (around the clock, handing over at a set time) or **A shift** (part of the day, with other rotas covering the rest). | | **Hands over at** | For an all-day layer: the time of day the rota passes to the next person. Left empty, the handover happens at whatever time the rotation started. | | **Priority** | Lower comes first. Within one shift, the lowest priority is paged first and the next is the backup. | ### Shifts Choose **A shift** to make a layer cover only part of the day: - Set the shift's start and end time, for example 08:00 to 16:00. An end time earlier than the start runs past midnight (22:00 to 06:00 is a night shift). The end time is exclusive, so 08:00–16:00 and 16:00–00:00 meet without overlapping. - Optionally pick **Only on these days**. With no days selected the shift runs every day. Days you leave out are off duty: nobody on this layer is paged then. This is which days the shift covers — not how often it changes hands. A team working three eight-hour shifts uses one schedule with three layers whose windows do not overlap. Keep them in one schedule rather than three, so a single escalation step can target "whoever is on call now". Once any layer on a schedule is a shift, the schedule shows a **Coverage over a day** bar. Hours that no layer covers are marked, with the message "Nobody is on call for part of the day". An alert arriving in an uncovered hour reaches no one on that schedule, and the alert's timeline says so ("No one was on call — this step reached nobody"). Leaving hours uncovered is allowed — just make sure it is intentional. ## Read the schedule The schedule page shows: - **Currently On-Call**: who holds the schedule right now and until when, on the schedule's clock. - A calendar of who is on call each day per layer, for **1W**, **2W** or **4W**, with arrows to move between weeks and **Today** to jump back. - **Layers**, with their rotation type, shift window and members. - **Overrides**, with the active one highlighted. **On-Call → Schedules** lists every schedule with **On-Call Now** for each. An active schedule that no escalation policy uses is flagged with **No escalation policy**, because nobody will be paged from it. ## Change the schedule timezone Click the timezone button next to the schedule name to open **Schedule timezone**. Changing it does not merely relabel the schedule: shift windows and handover times move by the difference between the two zones, and whoever is on call right now may change. Nothing is deleted. The dialog shows the same moment on both clocks before you save. ## Layers and escalation When an escalation step targets a schedule, it can target one specific layer or **Any layer (round-robin)**: - **A specific layer** pages whoever that layer has on call at the moment the step runs. - **Any layer (round-robin)** uses the step's position in the policy to pick among the layers that are on duty at that moment, in priority order: step 1 pages the first layer, step 2 the second, and so on, wrapping around. On a schedule with a primary and a backup layer, step 1 reaches the primary and step 2 the backup. Layers that are off duty (outside their shift) are skipped, so a step never pages a rota that is not working. Overrides always win: while an override is active, every step that targets the schedule reaches the override person. ## Delete a schedule or layer Use the trash icon on a layer to delete it, or the delete action in the schedules list to delete the whole schedule. Escalation steps that targeted a deleted schedule stop reaching anyone, so update your escalation policies first. ## Related - [Overrides and take over](https://docs-dev.evohub.io/overrides-and-takeover.md) - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) - [Migrate from Opsgenie](https://docs-dev.evohub.io/migrate-from-opsgenie.md) --- Source: https://docs-dev.evohub.io/overrides-and-takeover.md # Overrides and take over Plans change: someone goes on vacation, gets sick, or is already working on the problem that just paged a colleague. EvoHub handles this in two ways. An **override** changes who is on call on a schedule for a period of time. **Take over** makes a single alert yours. ## Overrides An override puts a replacement person on a schedule from a start time to an end time. While it is active, the override wins over every layer of the schedule: anyone who asks the schedule "who is on call" — the **Currently On-Call** banner, the mobile app and every escalation step that targets the schedule — gets the override person. ### Add an override in the console :::steps ### Open the schedule Go to **On-Call → Schedules** and open the schedule. ### Click Add Override In the **Overrides** section, click **Add Override**. ### Fill in the details Choose the **Replacement User**, the **Start Time** and **End Time**, and optionally a **Reason (optional)** such as "Vacation cover". ### Save The override appears in the list, marked **Active** while it is in effect, with the time range shown on the schedule's clock. ::: To remove an override, click the trash icon on its row. The schedule goes back to its normal rotation immediately. Only people who are allowed to respond to alerts can be chosen as a replacement. Adding and removing overrides needs permission to edit schedules (Owners, Admins and Members have it by default). ### Take or give a shift from the mobile app In the EvoHub mobile app, open the **On-Call** tab, choose **Schedule**, tap a day, and choose who covers it under **Assign coverage to** in the **Override shift** sheet. Choose yourself to take the shift, or a teammate to hand it over. The app creates an override for that whole day (midnight to midnight on your phone's clock). If the day is already covered by an override, the sheet shows it under **Currently overridden** with an option to remove it. ### Things to know - If two overrides overlap, the one that started first is used for the overlapping time. To change who covers a period, delete the old override rather than adding a second one on top. - An override covers the whole schedule, not one layer. On a schedule with a primary and a backup layer, the override person is reached by every step that targets that schedule. - Overrides are not copied by the Opsgenie importer. Recreate upcoming ones by hand after an import. ## Take over an alert **Take over** says "I've got this" for one alert. It: 1. Assigns the alert to you. 2. Acknowledges it, if it was still triggered. 3. Stops its escalation, so nobody else is paged for it. 4. Records **Taken over by** you (and from whom, if it was assigned to someone else) on the alert's timeline. You can take over an alert that is assigned to someone else or to nobody. You cannot take over a resolved alert. Where to find it: - **Console**: open the alert and click **Take over**. The button is hidden when the alert is already assigned to you. - **Bulk**: on **On-Call → Alerts**, select open alerts and click **Take over N**. - **Mobile app**: open the alert and tap **Take over**. - **Slack**: in the alert message's **…** menu, choose **Take over** (when Slack is connected; see [Slack](https://docs-dev.evohub.io/slack.md)). Taking over needs permission to respond to alerts, which Owners, Admins and Members have by default. ### Take over or redirect? | You want to | Use | | --- | --- | | Own the alert yourself and stop paging others | **Take over** | | Hand the alert to a different team's escalation policy | **Redirect** — the current escalation stops and the selected policy is paged from the top | | Stop being paged for it while you investigate, without taking ownership | **Acknowledge** | See [Alerts and incidents](https://docs-dev.evohub.io/alerts-and-incidents.md) for all alert actions. ## Related - [Schedules and rotations](https://docs-dev.evohub.io/schedules-and-rotations.md) - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) - [Mobile app](https://docs-dev.evohub.io/mobile-app.md) --- Source: https://docs-dev.evohub.io/escalation-policies.md # Escalation policies An escalation policy is the chain EvoHub follows when an alert arrives: who to notify first, how long to wait for a response, and who to try next. Every integration points at one escalation policy. This page explains each setting and how the chain runs. ## How a policy runs 1. An alert arrives through an integration and the integration's policy starts. 2. **Step 1** runs after its delay (which can be **0 (now)**). It notifies its target. 3. If nobody acknowledges, takes over or resolves the alert, the next step runs after its own delay. Each step's delay counts from the step before it. 4. After the last step, the policy either stops or, if **Repeat escalation** is on, waits and starts again from step 1. The escalation stops as soon as someone: - **acknowledges** the alert (unless the policy has an acknowledgement timeout, see below), - **takes over** the alert, - **resolves** the alert, or the source resolves it, - **suppresses** it from a voice call. **Redirect** on an alert stops the current escalation and starts the selected policy from the top. See [Alerts and incidents](https://docs-dev.evohub.io/alerts-and-incidents.md). An alert that arrives through an integration with no escalation policy is recorded, but nobody is paged. ## Create a policy :::steps ### Open Escalation Policies Go to **On-Call → Escalation Policies** and click **New Policy**. ### Name it Enter a **Policy Name**, for example "Platform — default". ### Add steps Under **Escalation Steps**, set when each step notifies (**Notify after** … **min**) and choose its target: a **Schedule** (and optionally one of its layers) or a **User**. Add more steps with the add button. ### Choose whether to repeat Optionally tick **Repeat escalation** and choose how many times and how long to wait between cycles. ### Create Save the policy. Then open it and click **Edit** for the full set of options described below. ::: The **New Policy** dialog offers the common settings. The policy's own page (**Edit**) adds per-step notification methods, retries, webhook targets, Slack channels and the acknowledgement timeout. ## Step settings | Setting | Options | What it does | | --- | --- | --- | | **Notify after** | First step: **0 (now)**, 1, 2, 3, 5, 10, 15, 30 or 60 minutes. Later steps: 1 to 60 from the same list. | How long to wait before this step runs, counted from the previous step (the first step from when the alert opened). | | **via** | **Default**, **Push**, **Call**, **Email** | **Default** uses each person's own enabled channels ("their preferences"). Choosing a method forces that one channel for this step. | | **Try** | **once** up to **10 times**, waiting 1 to 60 **min between** | Notifies the same target again before the policy moves on. "Call three times, two minutes apart, then wake the backup" is one step with **Try 3 times, waiting 2 min between**. | | Target | **Schedule**, **User**, **Webhook** | Who or what this step reaches. | | **Chat** → **Slack** | **Send to Slack** | Also posts the alert to a Slack channel when this step runs, in addition to paging the target. Available once Slack is connected. See [Slack](https://docs-dev.evohub.io/slack.md). | ### Targets - **Schedule** pages whoever is on call on that schedule when the step runs. Pick a specific layer, or **Any layer (round-robin)** to let the step's position choose among the layers on duty. See [Layers and escalation](https://docs-dev.evohub.io/schedules-and-rotations.md#layers-and-escalation). If nobody is on call at that moment, the step reaches nobody and the alert's timeline says "No one was on call — this step reached nobody". - **User** pages one person directly. Only people who can respond to alerts can be selected. - **Webhook** sends an HTTP `POST` to the URL you enter, so another system can react. The body is JSON: ```json { "alert_id": "6f1c2a4e-1b7d-4c55-9a0e-2f8b7d1c9e10", "title": "Database primary is down", "severity": "critical", "summary": "Replication lag over 30s, primary not answering on 5432", "step": 1 } ``` `summary` carries the alert's description. The request has a 10-second timeout and is sent with `Content-Type: application/json` and `User-Agent: EvoHub-OnCall/1.0`. ### Notification method and personal schedules When a step forces a method, that channel is used even if the person has turned it off in their own preferences. A person's **Notification schedule** still applies, though: if their schedule does not allow the forced channel at that time, EvoHub uses the channels their schedule does allow instead, and if none are allowed, nothing is sent to them and the escalation carries on. See [Notifications](https://docs-dev.evohub.io/notifications.md). SMS is not available as a method. A step saved with SMS in the past is shown as **SMS (no longer available)** and is reset to **Default** when you edit the policy. ## Policy settings ### Repeat escalation Turn on **Repeat escalation** to run the whole chain again when the last step has run and nobody responded. Choose **1** to **5** times and how many minutes to **wait** between cycles (1, 2, 3, 5, 10, 15, 30 or 60). ### If acknowledged but not resolved By default, acknowledging an alert ends its escalation. That can leave an alert open with nobody watching it if the person who acknowledged gets pulled away. To guard against that, set **If acknowledged but not resolved in** to a time (5 minutes to 4 hours). If the alert is still unresolved when the time runs out, the escalation resumes and the timeline records it. Choose **escalate again, up to** 1 to 5 times so it cannot ring all night, and choose **and call**: - **Someone else** — skips whoever acknowledged and looks for another pair of hands. - **Whoever acknowledged, then someone else** — reminds them once. If that goes unanswered, the next repeat escalates past them. **Never** (the default) turns this off. The setting is read each time, so turning it off also stops the reminders for alerts that are already acknowledged. ## Responding by phone During a voice call, the person called can press **4** to acknowledge, **3** to escalate to the next step right away, or **6** to suppress the alert. See [Notifications](https://docs-dev.evohub.io/notifications.md#voice-calls). ## Delete a policy Open the policy and delete it; the **Delete Policy** dialog asks you to confirm. Escalations of the policy that are still running stop. > [!WARNING] > Before you delete a policy, open **On-Call → Integrations** and point every integration that uses it at another policy (or **— None —**). An integration left pointing at a deleted policy can no longer open alerts. ## Related - [Schedules and rotations](https://docs-dev.evohub.io/schedules-and-rotations.md) - [Notifications](https://docs-dev.evohub.io/notifications.md) - [Alerts and incidents](https://docs-dev.evohub.io/alerts-and-incidents.md) - [Integrations overview](https://docs-dev.evohub.io/integrations-overview.md) --- Source: https://docs-dev.evohub.io/alerts-and-incidents.md # Alerts and incidents An **alert** is one problem reported by a monitoring tool (or raised by hand). An **incident** is a coordinated response your team declares, with a timeline, a public status page entry and a postmortem. This page covers both. ## Alert statuses | Status | Meaning | | --- | --- | | **Triggered** | New and unanswered. The escalation policy is running. | | **Acknowledged** | Someone is on it. The escalation stops (or pauses, if the policy has an acknowledgement timeout). | | **Resolved** | Over. Resolved by a person, or automatically by the source's recovery notification. | | **Suppressed** | Silenced from a voice call (key 6). Escalation stops. | "Open" means triggered or acknowledged. ## The Alerts page **On-Call → Alerts** lists your organization's alerts. - Counters at the top show **Total**, **Triggered**, **Acknowledged** and **Resolved**. - The status filter offers **All**, **Open** (the default), **Triggered**, **Acknowledged**, **Resolved** and **Suppressed**. - **Assigned to me** narrows the list to alerts assigned to you; it combines with the status filter. - **Search title or source…** searches as you type. - **Filters** adds **Severity**, **Source**, **Assignee**, **Escalation policy**, **Integration** and **Created between**. - Expand a row to see its description, custom details (labels) and fingerprint, or click **View full alert**. Each integration's detail dialog also has **View this integration's alerts**, which opens this list filtered to that integration. ## Respond to an alert Open an alert to see its details, labels and timeline. The actions available depend on its status and your permissions: | Action | What it does | | --- | --- | | **Acknowledge** | Marks the triggered alert as acknowledged and stops (or pauses) the escalation. | | **Take over** | Assigns the alert to you, acknowledges it if needed and stops the escalation. See [Overrides and take over](https://docs-dev.evohub.io/overrides-and-takeover.md#take-over-an-alert). | | **Redirect** | Hands the alert to another escalation policy. Its current escalation stops and the selected policy is paged from the top. An acknowledged alert goes back to triggered so the new policy actually pages. | | **Resolve** | Closes the alert and stops the escalation. | | **Add a comment…** | Adds a note to the alert's timeline under your name. | Acknowledging an alert that is already acknowledged, or resolving one that is already resolved, does nothing and is not an error. A resolved alert cannot be acknowledged again. Responding (acknowledge, take over, resolve, notes) needs permission to respond to alerts. Redirecting and creating alerts by hand need permission to write alerts. Owners, Admins and Members have both by default. ### Bulk actions Tick alerts on the Alerts page (or **Select all open alerts**) to act on many at once: **Acknowledge N** (triggered alerts), **Take over N** (open alerts) and **Resolve N**. Each alert is processed on its own, so one that cannot be changed does not stop the rest. ## The alert timeline Every alert keeps a timeline of what happened to it, including: - **Alert created**, **Acknowledged**, **Resolved**, **Taken over by** …, **Redirected to another policy** - Notifications sent: **Voice call to** …, **Push notification to** …, **Email sent to** …, and the call result (for example **Not answered** or **Line busy**) - **Escalated to next step** when someone pressed 3 on a call - **No one was on call — this step reached nobody** - Notifications held back by a person's notification schedule, by a maintenance window, or because your organization has used its free allowance with no payment method on file - **Retriggered ×N** when the source sent the same alert again - Comments, including those added from Slack When someone asks "why wasn't I paged?", the alert's timeline is the place to look. ## Deduplication and auto-resolve Every alert has a **fingerprint** that identifies the problem. Integrations derive it from the source's own identifiers (for example, Alertmanager's fingerprint, a Zabbix host and trigger, or a PRTG sensor ID). - If an alert arrives while an alert with the same fingerprint is still open (triggered or acknowledged), EvoHub does not open a second one. It records **Retriggered** on the existing alert instead, and nobody is paged again. - Once the alert is resolved, the next alert with that fingerprint opens a new alert. - When the source reports recovery, EvoHub resolves the open alert with the matching fingerprint. Each integration page says how its source reports recovery. ## Create an alert by hand Click **New Alert** on the Alerts page, enter a **Title**, choose a **Severity** (**Critical**, **High**, **Medium**, **Low** or **Info**), optionally choose an escalation policy and add a **Description**. If you choose a policy, it starts paging immediately — this is a convenient way to test a policy end to end. ## Maintenance windows While a maintenance window is in progress, On-Call pages nobody in your organization. Alerts are still recorded, and each skipped notification shows on the alert's timeline as suppressed by maintenance. Schedule windows in **On-Call → Maintenance** (**Schedule maintenance**); a window can also be shown on a status page and mute uptime monitors. See [Incidents and maintenance on status pages](https://docs-dev.evohub.io/status-incidents-and-maintenance.md) and [Uptime alerts and silence windows](https://docs-dev.evohub.io/uptime-alerts-and-silence-windows.md). ## Incidents An incident is a record your team declares for a significant problem. Incidents do not page anyone by themselves; they are where you coordinate, communicate and learn. ### Declare an incident Go to **On-Call → Incidents → New Incident** and enter: - **Title** and an optional **Summary** (you can edit both later). - **Severity**: **Critical**, **Major**, **Minor** or **None**. - **It already happened**: tick this to record an incident that has already ended, with its own **Started** and **Ended** times. Nobody is paged, and if you publish it to a status page it shows on the days it happened. Incidents are numbered (**INC-1**, **INC-2**, …). ### Work an incident An incident moves from **investigating** to **identified** (**Mark Identified**) to **resolved** (**Resolve**). On the incident page you can: - Edit the title, summary and severity. - Add timeline notes (**Add a timeline note…**), and edit or delete them later. - See the **Commander** and when it **Started**. ### Publish to a status page Click **Publish to status page**, choose the **Status page**, the **Public impact** (**None**, **Minor**, **Major**, **Critical**), the **Affected components**, and an optional **Public message**. Once published, the incident shows **On status page**, and EvoHub keeps the two in sync: marking it identified or resolved, and adding, editing or deleting timeline notes, update the public incident too. **Unpublish** removes it from the status page. Publishing an incident that has already ended puts it on the page's history on the days it happened; subscribers are not emailed. You need a status page first — see [Status pages overview](https://docs-dev.evohub.io/status-pages-overview.md). ### Postmortem After an incident is resolved, click **Postmortem** to write one. Start from a template (**Standard**, **5 Whys** or **Brief**) and fill in **Summary**, **Root cause**, **Contributing factors**, **Timeline**, **Action items** and **Lessons learned**. **Save internal** keeps it inside EvoHub; **Publish publicly** shows it on the incident's public status page entry (when the incident is published there). ## Related - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) - [Integrations overview](https://docs-dev.evohub.io/integrations-overview.md) - [Incidents and maintenance on status pages](https://docs-dev.evohub.io/status-incidents-and-maintenance.md) - [Mobile app](https://docs-dev.evohub.io/mobile-app.md) --- Source: https://docs-dev.evohub.io/notifications.md # Notifications When an escalation step reaches you, EvoHub notifies you on the channels you have turned on. Your notification settings are personal: they belong to you, not to an organization, and apply in every organization you are a member of. This page explains the channels, your notification schedule and voice calls. ## Channels EvoHub notifies people by: | Channel | Shown as | Requirement | | --- | --- | --- | | Voice call | **Phone Call** | A verified phone number. | | Mobile push | **Mobile Push** | You are signed in to the EvoHub mobile app on at least one iOS or Android device, with notifications allowed. | | Email | **Email** | Your account email. Turned on automatically. | > [!NOTE] > EvoHub does not currently send SMS. Use push or voice calls for urgent paging. ## Turn your channels on :::steps ### Open Notifications Click your avatar and open **My Account → Notifications**. ### Verify your phone number If you see **Phone number not verified**, click **Verify it in Settings →** and verify your number. Once it is verified, **Phone Call** can be turned on, and it shows the number EvoHub will call. ### Install the mobile app Install EvoHub from the [App Store](https://apps.apple.com/us/app/evohub/id6799893875) or [Google Play](https://play.google.com/store/apps/details?id=io.evohub), sign in, and allow notifications. **Mobile Push** then shows **Sends to your mobile devices**. ### Turn the toggles on Switch on each channel you want to be reached on. ::: With an escalation step set to **Default**, you are notified on every channel you turned on. A step can also force one channel (**Push**, **Call** or **Email**) for everyone it reaches, even if they turned that channel off. See [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md#step-settings). ## Notification schedule The **Notification schedule** limits which channels may reach you at which times — for example, no phone calls during working hours, or only push at night unless the alert is critical. - Pick the **Timezone** your rules are written in. - Click **Add rule** for each time window (up to 20). Each rule has: - the days it applies to, - **From** and **to** times, - **Allowed:** the channels that may reach you in that window (choosing none means nothing reaches you then), - **Critical alerts still use all my channels**, to let critical alerts through regardless. - Click **Save schedule**. How rules are read: - **The first rule that matches wins.** Use the arrows to put more specific rules first. - Outside every rule, you are reached on every channel you turned on. - A rule's days are the days its window *starts*. A Monday 23:00–08:00 rule covers Monday night and early Tuesday morning, not early Monday morning. - An end time before the start runs past midnight. The same start and end time (for example 00:00–00:00) means the whole day. The end time is exclusive. - A rule only narrows the channels you turned on above; it never turns on a channel you switched off. When a step forces a channel your schedule does not allow at that moment, EvoHub uses the channels your schedule does allow instead. If none are allowed, nothing is sent to you and the escalation continues to its next step as it would for anyone who did not respond. Either way, the alert's timeline records that your notification schedule held a notification back. ## Voice calls A voice call announces the alert and lets you act on it with the keypad: | Key | Action | | --- | --- | | **4** | Acknowledge the alert. The escalation stops (or pauses, if the policy has an acknowledgement timeout). | | **3** | Escalate to the next step now. | | **6** | Suppress the alert. The escalation stops. | If you do not press a key, the escalation continues as usual. The result of each call (for example connected, not answered or line busy) is recorded on the alert's timeline. When several alerts reach you within a short time, EvoHub combines them into one call that names how many alerts are waiting; pressing 4 acknowledges all of them. Push notifications are still sent for each alert as it arrives. ### Customize what the call says Organizations can replace the sentence that names the alert with their own wording in **On-Call → Voice Template**. The greeting and the key instructions always stay. - Write the **Template** with fields in braces, for example `{customer} customer, {service} service.` Insert fields from **About the alert** (available on every alert, such as `{alert.title}` and `{alert.severity}`) or **Sent by your integrations** (labels your alerts have carried). - A sentence that names a field the alert does not carry is left out of the call, so a call never reads out an empty gap. - The template is limited to 200 characters. Commas and full stops become pauses. - **What the call says** previews the spoken text and warns about fields no alert has sent. - An empty template restores the default call. When several alerts are combined into one call, the template is not used. Editing the voice template needs the On-Call settings permission (Owners, Admins and Members by default). ## Push notifications Push notifications go to every device where you are signed in to the EvoHub mobile app. Tap one to open the alert. On iPhone, a push also offers an **Acknowledge** button so you can acknowledge without opening the app. See [Mobile app](https://docs-dev.evohub.io/mobile-app.md). ## Email Alert emails go to your account email address. ## When notifications are not sent Check the alert's timeline. Common reasons: - **No one was on call** on the schedule the step targeted. - A **maintenance window** was in progress, so On-Call paged nobody. - Your **notification schedule** held the channel back. - The channel is not set up: phone number not verified, or no device signed in to the mobile app. - Your organization has used its free monthly allowance and has no payment method on file. Alerts are still recorded, but notifications are held back and the timeline says why. See [How billing works](https://docs-dev.evohub.io/how-billing-works.md). ## Test your setup Create an alert by hand with **On-Call → Alerts → New Alert** and choose an escalation policy whose first step targets you (or a schedule you are on). Check that your phone rings, your push arrives and you can acknowledge. ## Related - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) - [Mobile app](https://docs-dev.evohub.io/mobile-app.md) - [Alerts and incidents](https://docs-dev.evohub.io/alerts-and-incidents.md) --- Source: https://docs-dev.evohub.io/mobile-app.md # Mobile app The EvoHub mobile app is your on-call companion. It is built for responding when you are away from your desk: getting paged, acknowledging, taking over and resolving alerts, and covering a shift. Configuration — schedules, escalation policies, integrations — stays in the web console. ## Get the app - iPhone: [EvoHub on the App Store](https://apps.apple.com/us/app/evohub/id6799893875) - Android: [EvoHub on Google Play](https://play.google.com/store/apps/details?id=io.evohub) Sign in with your email and password, or with **Continue with Google** or **Continue with GitHub**. If your organization requires two-factor authentication, set it up in the web console first; the app then asks for your code when you sign in. After you sign in, allow notifications. Your device is registered for push, and **Mobile Push** turns on in your notification settings. See [Notifications](https://docs-dev.evohub.io/notifications.md). ## What you can do ### Alerts The **On-Call** tab lists your organization's alerts, filtered by **Open**, **Triggered**, **Ack**, **Resolved** or **All**. Open an alert to see its details, labels and timeline, and to act on it: - **Acknowledge** — you are on it; escalation stops. - **Take over** — assign it to yourself, acknowledge it and stop escalation. - **Redirect** — hand it to another escalation policy, which is paged from the top. - **Resolve** — close it. - **Add a note…** — write on the alert's timeline. The buttons you see depend on the alert's status and your permissions. ### Push notifications Each alert that reaches you arrives as a push notification. Tap it to open the alert. On iPhone, the notification also has an **Acknowledge** button that acknowledges the alert without opening the app; if that fails, you get a follow-up notification asking you to open the alert. Manage the devices that receive push under **Profile → Push Notification Devices**. Removing a device stops push to it until you register it again. ### Schedules and shifts **On-Call → Schedule** shows who is on call each day, per layer, including shift windows. If you can edit schedules, tap a day to open **Override shift** and assign coverage to yourself or a teammate for that whole day, or remove an existing override. See [Overrides and take over](https://docs-dev.evohub.io/overrides-and-takeover.md). ### Incidents **On-Call → Incidents** lists incidents. Open one to read its updates, add an update, and resolve it. ### Silence **On-Call → Silence** pauses paging and uptime alerts for the whole organization for **30 min**, **1 hour**, **4 hours** or **8 hours** — for example while you deploy a fix you know will trip monitors. The app asks you to confirm, because no one is paged during that time. You can end the silence early from the same screen. Silencing needs permission to manage maintenance windows and uptime silences. ### Also in the app - **Escalation Policies** and **Maintenance** under the **On-Call** tab, to look up who gets paged and what maintenance is scheduled or active. - The **Dashboard**, **Uptime** and **Status** tabs, for an at-a-glance view across On-Call, uptime monitors and status pages. - **Profile**: **Notification Preferences** (turn **Mobile Push**, **Voice Call** and **Email** on or off), **Biometric Unlock** (Face ID, Touch ID or fingerprint), switching between organizations, **Support** and **Delete Account**. ## What the app does not do Some things are only available in the web console at [https://evohub.io](https://evohub.io): - Creating alerts or incidents. - Creating or editing schedules, layers and rotations (the app can add and remove day overrides). - Creating or editing escalation policies and integrations. - Notification schedules and the voice template. ## Related - [Notifications](https://docs-dev.evohub.io/notifications.md) - [Alerts and incidents](https://docs-dev.evohub.io/alerts-and-incidents.md) - [Overrides and take over](https://docs-dev.evohub.io/overrides-and-takeover.md) --- Source: https://docs-dev.evohub.io/migrate-from-opsgenie.md # Migrate from Opsgenie Atlassian ended new Opsgenie sales on June 4, 2025, and Opsgenie reaches end of support on April 5, 2027. EvoHub On-Call has a built-in importer that reads your Opsgenie account and creates the matching schedules, escalation policies and integrations. This page explains how to run it, what it maps and what you finish by hand. ## How Opsgenie concepts map to EvoHub | Opsgenie | EvoHub | Notes | | --- | --- | --- | | Users | Organization members | Matched by email. Invite everyone — EvoHub has no per-seat fee. | | Teams | Teams | Not created by the importer. | | Schedules and rotations | Schedules and layers | Imported. | | Schedule overrides | Overrides | Not imported; recreate upcoming ones. | | Escalations | Escalation policies | Imported. | | Integrations (Prometheus, Datadog, CloudWatch, email, API…) | Integrations | Imported, each with a new URL to paste into the tool. | | Heartbeats | Uptime heartbeat monitors | Not imported. See [Heartbeat monitors](https://docs-dev.evohub.io/heartbeat-monitors.md). | | Notification rules | Notification settings and notification schedule | Not imported; each person sets their own. | | SMS notifications | Push or voice call | EvoHub does not currently send SMS. | | Mobile app | EvoHub iOS and Android apps | See [Mobile app](https://docs-dev.evohub.io/mobile-app.md). | ## Before you import - **Invite your people first.** The importer matches Opsgenie users to EvoHub members by email address. Anyone who is not a member yet is left out of rotations and escalation steps. The preview lists them and, if you are allowed to invite, offers an **Invite N people** button. - **Make sure they can respond to alerts.** People who are members but whose role cannot respond to alerts are also left out, with a warning naming them. - **Create an Opsgenie API key with read access.** In Opsgenie: **Settings → API key management → Add new API key**, give it read access and copy it. You need the On-Call settings permission to import (Owners, Admins and Members have it by default). ## Run the importer :::steps ### Open the importer Go to **On-Call → Import from Opsgenie** (also linked from the **Moving from Opsgenie?** banner on the Integrations page). ### Enter the key and region Paste the **Opsgenie API key** and choose the **Region**: **US** (api.opsgenie.com) or **EU** (api.eu.opsgenie.com). Click **Preview**. The key is used for this import only; EvoHub does not store it. ### Review the preview Nothing is created yet. The preview shows **People** (who matched), **Schedules** with their layers, **Escalation policies** with their steps and delays, **Integrations** with the EvoHub type each maps to, and every warning. Untick anything you do not want. ### Import Click **Import N items**. The result lists what was created (with links), what was **Skipped** and why, and the warnings. ### Point your tools at EvoHub For each imported integration, the result shows its new webhook URL with "Paste this into … in place of the Opsgenie URL". Update each monitoring tool. Alerts reach EvoHub only after you do this. ::: You can run the importer again at any time. Items that were already imported are marked and skipped, so a re-run never creates duplicates. Only one import runs at a time per organization. ## What is imported, and how ### Schedules Each Opsgenie schedule becomes an EvoHub schedule in the same timezone, and each rotation becomes a layer: - **Daily** and **weekly** rotations carry over. A rotation of several days or weeks becomes a custom rotation of that many days. - **Hourly** rotations are rounded to whole days, with a warning. For shifts shorter than a day, give each shift its own layer with a shift window after importing. - **Time restrictions** become shift windows when EvoHub can express them: a daily window, or the same window on selected weekdays (such as Mon–Fri 09:00–17:00). Restrictions that span days (Friday 18:00 to Monday 09:00) are not imported; the layer covers the whole day and a warning tells you to set a shift window. - Rotations that have already ended are skipped. Rotations with a future end date are imported without it (EvoHub layers have no end date), with a warning to remove the layer then. - Rotation participants are kept in order, each person once. Teams, escalations and empty ("no one") slots in a rotation are left out with a warning. - A disabled Opsgenie schedule is imported as inactive. ### Escalation policies Each Opsgenie escalation becomes an escalation policy, and each rule a step: - Steps can target a **user** or a **schedule**. Rules that notify a team or another escalation are skipped with a warning. - Opsgenie counts every rule's delay from when the alert was created; EvoHub counts each step's delay from the step before, using 1, 2, 3, 5, 10, 15, 30 or 60 minutes. The importer converts the delays so each step lands as close as possible to its Opsgenie time and warns about any difference. - A rule that notifies the "next" or "previous" on-call of a schedule notifies whoever is on call now in EvoHub. - Opsgenie rules that escalate "if not closed" become steps that escalate until someone acknowledges. - Repeat settings carry over, up to EvoHub's maximum of 5 repeats. - Steps use each person's own notification preferences. ### Integrations - Integrations that send alerts *into* Opsgenie are imported as the matching EvoHub integration type (Prometheus/Alertmanager, Grafana, Datadog, New Relic, CloudWatch, Azure Monitor, Dynatrace, Sentry, Google Cloud, Zabbix, UptimeRobot, Pingdom, Site24x7, StatusCake, Nagios, PRTG, email and API). - A type EvoHub has no equivalent for is imported as a generic **API** integration, with a warning to check the payload it sends. - Integrations that only send *out* of Opsgenie — Slack, Microsoft Teams, Jira, ServiceNow, Zendesk, outgoing webhooks, Statuspage — are not imported. - Disabled Opsgenie integrations are not imported. - Each integration is attached to the first (by name) escalation policy of its Opsgenie team that is imported. If none is, it is created without a policy — choose one on the Integrations page. ## What you recreate by hand The preview lists these under **Not imported yet**: - Heartbeats — use [heartbeat monitors](https://docs-dev.evohub.io/heartbeat-monitors.md). - Alert policies and routing rules. - Notification rules — each person sets [their own notifications](https://docs-dev.evohub.io/notifications.md). - Maintenance windows — schedule them in **On-Call → Maintenance**. - Team members — Opsgenie teams are not created. Everything is created in your current EvoHub team; team names are shown for reference. Also recreate: - **Schedule overrides** that are still upcoming. - **People who joined after the import.** A re-run skips items that were already imported, so add newcomers to those schedules and policies by hand (or delete the imported item and import it again). ## Run both in parallel, then cut over :::steps ### Add EvoHub next to Opsgenie Where a tool supports several destinations (Alertmanager receivers, Datadog webhooks, CloudWatch SNS subscriptions, email recipients), keep Opsgenie and add the EvoHub URL, so both receive the same alerts for a week or two. ### Test paging end to end Fire a test alert through each integration. Confirm the right person is paged, can acknowledge from the mobile app, and that recovery resolves the alert where the source supports it. ### Compare During the parallel run, compare what EvoHub paged with what Opsgenie paged. ### Cut over Remove the Opsgenie destinations from your tools and replace heartbeats and API calls. Leave time for a quiet week with EvoHub as the only pager before Opsgenie's end of support. ::: The import itself is free. Alerts and notifications in EvoHub are billed by usage — see [How billing works](https://docs-dev.evohub.io/how-billing-works.md). For help planning a migration, contact info@evosync.io. ## Related - [Integrations overview](https://docs-dev.evohub.io/integrations-overview.md) - [Schedules and rotations](https://docs-dev.evohub.io/schedules-and-rotations.md) - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) --- Source: https://docs-dev.evohub.io/integrations-overview.md # Integrations overview An integration connects one monitoring tool to EvoHub On-Call. It gives the tool a unique URL (or, for email, a unique address) to send alerts to, and it decides which escalation policy pages people for those alerts. This page lists every supported integration and explains how ingest works. ## Add an integration :::steps ### Open Integrations Go to **On-Call → Integrations** and open the **+ Add Integration** tab. ### Pick your tool Search or browse the catalog and click the tool. ### Name it and choose a policy Enter an **Integration Name** and choose the **Escalation Policy** that should page people for its alerts. Click **Create Integration**. ### Copy the URL into your tool The new integration shows its **Webhook URL** (or **Inbound email address**). Open the integration and click **Show config example** for setup steps with your URL already filled in. ::: Creating, editing and deleting integrations needs permission to write integrations (Owners, Admins and Members by default). Reading the list needs permission to read integrations, because each integration carries its key. ## The webhook URL and key Each integration's URL looks like this: ```text https://evohub.io/ingest/?key= ``` - `` selects the parser for the tool, for example `prometheus` or `datadog`. - The **integration key** (shown as **API Key** in the integration) identifies the integration. It decides the organization and the escalation policy; nothing in the request body can override it. - No other authentication is needed: monitoring tools cannot sign in, so the key in the URL is the credential. Treat the URL like a password. If it leaks, delete the integration and create a new one. The **API** integration also accepts the key in the request body or an `Authorization: Bearer` header. The **Email** integration uses an inbound email address instead of a URL. ## Manage an integration Click an integration on the **Installed** tab to open it. You can: - copy the **Webhook URL** and **API Key**, - see **Total Events** and **Last Event**, - **View this integration's alerts**, - **Edit** its name and escalation policy, - **Disable** it (requests are refused until you **Enable** it again), or - **Delete** it — its URL and key stop working immediately. An integration with no escalation policy still records alerts, but nobody is paged. ## Supported integrations | Integration | Type in URL | How it connects | Auto-resolve | Setup | | --- | --- | --- | --- | --- | | **API** | `api` | Your own scripts and tools send JSON; trigger, acknowledge or resolve. | Send `event_action: resolve` with the same `dedup_key`. | [Alert API and generic webhook](https://docs-dev.evohub.io/generic-webhook.md) | | **Prometheus** | `prometheus` | Alertmanager webhook receiver. | Yes, with `send_resolved: true`. | [Prometheus Alertmanager](https://docs-dev.evohub.io/prometheus-alertmanager.md) | | **Grafana** | `grafana` | Grafana Alerting webhook contact point. | Yes. | [Grafana](https://docs-dev.evohub.io/grafana.md) | | **Datadog** | `datadog` | Datadog Webhooks integration, mentioned in monitor messages. | Yes, when the monitor recovers. | [Datadog](https://docs-dev.evohub.io/datadog.md) | | **Zabbix** | `zabbix` | Webhook media type with a short script. | Yes, with a recovery message. | [Zabbix](https://docs-dev.evohub.io/zabbix.md) | | **PRTG** | `prtg` | "Execute HTTP Action" notification template. | Yes, when the sensor is Up again. | [PRTG](https://docs-dev.evohub.io/prtg.md) | | **Email** | — | A unique inbound email address. | Yes, from a follow-up email that says the issue recovered. | [Email](https://docs-dev.evohub.io/email-integration.md) | | **New Relic** | `newrelic` | Webhook destination used by an alert workflow. | Yes, when the issue is closed. | [Below](#new-relic) | | **AWS CloudWatch** | `cloudwatch` | SNS topic with an HTTPS subscription. | Yes, with the OK action set. | [Below](#aws-cloudwatch) | | **Azure Monitor** | `azuremonitor` | Action group webhook with the common alert schema. | Yes. | [Below](#azure-monitor) | | **Dynatrace** | `dynatrace` | Problem notification, custom integration. | Yes. | [Below](#dynatrace) | | **Sentry** | `sentry` | Internal integration with alert rule action. | Yes, when the issue is resolved. | [Below](#sentry) | | **Google Cloud** | `googlecloud` | Cloud Monitoring webhook notification channel. | Yes, when the incident closes. | [Below](#google-cloud-monitoring) | | **UptimeRobot** | `uptimerobot` | Web-Hook alert contact. | Yes, when the monitor is up. | [Below](#uptimerobot) | | **Pingdom** | `pingdom` | Webhook integration assigned to checks. | Yes, when the check recovers. | [Below](#pingdom) | | **Site24x7** | `site24x7` | Third-party webhook integration. | Yes, when the monitor is up. | [Below](#site24x7) | | **Statuscake** | `statuscake` | Contact group webhook URL. | Yes, when the test is up. | [Below](#statuscake) | | **Nagios** | `nagios` | Notification command that posts JSON with `curl`. | Yes, on a RECOVERY notification. | [Below](#nagios) | | **Cortex XDR** | `cortex` | Cortex XDR / XSIAM webhook forwarding. | Yes, when the issue is resolved. | [Below](#cortex-xdr) | | **OpenSearch** | `opensearch` | Alerting notification channel (custom webhook). | No — resolve in EvoHub. | [Below](#opensearch) | Uptime monitors in EvoHub can also page On-Call directly, without an integration. See [Uptime alerts and silence windows](https://docs-dev.evohub.io/uptime-alerts-and-silence-windows.md). For chat, see [Slack](https://docs-dev.evohub.io/slack.md). ## How ingest works When a tool sends to an integration URL (the API and Email integrations answer slightly differently; see their pages): 1. EvoHub checks the key. A missing, unknown or disabled key is refused with `401` and the code `INVALID_KEY`. 2. The parser reads the tool's payload. A body the parser cannot read is refused with `400` and `INVALID_BODY`. 3. Each firing alert in the payload opens an alert (or matches an open one, see below), and the integration's escalation policy starts. 4. Each recovery in the payload resolves the matching open alert. The response is `200` with a count of what happened: ```json { "data": { "received": 1, "created": 1, "resolved": 0 }, "success": true } ``` EvoHub answers `200` whenever the payload was read, even if it produced no alert, so that tools do not disable the webhook after repeated errors. If the key could not be checked on EvoHub's side, the answer is `503` with `INGEST_UNAVAILABLE` and a `Retry-After` header; most tools retry automatically. ### Deduplication Every alert gets a **fingerprint** from the tool's own identifiers — Alertmanager's fingerprint, a Datadog alert ID, a Zabbix host and trigger, a PRTG sensor ID, and so on. If an alert arrives while an alert with the same fingerprint is still open (triggered or acknowledged), EvoHub records **Retriggered** on the existing alert instead of opening a new one, and nobody is paged again. After the alert is resolved, the next one with that fingerprint opens a new alert. Only new alerts count toward usage; retriggers do not. ### Auto-resolve When a tool reports that a problem has recovered, EvoHub resolves the open alert with the same fingerprint, and the escalation stops. The alert's timeline shows it was resolved by the integration. If the tool's recovery notification is not configured, alerts stay open until someone resolves them. ### Severity Each parser maps the tool's severity to EvoHub's **critical**, **high**, **medium**, **low** or **info**. The integration pages describe the mapping. When a tool sends no severity EvoHub recognizes, most parsers use **medium**; email alerts are always **high**. ## Other integrations These steps match the **Show config example** text in the console, where your URL is filled in for you. Replace `YOUR_URL` with the integration's **Webhook URL**. ### New Relic 1. In New Relic, go to **Alerts → Destinations** and add a **Webhook** with **Endpoint URL** `YOUR_URL`. 2. Go to **Alerts → Workflows**, create a workflow and notify through that webhook. New Relic's default payload template works as-is. Alerts resolve when the issue is closed or deactivated. ### AWS CloudWatch 1. Create an SNS topic and set it as the alarm's **In alarm** action and its **OK** action (the OK action enables auto-resolve). 2. Add a subscription to the topic with **Protocol** HTTPS and **Endpoint** `YOUR_URL`. Leave **Raw message delivery** off. 3. EvoHub confirms the subscription automatically. Check in SNS that it shows an ARN rather than *PendingConfirmation*. ### Azure Monitor 1. In Azure Monitor, go to **Alerts → Action groups** and create or edit one. 2. Add an action of type **Webhook** with **URI** `YOUR_URL` and **Enable common alert schema** set to **Yes**. Payloads without the common alert schema are refused. 3. Attach the action group to your alert rules. ### Dynatrace 1. In Dynatrace, go to **Settings → Integration → Problem notifications → Add notification → Custom integration**. 2. Set **Webhook URL** to `YOUR_URL` and leave the pre-filled custom payload as it is. 3. Optionally add `"ProblemSeverity": "{ProblemSeverity}"` and `"ProblemDetailsText": "{ProblemDetailsText}"` to the payload for better severity mapping and a fuller description. ### Sentry 1. In Sentry, go to **Settings → Developer Settings → Custom Integrations** and create a new **Internal** integration. 2. Set **Webhook URL** to `YOUR_URL`, enable **Alert Rule Action**, and under **Webhooks** check **issue**. 3. In your alert rule, add the action that sends a notification through your integration. The legacy Sentry "Webhooks" plugin is not supported. ### Google Cloud Monitoring 1. In Google Cloud, go to **Monitoring → Alerting → Edit notification channels → Webhooks → Add new** and set **Endpoint URL** to `YOUR_URL`. 2. Select this channel in your alerting policies. An incident opening creates an alert; the incident closing resolves it. ### UptimeRobot 1. In UptimeRobot, go to **My Settings → Alert Contacts → Add → Web-Hook**. 2. In **URL to Notify**, paste `YOUR_URL&` — including the `&` at the end. UptimeRobot appends its alert data to the URL, and without the `&` the key is corrupted and nothing arrives. 3. No POST value is needed. ### Pingdom 1. In My Pingdom, go to **Integrations → Add integration**, choose **Webhook** and set **URL** to `YOUR_URL`. 2. Edit each check, open **Connect integrations** and enable the webhook. It sends nothing until it is assigned to a check. ### Site24x7 1. In Site24x7, go to **Admin → Third-Party Integration → Webhook**. 2. Set **Hook URL** to `YOUR_URL`, **Method** POST, turn on **Send Incident Parameters**, and turn on **Post as JSON** (recommended; form mode also works). ### Statuscake 1. In StatusCake, go to **Contact Groups**, create or edit one and set **Web Hook URL** to `YOUR_URL`. 2. Assign the contact group to your uptime tests. StatusCake sends a fixed form post on Down and Up; there is nothing else to configure. ### Nagios 1. Add a notification command to `commands.cfg` (the `$…$` parts are Nagios macros): ```text define command { command_name notify-evohub command_line /usr/bin/curl -s -X POST "YOUR_URL" -H "Content-Type: application/json" -d '{"host":"$HOSTNAME$","service":"$SERVICEDESC$","state":"$SERVICESTATE$","notification_type":"$NOTIFICATIONTYPE$","output":"$SERVICEOUTPUT$"}' } ``` 2. Set `notify-evohub` as the `service_notification_commands` of the contact your services notify. For host alerts, use `$HOSTSTATE$` and `$HOSTOUTPUT$` and leave `service` empty. If check output may contain quotes, use a small wrapper script that JSON-escapes it. ### Cortex XDR Requires Cortex XDR 5.x or later, or XSIAM 3.x or later. 1. Go to **Settings → Configurations → Integrations → External Applications → Add Application → Webhook** and set **URL** to `YOUR_URL`. 2. Go to **Settings → Configurations → General → Notifications**, add a **Forwarding Configuration** and pick the issues to forward. Cortex sends its own issue JSON, which EvoHub reads natively. ### OpenSearch 1. In OpenSearch, go to **Notifications → Channels → Create channel**, choose **Custom webhook**, set **Define endpoint by** to **Webhook URL** and paste `YOUR_URL`. (In "Custom attributes URL" mode, the `?key=` part is dropped.) 2. The default action message works as-is. For richer fields, replace the monitor trigger's action message with: ```json { "monitor": "{{ctx.monitor.name}}", "trigger": "{{ctx.trigger.name}}", "severity": "{{ctx.trigger.severity}}", "message": "{{ctx.trigger.name}}", "period_start": "{{ctx.periodStart}}", "period_end": "{{ctx.periodEnd}}" } ``` The channel's **Send test message** creates a real test alert, so the whole path, paging included, is exercised. OpenSearch sends no recovery notification; resolve these alerts in EvoHub. ## Related - [Alert API and generic webhook](https://docs-dev.evohub.io/generic-webhook.md) - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) - [Alerts and incidents](https://docs-dev.evohub.io/alerts-and-incidents.md) - [Migrate from Opsgenie](https://docs-dev.evohub.io/migrate-from-opsgenie.md) --- Source: https://docs-dev.evohub.io/generic-webhook.md # Alert API and generic webhook If your tool is not in the catalog, or you want to raise alerts from your own code, use the **API** integration. It accepts a small JSON body in which only a summary is required, and it can also acknowledge and resolve alerts. Its body is compatible with the Events v2 shape used by other paging tools, so a tool that already speaks it only needs a new URL and key. ## Set up the API integration :::steps ### Create the integration Go to **On-Call → Integrations → + Add Integration**, choose **API**, choose an **Escalation Policy** and click **Create Integration**. ### Copy the URL and key Open the integration. The **Webhook URL** is `https://evohub.io/ingest/api?key=`; the **API Key** is the key on its own. ### Send a test event Run the `curl` example below with your key and check that an alert appears in **On-Call → Alerts**. ::: ## Send an event `POST https://evohub.io/ingest/api` with a JSON body. Send the integration key in whichever way your tool makes easiest: - `"routing_key"` in the body, - `?key=` in the URL, or - an `Authorization: Bearer ` header. :::code-group ```bash [Minimal trigger] curl -X POST 'https://evohub.io/ingest/api' \ -H 'Authorization: Bearer YOUR_INTEGRATION_KEY' \ -H 'Content-Type: application/json' \ -d '{"summary": "Database primary is down"}' ``` ```bash [Full trigger] curl -X POST 'https://evohub.io/ingest/api' \ -H 'Content-Type: application/json' \ -d '{ "routing_key": "YOUR_INTEGRATION_KEY", "event_action": "trigger", "dedup_key": "db-primary-down", "payload": { "summary": "Database primary is down", "source": "db-01.prod.acme.example", "severity": "critical", "component": "postgres", "group": "payments", "custom_details": { "region": "eu-central" } } }' ``` ```bash [Resolve] curl -X POST 'https://evohub.io/ingest/api' \ -H 'Content-Type: application/json' \ -d '{ "routing_key": "YOUR_INTEGRATION_KEY", "event_action": "resolve", "dedup_key": "db-primary-down" }' ``` ::: ### Request fields You can send the alert details nested in `payload` (Events v2 style) or as flat fields at the top level. A flat field fills the matching `payload` field when that one is empty. | Field | Required | Description | | --- | --- | --- | | `routing_key` | No* | The integration key. *Required unless sent as `?key=` or a bearer token. | | `event_action` | No | `trigger` (default), `acknowledge` or `resolve`. | | `dedup_key` | For acknowledge and resolve | Identifies the alert. On a trigger without one, EvoHub derives a key from the summary and source and returns it. | | `payload.summary` / `summary` / `title` | For trigger | The alert's title. | | `payload.severity` / `severity` | No | `critical`, `high`, `medium`, `low` or `info`. The Events v2 values are accepted too: `error` is read as `high` and `warning` as `medium`. Anything else, or nothing, is `medium`. | | `payload.source` / `source` | No | Where the problem is, for example a host name. Stored as the `source` label. | | `description` / `payload.description` | No | The alert's description. | | `payload.component`, `payload.group`, `payload.class` | No | Stored as labels of the same name. | | `payload.custom_details` | No | An object; each entry becomes a label, its value as text. | | `payload.timestamp`, `client`, `client_url` | No | Accepted for compatibility and ignored. The alert is stamped when it arrives. | ### Response A processed event returns `202`: ```json { "data": { "status": "success", "message": "Event processed", "dedup_key": "db-primary-down" }, "success": true } ``` Keep the `dedup_key` if you let EvoHub derive it — you need it to resolve the alert later. A request that was not processed returns the same envelope with `"status": "invalid event"` and a `message`: | Status | When | Example `message` | | --- | --- | --- | | `400` | The body is not JSON, a trigger has no summary, an acknowledge or resolve has no `dedup_key`, or `event_action` is unknown. | `summary is required` | | `401` | No key was sent, or it names no enabled integration. | `invalid routing_key` | | `500` | The alert could not be created, acknowledged or resolved. | `failed to create alert` | | `503` | The key could not be checked right now. Retry after the `Retry-After` seconds. | `could not verify routing_key right now; retry later` | ### How events behave - **Trigger** opens an alert and starts the integration's escalation policy. A trigger whose `dedup_key` matches an alert that is still open does not open a second one; it is recorded as **Retriggered** on the existing alert. - **Acknowledge** acknowledges the open, triggered alert with that `dedup_key`. - **Resolve** resolves the open alert with that `dedup_key`. - Acknowledging or resolving a `dedup_key` that has no open alert does nothing and still returns `202`. - The integration's escalation policy always applies; the request cannot choose another one. ## Generic alert webhook EvoHub also accepts a simpler, flat alert format at `/ingest/alerts`. Use it with the key of your API integration: ```bash curl -X POST 'https://evohub.io/ingest/alerts?key=YOUR_INTEGRATION_KEY' \ -H 'Content-Type: application/json' \ -d '{ "title": "Disk almost full on web-03", "description": "/var is at 93%", "severity": "high", "source": "cron-disk-check", "fingerprint": "web-03-disk-var", "labels": { "host": "web-03.acme.example" }, "annotations": { "runbook": "https://wiki.acme.example/disk" } }' ``` | Field | Required | Description | | --- | --- | --- | | `title` | Yes | The alert's title. | | `description` | No | The alert's description. | | `severity` | No | `critical`, `high`, `medium` (default), `low` or `info`. | | `source` | No | Shown as the alert's source. Defaults to `webhook`. | | `fingerprint` | No | Deduplicates repeated sends while the alert is open. | | `labels`, `annotations` | No | String-to-string maps, shown on the alert and usable in the voice template. | | `escalation_policy_id` | No | Used only when the integration has no escalation policy of its own. | The key goes in `?key=` only. A missing `title` returns `400` with the code `VALIDATION_FAILED` and the field in `details`; a bad key returns `401` with `INVALID_KEY`. A successful call returns `200` with `{"data": {"received": 1, "created": 1, "resolved": 0}, "success": true}`. This format cannot acknowledge or resolve; use the API events above for that. ## Tips - Choose a stable `dedup_key` (or `fingerprint`) per problem, such as `-`, so repeats while the problem lasts do not open new alerts and your resolve finds the right alert. - Send a resolve when your check passes again. Without one, the alert stays open until someone resolves it. - Only new alerts count toward usage; retriggers of an open alert do not. See [How billing works](https://docs-dev.evohub.io/how-billing-works.md). ## Related - [Integrations overview](https://docs-dev.evohub.io/integrations-overview.md) - [Alerts and incidents](https://docs-dev.evohub.io/alerts-and-incidents.md) - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) --- Source: https://docs-dev.evohub.io/prometheus-alertmanager.md # Prometheus Alertmanager EvoHub receives alerts from Prometheus through Alertmanager's webhook receiver. Each firing alert opens an EvoHub alert, and when Alertmanager reports it resolved, EvoHub resolves it too. ## Set it up :::steps ### Create the integration In EvoHub, go to **On-Call → Integrations → + Add Integration**, choose **Prometheus**, pick an **Escalation Policy** and click **Create Integration**. Copy the **Webhook URL**. ### Add a receiver to Alertmanager Open your Alertmanager configuration (`alertmanager.yml`) and add a receiver that uses the URL. Keep `send_resolved: true` so EvoHub can resolve alerts automatically. ### Route alerts to it Point a route at the `evohub` receiver — the top-level route, or a sub-route for the alerts that should page — and reload Alertmanager. ::: ```yaml route: receiver: evohub # Or keep your current default receiver and add a sub-route: # routes: # - receiver: evohub # matchers: # - severity=~"critical|warning" receivers: - name: evohub webhook_configs: - url: "https://evohub.io/ingest/prometheus?key=YOUR_INTEGRATION_KEY" send_resolved: true ``` To keep another destination during a migration, add a second route or receiver for it; Alertmanager can notify several receivers. ## What EvoHub reads Alertmanager sends a group of alerts in one request. EvoHub handles each alert in the group separately: | EvoHub alert | Taken from | | --- | --- | | Title | The `alertname` label (or "Prometheus Alert" if it is missing). | | Description | The `summary` annotation, or the `description` annotation if there is no summary. | | Severity | The `severity` label — see below. | | Labels and annotations | All of the alert's labels and annotations, shown on the alert and usable in the voice template. | | Fingerprint | Alertmanager's fingerprint for the alert (or one computed from its labels). | ### Severity | `severity` label | EvoHub severity | | --- | --- | | `critical` | critical | | `warning` | high | | `info`, `informational` | info | | anything else, or missing | medium | Add a `severity` label to your alerting rules to control how urgent each alert is. ## Resolve and deduplication - An alert with status `firing` opens an EvoHub alert. If the same alert (same fingerprint) is still open in EvoHub, Alertmanager's repeat notifications are recorded as **Retriggered** and nobody is paged again. - An alert with status `resolved` resolves the matching EvoHub alert. This needs `send_resolved: true`. - Because the fingerprint comes from the alert's labels, an alert whose labels change (for example a label with a changing value) is treated as a new alert. Keep frequently changing values in annotations, not labels. ## Test it Send a test alert to Alertmanager with `amtool`: ```bash amtool alert add alertname=EvoHubTest severity=critical \ --annotation=summary="Test alert from Alertmanager" \ --alertmanager.url=http://localhost:9093 ``` After Alertmanager's group wait, the alert appears in **On-Call → Alerts** and the integration's **Last Event** updates. ## Troubleshooting - **Nothing arrives**: check Alertmanager's logs for webhook errors. A `401` means the key in the URL is wrong or the integration is disabled. - **Alerts never resolve**: make sure `send_resolved: true` is set on the webhook config. - **Alert arrives but nobody is paged**: check that the integration has an escalation policy and that someone is on call on the schedule it targets. ## Related - [Integrations overview](https://docs-dev.evohub.io/integrations-overview.md) - [Grafana](https://docs-dev.evohub.io/grafana.md) - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) --- Source: https://docs-dev.evohub.io/grafana.md # Grafana EvoHub receives alerts from Grafana Alerting through a **Webhook** contact point. Each firing alert opens an EvoHub alert, and Grafana's resolved notification resolves it. ## Set it up :::steps ### Create the integration In EvoHub, go to **On-Call → Integrations → + Add Integration**, choose **Grafana**, pick an **Escalation Policy** and click **Create Integration**. Copy the **Webhook URL**. ### Add a contact point in Grafana In Grafana, go to **Alerting → Contact points → Add contact point**. Choose the **Webhook** integration, paste the URL into **URL** and keep **HTTP Method** as **POST**. Save. ### Use it in a notification policy In **Alerting → Notification policies**, send the alerts that should page to the new contact point — as the default policy or a nested policy matching specific labels. ::: Grafana sends resolved notifications by default, so auto-resolve works without extra settings (unless you turned on **Disable resolved message** on the contact point). You can use Grafana's **Test** button on the contact point to check the connection; it sends a test notification that opens a test alert in EvoHub. ## What EvoHub reads Grafana can send several alerts in one notification. EvoHub handles each alert separately: | EvoHub alert | Taken from | | --- | --- | | Title | The alert's `alertname` label, otherwise the notification title. | | Description | The `summary` annotation, otherwise the notification message. | | Severity | The `severity` label — see below. | | Labels and annotations | All of the alert's labels and annotations. | | Fingerprint | Grafana's fingerprint for the alert (or one computed from its labels). | ### Severity Add a `severity` label to your Grafana alert rules to control urgency: | `severity` label | EvoHub severity | | --- | --- | | `critical` | critical | | `high`, `warning` | high | | `low` | low | | `info` | info | | anything else, or missing | medium | ## Resolve and deduplication - A `firing` alert opens an EvoHub alert. Repeat notifications for an alert that is still open are recorded as **Retriggered**, and nobody is paged again. - A `resolved` alert resolves the matching EvoHub alert. - Alerts with different label sets are different alerts. A multi-dimensional rule (for example one alert per host) opens one EvoHub alert per host. ## Troubleshooting - **Contact point test fails with 401**: the key in the URL is wrong, or the integration is disabled in EvoHub. - **Alerts never resolve**: check that resolved messages are not disabled on the contact point. - **Alert arrives but nobody is paged**: check the integration's escalation policy and who is on call. ## Related - [Integrations overview](https://docs-dev.evohub.io/integrations-overview.md) - [Prometheus Alertmanager](https://docs-dev.evohub.io/prometheus-alertmanager.md) - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) --- Source: https://docs-dev.evohub.io/datadog.md # Datadog EvoHub receives Datadog monitor notifications through Datadog's **Webhooks** integration. A triggered monitor opens an EvoHub alert, and its recovery notification resolves it. Datadog's default payload works as it is; an optional custom payload gives EvoHub better severity and a stable alert ID. ## Set it up :::steps ### Create the integration In EvoHub, go to **On-Call → Integrations → + Add Integration**, choose **Datadog**, pick an **Escalation Policy** and click **Create Integration**. Copy the **Webhook URL**. ### Add a webhook in Datadog In Datadog, go to **Integrations → Webhooks** and add a new webhook. Set **Name** to `evohub` and **URL** to the webhook URL. Save. ### Mention it in your monitors In each monitor's notification message, add `@webhook-evohub`. Datadog then notifies EvoHub when the monitor triggers and when it recovers. ::: ## Optional: custom payload With the default payload, EvoHub reads the monitor's state from the notification title (for example `[Triggered]` or `[Recovered]`) and every alert is **medium** severity. For proper severity, tags as labels and a stable alert ID, turn on **Use custom payload** on the webhook and paste: ```json { "id": "$ID", "title": "$ALERT_TITLE", "body": "$EVENT_MSG", "priority": "$ALERT_PRIORITY", "alert_type": "$ALERT_TYPE", "alert_status": "$ALERT_TRANSITION", "alert_id": "$ALERT_ID", "tags": "$TAGS" } ``` The `$…` parts are Datadog template variables that Datadog fills in. ## What EvoHub reads | EvoHub alert | Taken from | | --- | --- | | Title | `title`, with any `[Triggered on {…}]` prefix removed. A scope such as `{host:web-1}` in the prefix is kept as a `scope` label. | | Description | `body`. | | Severity | `alert_type` and `priority` — see below. | | Labels | `tags`, split into `key:value` pairs. | | Fingerprint | `alert_id` when present; otherwise derived from the title (and scope), which is the same on the trigger and recovery notifications. | ### Severity | `alert_type` | `priority` | EvoHub severity | | --- | --- | --- | | `error` | `P1`, `P2` or `normal` | critical | | `error` | anything else | high | | `warning` | any | medium | | `info`, `success` | any | info | | missing (default payload) | any | medium | ## Resolve and deduplication - A notification whose `alert_status` (custom payload) or title prefix (default payload) says **Recovered** resolves the matching alert. Nothing is opened. - Any other notification — triggered, re-triggered, warn, no data, renotify — opens an alert, or is recorded as **Retriggered** on the alert that is still open for the same monitor and scope. - Multi-alert monitors: with the default payload, each group (for example each host) gets its own EvoHub alert, because the scope is part of the fingerprint. With the custom payload above, the fingerprint is Datadog's `$ALERT_ID` alone, so groups that share it share one EvoHub alert. If you need one EvoHub alert per group, use the default payload, or add the group to `alert_id` in your custom payload. ## Troubleshooting - **Nothing arrives**: check that the monitor message contains `@webhook-evohub` and that the webhook URL includes `?key=`. - **Recoveries open new alerts instead of resolving**: use the custom payload so EvoHub gets a stable `alert_id`, or make sure the monitor title does not change between trigger and recovery. - **Every alert is medium**: switch to the custom payload above. ## Related - [Integrations overview](https://docs-dev.evohub.io/integrations-overview.md) - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) - [Alerts and incidents](https://docs-dev.evohub.io/alerts-and-incidents.md) --- Source: https://docs-dev.evohub.io/zabbix.md # Zabbix EvoHub receives Zabbix problems through a **Webhook** media type with a short script. A problem opens an EvoHub alert, and the recovery message resolves it. ## Set it up :::steps ### Create the integration In EvoHub, go to **On-Call → Integrations → + Add Integration**, choose **Zabbix**, pick an **Escalation Policy** and click **Create Integration**. Copy the **Webhook URL**. ### Create a webhook media type In Zabbix, go to **Alerts → Media types → Create media type** and choose type **Webhook**. ### Add the parameters Add these parameters (**Name** = **Value**), using your webhook URL for `url`. ### Paste the script Paste the script below into the **Script** field and save the media type. ### Assign the media type Add the media type to the Zabbix user that your trigger actions notify (the user's **Media** tab), and make sure an action sends problems to that user. ::: Parameters: ```text url = https://evohub.io/ingest/zabbix?key=YOUR_INTEGRATION_KEY event_id = {EVENT.ID} trigger_name = {EVENT.NAME} trigger_severity = {EVENT.SEVERITY} trigger_status = {EVENT.STATUS} host_name = {HOST.NAME} host_ip = {HOST.IP} event_value = {EVENT.VALUE} ``` Script: ```javascript var p = JSON.parse(value); var req = new HttpRequest(); req.addHeader('Content-Type: application/json'); req.post(p.url, JSON.stringify({ event_id: p.event_id, trigger_name: p.trigger_name, trigger_severity: p.trigger_severity, trigger_status: p.trigger_status, host_name: p.host_name, host_ip: p.host_ip, event_value: p.event_value })); if (req.getStatus() < 200 || req.getStatus() >= 300) throw 'EvoHub ' + req.getStatus(); return 'OK'; ``` ## Turn on auto-resolve Zabbix only sends a recovery if you ask it to: 1. In the media type, open **Message templates** and add a **Problem recovery** template. Without it, Zabbix never sends a recovery and alerts stay open. 2. In your trigger action, open **Recovery operations** and send a message through this same media type. No script or parameter change is needed: the recovery carries `event_value = 0` and EvoHub resolves the matching alert. ## What EvoHub reads | EvoHub alert | Taken from | | --- | --- | | Title | `trigger_name` (or "Zabbix Alert"). | | Description | "Host: " and `host_name`. | | Labels | `host`, `severity` and, when sent, `host_ip`. | | Fingerprint | The host name and trigger name together. | ### Severity | Zabbix severity | EvoHub severity | | --- | --- | | Disaster | critical | | High | high | | Average | medium | | Warning | low | | Information, Not classified | info | ## Resolve and deduplication - A message is a recovery when `event_value` is `0` or `trigger_status` is `RESOLVED` or `OK`. It resolves the open alert for the same host and trigger. - Because the fingerprint is host plus trigger name, repeated notifications for the same problem are recorded as **Retriggered** on the open alert, and the same trigger on different hosts opens separate alerts. ## Troubleshooting - In Zabbix, **Reports → Action log** shows each media type call. An error such as `EvoHub 401` means the key is wrong or the integration is disabled. - If alerts never resolve, check that the **Problem recovery** template exists and that the action has a recovery operation using this media type. ## Related - [Integrations overview](https://docs-dev.evohub.io/integrations-overview.md) - [PRTG](https://docs-dev.evohub.io/prtg.md) - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) --- Source: https://docs-dev.evohub.io/prtg.md # PRTG EvoHub receives PRTG sensor alerts through a notification template with **Execute HTTP Action**. A sensor going Down or Warning opens an EvoHub alert, and the sensor returning to Up resolves it. ## Set it up :::steps ### Create the integration In EvoHub, go to **On-Call → Integrations → + Add Integration**, choose **PRTG**, pick an **Escalation Policy** and click **Create Integration**. Copy the **Webhook URL**. ### Create a notification template In PRTG, go to **Setup → Notification Templates → Add Notification Template** and enable **Execute HTTP Action**. ### Set the URL and method Set **URL** to your webhook URL and **HTTP Method** to **POST**. GET sends no payload. ### Set the postdata Paste the postdata below as it is. PRTG fills in the `%…` placeholders. ### Use it in triggers Add the template to the notification triggers of the sensors (or groups and devices) that should page: a **State Trigger** for Down and Warning, and for Up so alerts resolve. ::: Postdata: ```text device=%device&sensor=%sensor&status=%laststatus&message=%message&priority=%priority&sensor_id=%sensorid ``` PRTG's HTTP action sends form-encoded data, which EvoHub reads directly. EvoHub also accepts the same fields as JSON. > [!WARNING] > Keep `sensor_id=%sensorid` in the postdata. EvoHub uses the sensor ID to match a recovery to its alert and to keep different sensors apart. Keep `priority=%priority` too — without it, a Down sensor is treated as the lowest priority. ## What EvoHub reads | EvoHub alert | Taken from | | --- | --- | | Title | "`sensor` on `device` is `status`", for example "Ping on db-01 is Down". | | Description | `message`. | | Labels | `device`, `sensor`, `status`. | | Fingerprint | `sensor_id`. | ### Severity Severity comes from the sensor's status and its priority (PRTG's `%priority` renders as stars, `*` to `*****`; a digit 1–5 also works): | Status | Priority | EvoHub severity | | --- | --- | --- | | Down | 5 stars | critical | | Down | 4 stars | high | | Down | 3 stars | medium | | Down | 2 stars | low | | Down | 1 star or missing | info | | Warning | any | medium | | any other status (except Up and Paused) | any | low | Raise the priority of sensors whose failure should wake someone up. ## Resolve and deduplication - A notification whose status is **Up** or **Paused** resolves the open alert for that sensor. EvoHub reads the current state from both `%laststatus` ("Down") and the longer `%status` form ("Up ended (now: Down)"). - Repeated Down or Warning notifications for a sensor whose alert is still open are recorded as **Retriggered**. ## Troubleshooting - **Nothing arrives**: check that **HTTP Method** is POST and that the URL includes `?key=`. A `400` means the postdata has none of `device`, `sensor` or `sensor_id`. - **All sensors collapse into one alert, or recoveries do not resolve**: `sensor_id=%sensorid` is missing from the postdata. - **Alerts stay open**: add the template to the trigger for the Up state too. ## Related - [Integrations overview](https://docs-dev.evohub.io/integrations-overview.md) - [Zabbix](https://docs-dev.evohub.io/zabbix.md) - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) --- Source: https://docs-dev.evohub.io/email-integration.md # Email integration Some tools can only send email. The **Email** integration gives you a unique inbound address: every email sent to it opens an alert, and a follow-up email saying the problem recovered resolves it. No webhook is needed. ## Set it up :::steps ### Create the integration In EvoHub, go to **On-Call → Integrations → + Add Integration**, choose **Email**, pick an **Escalation Policy** and click **Create Integration**. ### Copy the inbound address EvoHub shows "Integration created! Send alert emails to this address:" with the address. You can copy it again later from the integration's **Inbound email address** field. ### Use it as the alert recipient In your monitoring tool, add the address as the recipient of its alert emails. ### Send a test Send a test email from the tool (or from your own mailbox) and check that an alert appears in **On-Call → Alerts**. ::: Each email integration has its own address, so you can create one per tool and route each to a different escalation policy. ## What EvoHub reads | EvoHub alert | Taken from | | --- | --- | | Title | The email subject (or "Email alert" if empty). | | Description | The plain-text body. For HTML-only emails, the text with the HTML tags removed. | | Severity | Always **high**. Email carries no reliable severity, so EvoHub does not guess one. | | Fingerprint | The subject, with status words (such as DOWN, UP, ALERT, RESOLVED, CRITICAL) and punctuation removed. | ## Auto-resolve An email is treated as a recovery when its subject or body contains any of these phrases (case does not matter): `resolved`, `recovered`, `is up`, `back up`, `is back`, `cleared`, `no longer`, `has recovered`, `up again`, `ok again` A recovery email does not open an alert. It resolves the open alert whose fingerprint matches — so `[DOWN] api.acme.example` is resolved by `[UP] api.acme.example is back up`, because both subjects reduce to the same fingerprint. > [!WARNING] > The recovery check looks at the body as well as the subject. If your tool's problem emails contain one of those phrases in their body (for example a footer such as "This alert will be resolved automatically"), they are read as recoveries and no alert opens. Remove the phrase from the tool's email template, or use a webhook integration for that tool. ## Deduplication While an alert is open, more emails with the same subject (after status words are removed) are recorded as **Retriggered** on that alert instead of opening new ones. Different monitors should therefore have different subjects, for example by including the monitor or host name. ## Security - The address contains a random token and is not guessable. EvoHub does not check the sender, so anyone who knows the address can open alerts. Share it only with the tools that need it. - To stop accepting email, **Disable** the integration. To retire an address, **Delete** the integration and create a new one. - Emails to an address that does not belong to an enabled integration are ignored. ## Related - [Integrations overview](https://docs-dev.evohub.io/integrations-overview.md) - [Alert API and generic webhook](https://docs-dev.evohub.io/generic-webhook.md) - [Alerts and incidents](https://docs-dev.evohub.io/alerts-and-incidents.md) --- Source: https://docs-dev.evohub.io/slack.md # Slack Connecting Slack lets EvoHub post On-Call alerts to your channels and lets your team act on them from Slack. Slack runs alongside paging: it never replaces the phone calls, push notifications and emails your escalation policies send. ## What the Slack integration does - **Posts new alerts to a default channel.** Every new alert in your organization is posted to the channel you choose. - **Posts from escalation steps.** Any escalation step can also post the alert to a channel when it runs, in addition to paging its target. - **Keeps each message up to date.** When an alert is acknowledged, taken over, assigned, redirected or resolved — in EvoHub, in the mobile app, by phone or in Slack — EvoHub edits the messages it already posted instead of posting new ones. - **Lets people respond from Slack.** Each message has **Acknowledge** (while the alert is triggered), **Resolve** (until it is resolved), a **…** menu with **Take over** and **Add note**, and **View in EvoHub**. Each message shows the alert's status and severity, its title, the start of its description, and **Source**, **Started** and **Assigned to**. Slack is free: posting messages and button presses are not metered. ## Connect your workspace :::steps ### Open Org Settings Click your avatar and go to **Organization → Org Settings**. The **Slack** card is there. ### Add to Slack Click **Add to Slack**, choose your Slack workspace and approve the EvoHub app. You return to Org Settings with "Slack connected". ### Invite the app to your channels In each Slack channel EvoHub should post to, type `/invite @EvoHub`. EvoHub can only list and post to channels the app has been added to. ### Choose the default channel Under **Post alerts to**, pick the channel. If it is not listed, invite the app and click **Refresh**. Choose **None** to stop posting every new alert. ::: Connecting, disconnecting and choosing the default channel need the On-Call settings permission and access to **Org Settings**. If the card says **Slack is not available yet**, Slack cannot be connected for your organization at the moment. ## Post from an escalation step To post an alert to a team's channel only when a particular step runs: 1. Open the escalation policy and click **Edit**. 2. In the step's **Chat** area, click **Send to Slack** and choose a channel. 3. Click **Save Changes**. The step still pages its target; the channel post is in addition. To stop posting, remove the channel from the step. See [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md). ## Respond from Slack When someone clicks a button, EvoHub acts as that person's EvoHub account: - EvoHub reads the email address on the person's Slack profile and looks for an EvoHub member of your organization with the same email. - That member must be allowed to respond to alerts. - The action is recorded on the alert's timeline under their name, marked as coming from Slack (for example "Acknowledged from Slack."). Notes added with **Add note** go on the timeline with "(from Slack)". If the action cannot be carried out, Slack shows the person a message explaining why, for example: - "Your Slack email (…) is not a member of the EvoHub organization this workspace is connected to." — invite them to EvoHub with that email. - "You are not allowed to respond to alerts in EvoHub." — give them a role that can respond to alerts. - "This alert is already resolved." ## Disconnect In **Org Settings → Slack**, click **Disconnect** and confirm. EvoHub stops posting alerts to Slack, and buttons on existing messages stop working. Channels chosen on escalation steps are kept for when you reconnect. Reconnecting the same workspace also keeps the default channel; connecting a different workspace clears it. ## Related - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) - [Alerts and incidents](https://docs-dev.evohub.io/alerts-and-incidents.md) - [Integrations overview](https://docs-dev.evohub.io/integrations-overview.md) --- Source: https://docs-dev.evohub.io/status-pages-overview.md # Status pages A status page tells your customers whether your services are working, what is wrong when they are not, and what work is planned. This page walks through creating one, choosing its layout and branding, and putting it live. ## How a status page fits together A status page is made of a few parts, each covered on its own page: | Part | What it is | |------|-----------| | [Components](https://docs-dev.evohub.io/components-and-groups.md) | The services visitors see a status for, such as "API" or "Dashboard". A component's status is set by hand or follows an uptime monitor. | | [Component groups](https://docs-dev.evohub.io/components-and-groups.md#component-groups) | Optional groups of components, one per product, region or service. | | [Incidents and maintenance](https://docs-dev.evohub.io/status-incidents-and-maintenance.md) | Published from On-Call and shown on the page with their updates. | | [Subscribers](https://docs-dev.evohub.io/subscribers.md) | Visitors who follow the page by email or through an RSS/Atom feed. | | [Custom domain](https://docs-dev.evohub.io/status-page-custom-domain.md) | The address the page is served on, such as `status.example.com`. | Status pages live under **Status Page** in the EvoHub console. The **Pages** list shows every page in your organization. If you pick a team in the team switcher, the list shows that team's pages. ## Create a status page :::steps ### Open Status Page In the EvoHub console, open **Status Page** from the **Services** menu. ### Start a new page Click **New Page** (or **Create your status page** if you have none yet). ### Name it and add a logo Enter a **Name**, for example `Acme Status`. You can upload a logo now (PNG, JPEG, WebP or SVG, up to 2 MB) or paste a logo URL. Both are optional and can be changed later. ### Create Click **Create Page**. EvoHub opens the new page's settings. ::: A page created while a team is selected in the team switcher belongs to that team. ## The page settings Each status page has five tabs: | Tab | What you do there | |-----|------------------| | **Components** | Add components, group them, set their status and pick the public layout. | | **Incidents** | See the incidents and scheduled maintenance published to this page. | | **Subscribers** | Turn email subscriptions on or off and see who is subscribed. | | **Appearance** | Page text, links, analytics, history window, logo, favicon and theme. | | **Domain** | Connect your custom domain, and delete the page. | Use **Edit page** in the header to rename the page or change **Publicly accessible**. ## Choose a public layout Once a page has components, the **Components** tab shows **Public page layout**. It decides how visitors see your components: | Layout | What visitors see | |--------|------------------| | **One list** (default) | Every component in one plain list. Group names are not shown. | | **Folded groups** | Each group folds into one line with its worst status and one uptime bar. Visitors can open a group to see its components. | | **A page per group** | The main page shows a card for each group, and each group has its own page at an address made from its name, such as `status.example.com/payments`. | The two grouped layouts need at least one group. See [Components and groups](https://docs-dev.evohub.io/components-and-groups.md) for how to create groups, choose which groups start open, and the limit on **A page per group**. ## Brand the page Open the **Appearance** tab. **Customization** holds the page's text and links. Click **Save customization** when you are done. | Field | What it does | |-------|-------------| | **Page description** | Shown under the header on the public page. | | **Footer text** | Small print at the bottom of the page, for example `© 2026 Acme`. | | **Support URL** | Where the support link in the header points. Use a `mailto:` or `https:` address. The link only appears when this is set. | | **Support button label** | The text of that link. Defaults to **Report a problem**. | | **Privacy policy URL** / **Terms of service URL** | Linked in the footer. | | **Google Analytics tag** | A tag such as `G-XXXXXXX`. Page views are sent to your Google Analytics property. | | **History window** | How far back the uptime bars and the incident calendar go: **30 days** or **90 days** (the default). | | **Show this page in search engine results** | Clear it to ask search engines not to index the page. | The other cards on the tab: - **Logo**: shown in the page header instead of the page name. PNG, JPEG, WebP or SVG, up to 2 MB. - **Favicon**: the browser-tab icon. If you do not set one, the logo is used. - **Public page theme**: **Light** or **Dark**. Visitors see the page in the theme you pick. Every public page also has a small "Powered by EvoHub" link in the footer. ## Publish the page A status page goes live on your own domain. Until you connect one, **View public page** is disabled. :::steps ### Add components On the **Components** tab, add the services you want to show. See [Components and groups](https://docs-dev.evohub.io/components-and-groups.md). ### Connect a domain On the **Domain** tab, connect a subdomain such as `status.example.com` and add the DNS records the console shows. See [Custom domain](https://docs-dev.evohub.io/status-page-custom-domain.md). ### Check it When the domain shows **Verified · SSL active**, click **View public page**. ::: Changes you make in the console, and incidents published from On-Call, reach visitors within about 30 seconds. An open public page refreshes itself, so visitors do not need to reload. ### Hide a page without deleting it Click **Edit page** and clear **Publicly accessible**. Visitors to your domain then see "This status page is not configured", and the page's feeds stop working. Turn it back on to restore the page. ## Delete a status page On the **Domain** tab, under **Danger Zone**, click **Delete page**. Type the page's name to confirm, then click **Delete permanently**. > [!WARNING] > Deleting a page permanently removes its public page, components, incidents, maintenance windows and connected custom domain. It cannot be undone. ## Who can do what Status page actions follow your organization role. The permissions involved are: | Action | Permission | |--------|-----------| | Create, edit, brand, publish or delete a page; connect a domain | `status:page:write` | | Add, edit, reorder and group components; set a component's status | `status:component:write` | | See incidents / maintenance on the page | `status:incident:read` / `status:maintenance:read` | | See subscribers / remove subscribers and change subscription settings | `status:subscriber:read` / `status:subscriber:write` | The built-in Owner, Admin and Member roles hold all of these. Viewers can see pages but not change them. See [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md). ## Billing Status pages are billed by usage. See [How billing works](https://docs-dev.evohub.io/how-billing-works.md) for what is metered and the current rates. ## Related - [Components and groups](https://docs-dev.evohub.io/components-and-groups.md) - [Incidents and maintenance](https://docs-dev.evohub.io/status-incidents-and-maintenance.md) - [Custom domain](https://docs-dev.evohub.io/status-page-custom-domain.md) - [Subscribers](https://docs-dev.evohub.io/subscribers.md) --- Source: https://docs-dev.evohub.io/components-and-groups.md # Components and groups Components are the services your visitors see a status for. This page covers adding them, setting their status, linking them to uptime monitors, grouping them, and how the uptime history on the public page is built. ## Add a component :::steps ### Open the Components tab Open your status page in **Status Page** and select the **Components** tab. ### Add it Click **Add component** (or **Add your first component**). ### Fill in the details - **Name**: what visitors see, for example `API`. - **Description** (optional): what the component covers. - **Group** (optional): the product it belongs to. See [Component groups](#component-groups). - **Status source**: **Set manually** or **Sync from monitor**. See [Status source](#status-source). - **Initial status**: the status a manual component starts with. ### Save Click **Add Component**. ::: To change a component later, use the pencil icon on its row. To remove it, use the trash icon and confirm. ## Component statuses A component is always in one of five statuses: | Status | Use it when | |--------|------------| | **Operational** | The service works normally. | | **Degraded Performance** | It works, but slowly or with errors for some requests. | | **Partial Outage** | Part of the service is down, or it is down for some customers. | | **Major Outage** | The service is down. | | **Maintenance** | Planned work is in progress. | The page's overall banner at the top shows the worst status across its components, for example **All Systems Operational** or **Major Outage**. ## Status source ### Set manually A manual component keeps the status you give it. Change it from the status control on the component's row in the **Components** tab. ### Sync from monitor A synced component follows an [uptime monitor](https://docs-dev.evohub.io/uptime-overview.md). Pick the monitor under **Uptime Monitor** when you add or edit the component. Its row then shows a **Synced** badge, and its status cannot be set by hand. The status follows the monitor: | Monitor state | Component status | |---------------|-----------------| | Up | Operational | | Degraded (slow) | Degraded Performance | | Down | Major Outage | | Paused | Maintenance | If EvoHub cannot read the monitor's state for a moment, the page keeps showing the component's last stored status. ### What visitors see during incidents and maintenance A published incident or a maintenance window in progress can show a component differently from its own status: - An active incident colors its affected components by its public impact: **Minor** shows as Degraded Performance, **Major** as Partial Outage, **Critical** as Major Outage. A component already in a worse state keeps the worse one. - A maintenance window in progress shows its affected components as **Maintenance**, even if their monitor reports them down. When that happens, the component's row in the console says so, for example "Major Outage on the page · API latency", so you see what visitors see. The status control on the row still shows the component's own status. See [Incidents and maintenance](https://docs-dev.evohub.io/status-incidents-and-maintenance.md). ## Order components Visitors see components in the order the console shows them. To reorder, drag a component by its handle onto another, or use the up and down arrows beside it. Dropping a component onto one in another group also moves it into that group. ## Component groups A group is a product: a name and the components in it. Groups only change what visitors see when the page uses the **Folded groups** or **A page per group** layout. With **One list**, groups help you organize the console but are not shown publicly. ### Create a group You can create a group in two ways: - Type a new name in a component's **Group** field. The field suggests the page's existing groups as you type. - Click **New group**, give it a **Name**, tick the components that belong in it, and click **Create group**. Components you pick move out of any group they were in. Group names are unique on a page. ### Manage groups Each group has controls in its heading: - **Add**: add a component straight into this group. - **Rename group** (pencil icon): renames the group everywhere, including its subscribers and its logo. Clear the name to ungroup its components. - **Move group up** / **Move group down**: change the order of groups. - **Starts open** (Folded groups layout only): whether the group is expanded when a visitor opens the page. Groups are folded by default. ### Groups as product pages With **A page per group**, the main page shows a card per group, and each group gets its own page. The group heading in the console shows that page's address, for example `/payments`, and how many subscribers follow it. - **Group logo**: click the image slot beside the group name to upload a logo for its card and page (PNG, JPEG or WebP, up to 2 MB). Use the **×** to remove it. - **Limit**: **A page per group** allows up to 3 groups. The console will not let you add a fourth group while this layout is on. A page that already has more groups cannot switch to it until you merge or ungroup some. - **Ungrouped components**: components in no group are not shown to visitors in this layout. The console warns you about them. Move them into a group, or give that set a name. ## Uptime history The public page shows a row of daily bars for each component, covering the page's **History window** (30 or 90 days, set on the **Appearance** tab), with the uptime percentage for that period. - For a component synced from a monitor, each day's bar comes from the monitor's measured uptime for that day. - For a manual component, a day is green unless an incident or maintenance window affecting that component overlapped it. Those days take the incident's or the maintenance's color. - Today's bar always shows the component's current status. Hover over a day to see what happened. In the **Folded groups** layout, each group shows one combined bar; open the group to see each component's bar. ## Related - [Status pages](https://docs-dev.evohub.io/status-pages-overview.md) - [Incidents and maintenance](https://docs-dev.evohub.io/status-incidents-and-maintenance.md) - [Uptime monitoring](https://docs-dev.evohub.io/uptime-overview.md) --- Source: https://docs-dev.evohub.io/status-incidents-and-maintenance.md # Status page incidents and maintenance Incidents and scheduled maintenance reach your status page from On-Call. You run the incident or the maintenance window in On-Call, and the status page shows it to visitors and keeps it in step. This page explains what gets published, how it stays up to date, and what visitors see. > [!NOTE] > The **Incidents** tab of a status page is read-only. It lists what On-Call has published to the page. To publish, update or remove anything, use On-Call. ## Publish an incident :::steps ### Open the incident in On-Call Go to **On-Call → Incidents** and open the incident. ### Publish it Click **Publish to status page**. ### Fill in what visitors see - **Status page**: which page to publish to. - **Public impact**: **None**, **Minor**, **Major** or **Critical**. It starts from the incident's severity; change it to what customers actually experience. - **Affected components**: tick the components the incident affects. - **Public message**: the first update visitors read. If you leave it empty, EvoHub uses the incident's summary, or "We are investigating an issue." when there is none. ### Publish Click **Publish**. The incident header then shows an **On status page** badge. ::: The public impact decides how the affected components look while the incident is open: **Minor** shows them as Degraded Performance, **Major** as Partial Outage, and **Critical** as Major Outage. **None** shows the incident without changing any component's color. Subscribers who follow the affected components, or the whole page, get an email. See [Subscribers](https://docs-dev.evohub.io/subscribers.md). ## Keep a published incident up to date Once an incident is published, On-Call keeps the status page in step: | You do this in On-Call | The status page shows | |------------------------|----------------------| | Click **Mark Identified** | A new update, "Issue identified.", with the status Identified. | | Add a timeline note | Your note as a new update, at the incident's current status. | | Edit or delete a timeline note | The same change on the published update. | | Change the incident's title | The new title. | | Change the incident's severity | A public impact derived from the new severity. | | Click **Resolve** | A final update, "Issue resolved.", and the incident moves to the page's history. | Each update is emailed to the incident's subscribers. To take an incident off the page, click **Unpublish** next to the **On status page** badge and confirm. ## Publish a postmortem After an incident is resolved, you can write a postmortem in On-Call with the **Postmortem** button on the incident. **Save internal** keeps it private. **Publish publicly** shows it under the incident on your status page. You can unpublish it later. The status page's **Incidents** tab shows a **Postmortem** badge on incidents that have one. ## Record a past incident You can publish an incident that has already been resolved, for example one you handled before you had a status page. Use **Publish to status page** on the resolved incident as usual. A past incident: - appears on the days it actually happened, in the uptime bars and the incident calendar - is marked **Past** in the status page's **Incidents** tab - does not email any subscriber ## Schedule maintenance :::steps ### Open Maintenance in On-Call Go to **On-Call → Maintenance** and click **New maintenance**. ### Describe the work Enter a **Title** and a **Description**, and choose the **Start** and **End** times. ### Decide what it affects - **Silence on-call alerts for this window**: mute paging while the work runs. - **Status page (optional)**: the page to publish the window on. - **Affected components**: the components the work touches, once a page is chosen. - **Silence uptime monitors (optional)**: monitors whose alerts should be muted during the window. ### Schedule Click **Schedule**. ::: When a window with a description is published to a status page, its subscribers get an email. ### What visitors see - **Before it starts**: a "Scheduled maintenance" notice near the top of the page when the start is within the next 7 days. The window also appears in the incident calendar. - **While it runs**: the affected components show **Maintenance**, and the window is listed at the top of the page. This happens automatically between the start and end times. - **After it ends**: it moves to the page's history. ### Update or finish maintenance Open the window under **On-Call → Maintenance**: - **Post update** adds a timeline update. It appears on the status page and is emailed to subscribers. - **Edit** changes the title, description or schedule, for example to extend a window that is running long. - **End now** (while it runs) or **Mark completed** finishes it. The status page adds a closing update, "This maintenance has been completed." ## What visitors see The public page shows incidents and maintenance in a few places: - **The banner and the top of the page**: the overall status, and anything happening now listed by name. - **Components**: affected components take the incident's or maintenance's color, as described above. - **Incident & Maintenance Calendar**: a month-by-month calendar of incidents and maintenance across the page's history window, with each one's updates and, if published, its postmortem. - **Permalinks**: every incident and maintenance window has its own address, `/incidents/` or `/maintenances/` on your status page domain. Emails to subscribers link there. On a page that uses **A page per group**, each group's page shows only the incidents and maintenance that affect that group's components. ## Related - [Alerts and incidents in On-Call](https://docs-dev.evohub.io/alerts-and-incidents.md) - [Components and groups](https://docs-dev.evohub.io/components-and-groups.md) - [Subscribers](https://docs-dev.evohub.io/subscribers.md) - [Silence windows](https://docs-dev.evohub.io/uptime-alerts-and-silence-windows.md#silence-windows) --- Source: https://docs-dev.evohub.io/subscribers.md # Status page subscribers Visitors can follow your status page in two ways: by email, or with a feed reader using the page's RSS or Atom feed. This page covers turning subscriptions on, what subscribers receive, and how to manage them. ## Turn on email subscriptions :::steps ### Open the Subscribers tab Open your status page in **Status Page** and select the **Subscribers** tab. ### Enable subscriptions Tick **Enable email subscriptions on the public page**. ### Save Click **Save settings**. ::: The public page then shows a **Subscribe** button in its header. Turn the setting off to hide the button and stop sending update emails. ## How visitors subscribe :::steps ### Open the form The visitor clicks **Subscribe** in the page header. ### Choose what to follow On a page that uses the **A page per group** layout, the main page lets them pick **Everything on this page** or one or more products. On a product's own page, they follow that product. On other layouts they follow the whole page. ### Enter an email address They enter their address and click **Subscribe**. ### Confirm EvoHub sends a confirmation email, "Confirm your subscription to *your page name*". The subscription starts only after they click the link in it. ::: Following a product covers every component in that group, including components you add to it later. A visitor who subscribes again with the same address keeps their existing subscription and adds what they picked the second time. The form has built-in protection against bots, and repeated sign-ups for the same address are limited, so a confirmation email is not re-sent on every click. ## What subscribers receive Confirmed subscribers get an email when: - an incident affecting what they follow is published to the page - an update is posted to that incident - maintenance affecting what they follow is scheduled with a description - an update is posted to that maintenance Subscribers who follow the whole page get every one of these. The subject reads like `[Acme Status] API latency — Investigating`, and the email links to the incident's or maintenance's own page on your status page. Past incidents, published after they ended, are not emailed. See [Incidents and maintenance](https://docs-dev.evohub.io/status-incidents-and-maintenance.md#record-a-past-incident). Emails are sent from `no-reply@status.evohub.io` with your page's name as the sender name. Replies are not read. Subscribers who need to reach you can use the support link in your page header; set it with **Support URL** on the **Appearance** tab. ### Unsubscribe Every email has an **Unsubscribe** link. One click removes the subscription. ## Manage subscribers The **Subscribers** tab lists everyone who has signed up, with: - their email address - a **pending** or **confirmed** badge (pending means they have not clicked the confirmation link yet) - what they follow: **Everything**, a group (shown as "*name* (group)"), or specific components - when they signed up To remove a subscriber, click the trash icon on their row. Seeing subscribers needs `status:subscriber:read`; removing them or changing the setting needs `status:subscriber:write`. Owners, Admins and Members have both. ## RSS and Atom feeds Every public status page has two feeds on its own domain: ```text https://status.example.com/feed.rss https://status.example.com/feed.atom ``` Visitors also find links to them at the bottom of the **Subscribe** form. Each feed item is one update to an incident or a maintenance window, newest first, up to the 50 most recent. Feeds need no sign-up, and they stop working if you turn off **Publicly accessible** for the page. ## Related - [Incidents and maintenance](https://docs-dev.evohub.io/status-incidents-and-maintenance.md) - [Components and groups](https://docs-dev.evohub.io/components-and-groups.md) - [Custom domain](https://docs-dev.evohub.io/status-page-custom-domain.md) --- Source: https://docs-dev.evohub.io/status-page-custom-domain.md # Status page custom domain Your status page is served on a subdomain you own, such as `status.example.com`. You connect it in the console, add two DNS records at your DNS provider, and EvoHub issues and renews the TLS certificate for you. A status page goes live only once a domain is connected. ## Before you start - Use a **subdomain**, such as `status.example.com`. Apex domains such as `example.com` are not supported. - You need access to your domain's DNS settings to add CNAME records. - Each status page has one custom domain. ## Connect a domain :::steps ### Open the Domain tab Open your status page in **Status Page** and select the **Domain** tab. ### Enter your domain Under **Custom Domain**, type the subdomain in **Your domain**, for example `status.example.com`, and click **Connect domain**. ### Copy the DNS records The console shows the records to add, each with a **Copy** button: | Type | Name | Value | |------|------|-------| | `CNAME` | `_acme-challenge.status.example.com` | The validation value the console shows for your domain | | `CNAME` | `status.example.com` | `cname.evohub-dns.com` | ### Add the validation record first At your DNS provider, add the `_acme-challenge` CNAME exactly as the console shows it. This record lets EvoHub prove the domain is yours and issue its certificate. ### Then add the routing record Once validation succeeds, add the CNAME from your subdomain to `cname.evohub-dns.com`. Adding the records in this order means visitors are never sent to the domain before its certificate is ready. ### Wait for it to go green The status badge updates on its own while validation is in progress. You can also click **Re-check**. When it reads **Verified · SSL active**, click **View public page**. ::: > [!TIP] > If your domain's DNS is on Cloudflare with the proxy turned on, set Cloudflare's SSL/TLS mode to **Full**, not **Flexible**. ## Domain statuses | Badge | Meaning | |-------|---------| | **Pending DNS** | The domain is connected in EvoHub, but it has not been validated yet. Check the DNS records. | | **Validating** | The domain was accepted and the certificate is being validated and issued. | | **Verified · SSL active** | The domain is live with a valid certificate. | | **Action needed** | The domain was blocked or its certificate expired. Check that both records still match what the console shows and click **Re-check**. If it stays this way, remove the domain and connect it again, or contact info@evosync.io. | DNS changes can take a while to spread, depending on your provider and the records' TTL. The console keeps checking while a domain is **Pending DNS** or **Validating**. ## TLS certificates EvoHub issues the certificate for your domain automatically once validation succeeds, and renews it before it expires. Keep the `_acme-challenge` record in place so renewal keeps working. There is nothing to upload. ## Change or remove a domain To move the page to a different domain, remove the current one and connect the new one. To remove a domain, click **Remove domain** on the **Domain** tab and confirm. The page stops being served on that address. You can then delete the two CNAME records at your DNS provider. Deleting a status page also removes its custom domain. ## Who can connect a domain Connecting, re-checking and removing a domain needs `status:page:write`, which Owners, Admins and Members have. ## Related - [Status pages](https://docs-dev.evohub.io/status-pages-overview.md) - [Subscribers](https://docs-dev.evohub.io/subscribers.md) - [Docs custom domain](https://docs-dev.evohub.io/docs-custom-domain.md) --- Source: https://docs-dev.evohub.io/uptime-overview.md # Uptime monitoring EvoHub Uptime checks your services on a schedule and tells you when one stops answering, answers wrongly or slows down. This page covers the monitor types, how to create a monitor, and the settings that decide when a monitor counts as down. ## How it works A monitor checks one target at a fixed interval. When a check fails, the monitor goes **Down**, EvoHub opens an uptime incident, and it alerts you through the monitor's [notification channels](https://docs-dev.evohub.io/uptime-alerts-and-silence-windows.md) and, if you choose, pages your team through On-Call. When a later check passes, the incident closes and EvoHub sends a recovery message. Monitors live under **Uptime** in the EvoHub console. If you pick a team in the team switcher, the **Monitors** list shows that team's monitors, and new monitors belong to that team. ## Monitor types | Type | Console label | What it checks | Target | |------|---------------|----------------|--------| | HTTP | **HTTP** | Requests a URL and checks the response code, and optionally the response body, headers and SSL certificate. | A full URL, such as `https://example.com/health` | | Keyword | **Keyword** | Requests a URL and checks that the response body contains a word or phrase. | A full URL | | TCP | **TCP** | Opens a TCP connection to a port. | `host:port`, such as `db.example.com:5432` | | Ping | **ICMP (Ping)** | Sends a ping to a host. | A host name or IP address | | Heartbeat | **Heartbeat (cron / job)** | Waits for your job to ping EvoHub, and alerts when a ping is late. | None. EvoHub gives you a URL to call. | Heartbeat monitors work the other way round from the rest. See [Heartbeat monitors](https://docs-dev.evohub.io/heartbeat-monitors.md). ## Create a monitor :::steps ### Open Uptime In the EvoHub console, open **Uptime** and click **New Monitor**. ### Name it and pick a type Enter a **Name** and choose the **Monitor Type**. The fields below change to match the type. ### Enter the target Fill in **URL**, **Host and port** or **Host**, depending on the type. ### Set the schedule Choose a **Check Interval** and a **Timeout**. ### Set the options you need Open the sections below the form to add HTTP settings, SSL monitoring, assertions, performance and SLA settings, tags, and alerting. All of them are optional. ### Create Click **Create Monitor**. EvoHub opens the monitor's page and starts checking. ::: ## Check interval and timeout | Setting | Options | Default | |---------|---------|---------| | **Check Interval** | 15 seconds, 30 seconds, 1 minute, 5 minutes, 15 minutes, 30 minutes, 60 minutes | 5 minutes | | **Timeout** | 10 seconds, 30 seconds, 60 seconds | 30 seconds | A check that gets no answer within the timeout fails. A monitor goes down on its first failed check, so you hear about an outage on the next check after it starts. EvoHub does not currently let you choose the regions checks run from. Each check's location is shown in the monitor's **Recent Checks** table. ## HTTP and keyword options **Expected Status Code** (HTTP monitors, default `200`): the response code that counts as up. **Keyword** (keyword monitors): the text the response body must contain. The match is case-sensitive. **HTTP Settings** (HTTP monitors): - **Method**: `GET`, `POST`, `HEAD` or `PUT`. - **Headers**: request headers to send, for example an API token your health endpoint needs. - **Request Body**: sent with `POST` and `PUT`. - **Basic Auth**: a username and password. ## Assertions HTTP monitors can check more than the status code. Open **Assertions** and click **Add assertion**. Every assertion must pass, or the check fails and the reason is recorded, for example "Expected status 200–299, got 503". | Assertion | Operators | Example | |-----------|-----------|---------| | **Status code** | is one of | `200-299,301` | | **Body** | contains, does not contain | `ok` | | **JSON value** | equals, does not equal, contains, exists | field `$.status` equals `up` | | **Header** | equals, contains | field `Content-Type` contains `application/json` | - A monitor can have up to 10 assertions. - JSON paths look like `$.status` or `$.items[0].id`. - Without a status code assertion, **Expected Status Code** still applies. - Up to 1 MB of the response body is read. ## Slow responses and SLA Open **Performance & SLA** on any monitor except a heartbeat: - **Slow if response takes longer than** (milliseconds, 1 to 60000): a passing check slower than this marks the monitor **Degraded** instead of Up. Leave it empty to turn it off. Degraded counts as up for uptime and SLA. - **Also page on-call when slow**: raises a lower-priority On-Call alert while the monitor is degraded, and resolves it when the monitor recovers. It needs a slow threshold and an escalation policy under **Alerting**. - **SLA target**: an uptime percentage such as `99.9`. When 30-day uptime falls below it, the monitor is marked as breaching its SLA. ## SSL certificate expiry HTTP monitors can watch the site's certificate. Open **SSL Monitoring**, turn on **Monitor SSL certificate**, and pick an **Alert threshold**: 7, 14, 30 (the default) or 60 days. When the certificate has fewer days left than the threshold, EvoHub sends a warning to the monitor's notification channels, for example "SSL certificate expires in 12 days (threshold: 30 days)". The warning is only sent while the monitor is up. The monitor's page shows the days left, and the **Monitors** list flags certificates with less than 30 days left. ## Tags Use **Tags** to group monitors, for example by service or environment. Press Enter or a comma to add a tag. The **Monitors** list can be filtered by tag. ## Alerting Under **Alerting**, pick an **Escalation Policy** to page your team through On-Call when the monitor goes down, and choose **Re-alert while still down**: every 5, 15 (the default), 30 or 60 minutes. See [Uptime alerts and silence windows](https://docs-dev.evohub.io/uptime-alerts-and-silence-windows.md). ## The monitor page Open a monitor from the list to see: - its current status, and uptime for the last 24 hours, 7 days, 30 days and 90 days - days left on the SSL certificate, if monitored - a **Response Time** chart - **Recent Checks**: status, location, latency, HTTP code, time and error of each check - **Incidents**: when each outage started and ended, how long it lasted, and its cause - **Notification Channels** linked to this monitor From the header you can **Pause** and **Resume** checking, **Edit** the monitor, or delete it. A paused monitor runs no checks. Individual check results are kept for 90 days. ### Monitor statuses | Status | Meaning | |--------|---------| | Up | The last check passed. | | Degraded | Checks pass, but slower than the slow threshold. | | Down | The last check failed. | | Unknown | The monitor has not been checked yet, or the last result was inconclusive. | The list also marks monitors that are **Paused**, or muted by a silence window (**Maintenance**). ## Show a monitor on a status page A status page component can follow a monitor, so your public page updates on its own when the monitor goes down. See [Components and groups](https://docs-dev.evohub.io/components-and-groups.md#sync-from-monitor). ## Permissions and billing Creating, editing, pausing and deleting monitors needs `uptime:monitor:write`. Owners, Admins and Members have it; Viewers can see monitors but not change them. See [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md). Uptime checks are billed by usage. Heartbeat monitors are not metered. See [How billing works](https://docs-dev.evohub.io/how-billing-works.md). ## Related - [Heartbeat monitors](https://docs-dev.evohub.io/heartbeat-monitors.md) - [Uptime alerts and silence windows](https://docs-dev.evohub.io/uptime-alerts-and-silence-windows.md) - [Weekly email summary](https://docs-dev.evohub.io/weekly-summary.md) --- Source: https://docs-dev.evohub.io/heartbeat-monitors.md # Heartbeat monitors A heartbeat monitor watches something that runs on a schedule, such as a cron job, a nightly backup or a queue worker. Instead of EvoHub checking a target, your job calls a unique URL each time it runs. If a call does not arrive in time, EvoHub marks the monitor down and alerts you, the way it would for any other monitor. ## When to use one Use a heartbeat monitor when the thing you care about has no address to check, or when "it ran" is what matters: - scheduled jobs and cron tasks - backups and data exports - background workers that should check in regularly - anything that fails silently by simply not running ## Create a heartbeat monitor :::steps ### Start a new monitor In **Uptime**, click **New Monitor**, enter a **Name**, and set **Monitor Type** to **Heartbeat (cron / job)**. ### Set the expected ping interval Under **Expected ping interval**, enter how often your job runs, as a number and a unit: minutes, hours or days. For example, `1 days` for a nightly job or `3 hours` for one that runs every three hours. ### Set the grace period Under **Grace period**, enter how late a ping may be before the monitor goes down, in minutes or hours. Allow for how long your job takes and how much its start time varies. ### Choose alerting Optionally pick an **Escalation Policy** under **Alerting** to page your team through On-Call. You can also add **Tags**. ### Create Click **Create Monitor**. The monitor's page shows its ping URL. ::: ## Ping the URL The monitor's page has a **Heartbeat ping URL** card with the address to call and a **Copy** button. The URL looks like `https://evohub.io/ping/`; always copy the exact one from the console. Call it at the end of your job, after the work has succeeded. `GET`, `POST` and `HEAD` all work, and no API key or other credential is needed. :::code-group ```bash [cron] # Runs at 02:00 every day, and pings only if the backup succeeds 0 2 * * * /usr/local/bin/backup.sh && curl -fsS https://evohub.io/ping/ ``` ```bash [shell script] #!/usr/bin/env bash set -euo pipefail ./run-export.sh curl -fsS --retry 3 https://evohub.io/ping/ ``` ::: A successful ping returns `200` with `{"status":"ok"}`. An unknown URL returns `404`. The card also shows **Last ping**, so you can confirm your job is reaching EvoHub. > [!WARNING] > The ping URL is the only credential. Anyone who has it can reset the monitor's timer. Keep it out of public repositories and logs. ## When a heartbeat goes down The monitor is down when the time since the last ping is longer than the expected interval plus the grace period. For example, with an interval of 1 day and a grace period of 30 minutes, a job that last pinged at 02:05 on Monday is marked down if no ping arrives by 02:35 on Tuesday. - A new monitor that has never been pinged gets one full interval plus grace, counted from when you created it, before it can go down. - When the monitor goes down, EvoHub opens an incident with the reason, for example "no heartbeat received for 25h0m0s", and alerts your notification channels and On-Call, just like a failed HTTP check. - While it stays down, you are alerted again on the monitor's **Re-alert while still down** schedule. - The next ping brings the monitor back up and closes the incident. ## How heartbeats differ from other monitors - There is no target, timeout, assertion, slow threshold or SLA target. - The monitor page records state changes rather than every ping, so **Recent Checks** shows when it went down and came back. - Heartbeat monitors are not metered for billing. ## Pause or silence a heartbeat **Pause** on the monitor's page stops it from being evaluated, for example while a job is switched off. To mute alerts for a planned window instead, use a silence window. See [Uptime alerts and silence windows](https://docs-dev.evohub.io/uptime-alerts-and-silence-windows.md#silence-windows). ## Related - [Uptime monitoring](https://docs-dev.evohub.io/uptime-overview.md) - [Uptime alerts and silence windows](https://docs-dev.evohub.io/uptime-alerts-and-silence-windows.md) - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) --- Source: https://docs-dev.evohub.io/uptime-alerts-and-silence-windows.md # Uptime alerts and silence windows When a monitor goes down, EvoHub can tell you in two ways: through **notification channels** (Slack or a webhook) and by **paging On-Call** through an escalation policy. You can use either or both. Silence windows mute those alerts while you do planned work. ## What triggers an alert | Event | Notification channels | On-Call | |-------|----------------------|---------| | Monitor goes down | Down alert with the reason | Alert raised (if an escalation policy is set) | | Still down after the re-alert interval | Repeated down alert | Alerted again | | Monitor recovers | Recovery message with how long it was down | Alert resolved | | SSL certificate below its threshold | Warning with the days left | No | | Monitor is slow (degraded) | No | Lower-priority alert, if **Also page on-call when slow** is on | The reason in each alert is the failing check's error, for example "Expected status 200–299, got 503" or "keyword "ok" not found in response body". ## Notification channels A notification channel is a destination for uptime alerts. You create channels once, then link them to the monitors that should use them. ### Create a channel :::steps ### Open Channels In **Uptime**, select **Channels** and click **Add Channel**. ### Name it and pick a type Enter a **Name**, for example `Engineering Slack`, and choose **Slack** or **Webhook**. ### Enter the destination - **Slack**: paste a Slack incoming-webhook URL (it starts with `https://hooks.slack.com/`) into **Webhook URL**. - **Webhook**: enter your endpoint's **Webhook URL**. Optionally set a **Secret Header** value so your endpoint can check that requests come from EvoHub. Private and local network addresses are not allowed. ### Save Click **Add Channel**. ::: To delete a channel, use the trash icon next to it on the **Channels** page. EvoHub does not currently offer an email notification channel for Uptime. To be called, texted or emailed, page your team through On-Call instead. ### Link a channel to a monitor Open the monitor, find **Notification Channels**, and click **Add channel**. Pick one of your channels. A monitor only alerts the channels linked to it. To unlink one, use the remove icon next to it. ### Webhook payload A webhook channel receives a `POST` with a JSON body. When you set a secret, it is sent in the `X-Webhook-Secret` header. Your endpoint should answer with a `2xx` status. :::tabs ::tab{title="Down"} ```json { "event": "triggered", "monitor_id": "mon_6f1c2e4a-0b7d-4d1e-9a52-2c8e7f3b1a90", "monitor_name": "Checkout API", "monitor_url": "https://api.example.com/health", "cause": "Expected status 200–299, got 503", "timestamp": "2026-10-09T14:30:00Z" } ``` ::tab{title="Recovered"} ```json { "event": "resolved", "monitor_id": "mon_6f1c2e4a-0b7d-4d1e-9a52-2c8e7f3b1a90", "monitor_name": "Checkout API", "monitor_url": "https://api.example.com/health", "timestamp": "2026-10-09T14:42:10Z", "down_duration_seconds": 730 } ``` ::: SSL expiry warnings are also sent with `"event": "triggered"`, with the warning as the `cause`. ## Page your team through On-Call To have a down monitor page whoever is on call, give it an escalation policy: :::steps ### Edit the monitor Open the monitor and click **Edit** (or set this when you create it). ### Pick an escalation policy Under **Alerting**, choose an **Escalation Policy**. **None — do not alert on-call** turns paging off. ### Choose how often to re-alert Set **Re-alert while still down**: every 5, 15, 30 or 60 minutes. ::: When the monitor goes down, On-Call receives a high-severity alert titled `Monitor Down: `, with the failure reason as its description. On-Call then notifies people as the [escalation policy](https://docs-dev.evohub.io/escalation-policies.md) says. Repeats for the same outage are grouped into the same alert, and the alert resolves on its own when the monitor recovers. If you turned on **Also page on-call when slow**, a slow monitor raises a medium-severity alert titled ` is slow`, which resolves when the monitor is no longer degraded. Picking a policy needs permission to read On-Call escalation policies (`oncall:escalation:read`). Without it, the console explains that the monitor will not page anyone. ## Silence windows A silence window mutes alerts for a while, for example during a deployment or planned maintenance. Monitors keep checking while silenced. There are three ways to silence alerts. ### Silence the whole organization Use this to mute everything at once. :::steps ### Open the silence menu Click the bell icon, **Silence notifications**, in the top bar of the EvoHub console. ### Pick a duration Under **Silence uptime & on-call**, choose **30 min**, **1 hour**, **4 hours** or **8 hours**. ::: While the silence lasts, a banner reads "Notifications silenced — uptime & on-call alerts are muted", and the bell menu shows the time left. Uptime notification channels and On-Call paging are both muted for the whole organization. A silence always ends on its own; there is no open-ended option. To end it early, open the bell menu and click **Resume notifications**. Silencing needs `uptime:silence:write`, which Owners, Admins and Members have. People without it do not see the bell. ### Silence during a maintenance window When you schedule maintenance in **On-Call → Maintenance**, the **Schedule maintenance** form has two silencing options: - **Silence on-call alerts for this window**: while the window is in progress, On-Call does not page anyone, and Uptime does not open incidents or send alerts for any monitor in your organization. - **Silence uptime monitors (optional)**: pick monitors whose notification channels stay quiet between the window's start and end. These monitors show **Maintenance** in the **Monitors** list while muted. See [Incidents and maintenance](https://docs-dev.evohub.io/status-incidents-and-maintenance.md#schedule-maintenance) for publishing the same window on a status page. ### Silence through the API You can also set silences with an [API key](https://docs-dev.evohub.io/api-keys-and-scopes.md) that has the `uptime:silence:write` scope. Mute Uptime's notification channels across the organization for a number of minutes: ```bash curl -X PUT https://evohub.io/api/v1/silence \ -H "X-API-Key: $EVOHUB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"minutes": 60}' ``` `DELETE /api/v1/silence` lifts it, and `GET /api/v1/silence` returns `silenced_until`. This call mutes Uptime's notification channels only. The bell menu in the console also silences On-Call. Mute one monitor's notification channels for a fixed window: ```bash curl -X PUT https://evohub.io/api/v1/monitors//silence \ -H "X-API-Key: $EVOHUB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"starts_at": "2026-10-10T22:00:00Z", "ends_at": "2026-10-10T23:30:00Z"}' ``` `GET` on the same path returns the monitor's silence, and `DELETE` removes it. ## Related - [Uptime monitoring](https://docs-dev.evohub.io/uptime-overview.md) - [Escalation policies](https://docs-dev.evohub.io/escalation-policies.md) - [Notifications in On-Call](https://docs-dev.evohub.io/notifications.md) - [Weekly email summary](https://docs-dev.evohub.io/weekly-summary.md) --- Source: https://docs-dev.evohub.io/weekly-summary.md # Weekly email summary The weekly email summary gives your organization's owners and administrators a short report every Monday: how your monitors did over the previous week. It is off until an Owner or Admin turns it on. ## What the email contains The summary covers the last full week, Monday to Sunday (UTC), across all of your organization's monitors: - **Average uptime** - **Incidents**: how many outages there were - **Total downtime** - **Avg response time** - the **Longest outage**, if there was one - a table of monitors with each one's uptime and downtime, the ones with the most downtime first - any monitor that is **Down right now**, called out at the top - a link to open the uptime dashboard An organization with no monitors gets no email. ## Who receives it The summary goes to the organization's **Owners and Admins**, because they can see every monitor, including team monitors. Members and Viewers do not receive it. It is sent every Monday from 09:00 UTC, by email only. It is not posted to Slack or webhook channels, and it is sent even while notifications are silenced, because silencing applies to alerts. ## Turn the summary on or off :::steps ### Open the setting In **Uptime**, select **Channels**. The **Weekly email summary** card is on that page. ### Switch it on Tick **Send the weekly summary for this organization**. Only Owners and Admins can change this; everyone else sees "Only owners and administrators can change this." ::: Clear the same box to stop the summary for everyone. ## Preview it Owners and Admins can click **Send me a preview** on the card to get last week's summary right away. Only you receive the preview; its subject starts with `[Preview]`. You can send one preview a minute. ## Stop receiving it yourself An Owner or Admin who does not want the email can opt out without turning it off for everyone else: - In the console, clear **Send it to me** on the **Weekly email summary** card. - From the email, click **Unsubscribe** at the bottom and confirm. To start receiving it again, tick **Send it to me**. ## Related - [Uptime monitoring](https://docs-dev.evohub.io/uptime-overview.md) - [Uptime alerts and silence windows](https://docs-dev.evohub.io/uptime-alerts-and-silence-windows.md) - [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md) --- Source: https://docs-dev.evohub.io/retro-boards.md # Retro boards A retro board is where your team looks back on a sprint, a release or an incident. People add cards to columns, vote and comment on them, and turn what they agree on into action items. This page covers creating a retro, running it, inviting guests, and closing it. Retro boards live under **Retro** in the EvoHub console. ## Create a retro board :::steps ### Start a new board In **Retro**, click **New Board**. ### Name it Enter a name, for example `Q2 Sprint 4 Retro`, and optionally a description. ### Choose who sees it - **Whole organization**: everyone in the organization. - ***Team name* (team only)**: that team and your administrators. You can share it with other teams afterwards. - **Only people I add**: nobody but you until you add people from the retro's **People** panel. Administrators always see it. ### Pick a template | Template | Columns | |----------|---------| | **Blank** | None. Add your own. | | **Start / Stop / Continue** | Start · Stop · Continue | | **Mad / Sad / Glad** | Mad · Sad · Glad | | **4 Ls** | Liked · Learned · Lacked · Longed For | | **What went well / What didn't / Action items** | What went well · What didn't go well · Action items | ### Create Click **Create**. The board opens, ready for cards. ::: The **Retro** home page lists your boards with their status (**active** or **closed**) and counts of total, active and closed boards and open action items. If you pick a team in the team switcher, the list shows that team's retros. ## Columns People who can manage the board can shape it: - **Add a column**: click **Column**, give it a name, optionally pick an emoji and a color, and click **Add**. - **Rename** a column, or change its emoji and color, from its heading. - **Reorder** columns by dragging them by the handle. - **Delete** a column. All of its cards are deleted with it. ## Cards Type in a column's box ("What's on your mind?") to add a card. Cards do not show who wrote them, so people can be candid. On each card you can: - **Vote**: click **Agree** (thumbs up) or **Disagree** (thumbs down). The card shows the total score. Each person has one vote per card; clicking the same direction again removes your vote, and clicking the other direction switches it. - **React** with an emoji: 👍 ❤️ 🎉 🤔 😢 🚀 💯 🔥. Click a reaction again to remove yours. - **Comment**: open the card's comments and write one. - **Edit** or **Delete** it, with the icons on the card. Double-clicking a card's text also starts editing. You can edit and delete your own cards, and delete your own comments. People whose role can manage retros (`retro:board:write`, held by Owners, Admins and Members) can edit or delete anyone's cards and delete anyone's comments, which is how you moderate a board. ### Merge duplicate cards When two cards say the same thing, drag one onto the other in the same column. Confirm **Merge** and the dragged card's text is combined into the target card, and the dragged card is removed. ### Turn voting or comments off Use the **Voting on/off** and **Comments on/off** buttons in the board header. Both are on for a new board. Turning one off hides it on every card. ## Action items The **Action Items** section below the columns holds the follow-ups the team agrees on. - Click **Add**, describe the action, optionally type an **Assignee** (free text, so it can be a person, a team or anything else), and click **Create**. - Tick an action item to mark it done; tick again to reopen it. - The section shows how many are still open. Action items stay with the board after it is closed, and the **Retro** home page counts the open ones across all your boards. ## Run it live Everyone on the board sees new cards, votes, reactions, comments and action items appear as they happen, without reloading. The header shows how many people are online. ## Invite guests with a share link People outside your EvoHub organization can join a retro through a share link. :::steps ### Copy the link Click **Share** in the board header. The link is copied to your clipboard. ### Send it Paste it into your chat or meeting invite. ::: Guests open the link without an EvoHub account. They can add cards, vote, react and comment. They cannot manage the board, add action items, edit or delete cards from the board, or export it. Anyone with the link can join, so share it only with the people you mean to. ### Share link modes A share link has three modes: | Mode | What guests can do | |------|-------------------| | `participate` (default) | View the board and take part, as described above. | | `read_only` | View the board only. | | `disabled` | Nothing. The link does not open the board. | The console always creates links in `participate` mode. To change the mode, or to revoke every existing link by issuing a new one, use the API with an [API key](https://docs-dev.evohub.io/api-keys-and-scopes.md) that has the `retro:board:write` scope: ```bash # Make the link read-only curl -X PATCH https://evohub.io/api/v1/retro/boards//share \ -H "X-API-Key: $EVOHUB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"mode": "read_only"}' # Issue a new link; every old link stops working at once curl -X POST https://evohub.io/api/v1/retro/boards//share/rotate \ -H "X-API-Key: $EVOHUB_API_KEY" ``` The rotate call returns the new `share_url`. ## Who can see a retro Click **People** in the board header to open **Who can see this retro**. - **Who sees it without being named**: **Everyone in the organization**, the retro's team, or **Only the people named here**. - **People**: add someone from your organization with **Add a person…**, or remove them. Members of the retro's team are listed as **via team**. - **Teams**: give another team access with **Share with a team…**, or **Unshare** it. Naming someone only lets them see the retro. What they can do on it comes from their organization role. Owners and Admins can see every retro. ## End and reopen a retro When you are done, click **End Retro** and confirm. The board is closed: a banner says "This retrospective has been closed. No further changes can be made.", and nobody can add or change cards, votes, comments or action items. To continue, click **Reopen Retro**. ## Export to PDF Click **Export PDF** in the board header to download the board, with its columns, cards and action items, as a PDF. ## Delete a retro board On the **Retro** home page, hover over a board and click the trash icon, **Delete retro**, then confirm. Deleting a board removes its columns, cards, votes, comments and action items. Only Admins and Owners can delete retros (`retro:board:delete`). Members can create, run and close them, but not delete them. ## Permissions | Action | Permission | Built-in roles | |--------|-----------|----------------| | See retros | `retro:board:read`, `retro:card:read`, `retro:action:read` | Everyone | | Create a retro; manage columns, settings, people, sharing; end and reopen; moderate cards | `retro:board:write` | Owner, Admin, Member | | Add cards, vote, react, comment | `retro:card:write` | Owner, Admin, Member | | Add and update action items | `retro:action:write` | Owner, Admin, Member | | Delete a retro | `retro:board:delete` | Owner, Admin | Viewers can open the retros they can see, but the board is read-only for them. See [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md). ## Related - [Boards](https://docs-dev.evohub.io/boards.md) - [Organizations and teams](https://docs-dev.evohub.io/organizations-and-teams.md) - [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md) --- Source: https://docs-dev.evohub.io/boards.md # Boards EvoHub Board is a Kanban board for your team's work: lists you drag cards between, with one extra rule an engineering team needs. If you want it, work cannot reach the done list until someone other than the people doing it has approved it. This page covers creating boards, working with cards, the review gate, who can see a board, and the archive. Boards live under **Board** in the EvoHub console. A board holds lists, and lists hold cards. There is no project layer: the board is the project. ## Create a board :::steps ### Start a new board In **Board**, click **New board** (or **Create a board**). ### Name it Enter a **Name**, for example `Q3 platform work`. ### Choose a card prefix Every card gets a reference made of the board's prefix and a number, such as `DEV-1`, `DEV-2`. Type two to six letters, or leave it blank and EvoHub picks one from the board's name. The prefix must be unused in your organization. ### Pick a template Choose how the board starts. See [Templates](#templates). ### Choose who sees it **Private** (only people you add, the default), **One team** (everyone in the team you pick, and nobody else), or **Everyone in the organization**. You can only pick a team you belong to; administrators can pick any team. ### Create Click **Create board**. ::: > [!WARNING] > A board's card prefix cannot be changed later. Card references are meant to be written into commit messages and stay valid, so choose it with care. ### Templates | Template | Lists | Review gate | |----------|-------|-------------| | **Blank** | None. Add your own. | No | | **Kanban** | To Do · In Progress · Done | No | | **Kanban with review** | To Do · In Progress · Review · Done | Yes | | **Sprint** | Backlog · To Do · In Progress · Review · Done | Yes | | **Bug triage** | Reported · Confirmed · Fixing · Verify · Closed | Yes | | **Product roadmap** | Ideas · Planned · Building · Shipped | No | | **Support queue** | Triage · Investigating · Waiting on customer · Resolved | No | | **Content** | Idea · Drafting · Review · Published | Yes | | **Hiring pipeline** | Applied · Screening · Interview · Offer · Hired | No | Templates already mark the right lists as the review list and the done list. You can change that afterwards. ## Lists - **Add a list**: click **Add a list** at the end of the board, type a name and click **Add list**. - **Reorder** lists by dragging them by their handle. - From a list's menu: **Rename**, set what the list is for (**An ordinary list**, **The review list** or **The done list**), choose a **Celebration…**, or **Archive list**. Archiving a list archives all the cards in it. See [The archive](#the-archive). ### The done list Mark one list as **The done list** to tell EvoHub that cards there are finished. A card that reaches it gets a **Completed** badge, and it is archived automatically after a while. See [Auto-archive](#auto-archive). ### Celebrations A list can play a short effect when someone drops a card into it: **Confetti**, **Hearts**, **Sparkles**, **Fireworks**, **Balloons**, **Bubbles**, **Petals**, **Applause** or **A ring of light**, or **Nothing**. It plays once, only on the screen of the person who moved the card, and not at all if their device is set to reduce motion. ## Cards Click **Add a card** at the top or bottom of a list, type a title ("What needs doing?"), and press Enter. Drag cards within a list or to another list. Cards never move between boards. Click a card to open it. A card can have: | Field | What it is for | |-------|---------------| | **Description** | Details of the work. | | **Assignees** | The people doing it. Click **Add** and pick someone from your organization. | | **Labels** | Board-wide tags, such as `urgent` or a sprint name. | | **Colour** | A colour stripe on the card's tile. | | **Due date** | When it is due. | | **Checklist** | Steps, ticked off one by one. The section shows progress, such as `Checklist · 2/5`. | | **Linked work** | Links to things in other EvoHub products, such as an incident, a monitor, a retro or a status page. | | **Comments** | The conversation about the card. | | **History** | Everything that has happened to the card. Click **Show**. | To archive a card, open its options menu and choose **Archive card**. ### Comments You can edit and delete your own comments; an edited comment is marked "edited". People whose role can manage boards (`board:board:write`) can also delete anyone's comment, but nobody can edit someone else's. ### Labels Labels belong to the board, so the same label can go on many cards. Create them from a card's **Labels** section with **New**, or on the board's **Labels** page with **New label**. Label names are unique on a board, ignoring case. Deleting a label removes it from every card. ## Find cards Use **Search cards…** at the top of the board to search card titles. You can also type a card reference, such as `DEV-42`, or just its number. Click **Filters** to narrow the board by label, by due date (**Overdue**, **Due today**, **Due this week**, **No due date** or **Between two dates…**), or by when cards were created. **Clear all** removes the filters. ## The review gate The review gate makes sure work is checked by someone other than the people who did it before it counts as done. It is opt-in: a board only has a gate if one of its lists is marked **The review list**. How it works: 1. When a card enters the review list, it is **In review** (pending). 2. A reviewer opens the card and clicks **Approve**, or **Send back** with a note on what needs doing. 3. An approved card can move to the done list. If the board has a review list, a card cannot enter the done list without approval. 4. **Send back** returns the card to the list it came from, and the note is added to the card's comments. The rules: - **An assignee cannot approve their own card.** Anyone else with review permission can, including the person who created it. If you try, the console explains: "You cannot approve work assigned to you." - A card with no assignees can be approved by anyone with review permission. - Sending a card back is not restricted the same way: assignees can pull back their own work. - A card that is in review cannot be moved out of the review list except by approving or sending it back. - If an approved card is moved back to an ordinary list, its approval is cleared and it needs reviewing again. - While you drag a card, lists it cannot enter say why: "Needs review first" or "Waiting for review". Approving and sending back need `board:review:write`. Choosing which list is the review list or the done list needs both `board:review:write` and `board:board:write`. ## Who can see a board A board decides who **sees** it. What people can **do** on it comes from their organization role. | Visibility | Who sees it | |------------|------------| | **Private** | Only the people and teams on the board's **Members** page. | | **One team** | Everyone in that team, plus anyone named on the board. | | **Everyone in the organization** | Everyone in the organization. | Owners and Admins can see every board, including private ones. ### Share a board Open the board's **Members** page: - **Add member**: choose a person and click **Add to board**. They can now see the board. - **Share with a team**: pick a team. Everyone in it can see the board. Use **Unshare** to undo. - Remove someone from the board from their row. Members of the board's own team are listed as **Via team**. Adding people needs `board:member:write`. To change a board's name, description or visibility, open its **Settings** page and click **Save changes**. That needs `board:board:write`. ## The archive Cards leave a board through the archive, whether you archive them by hand, archive their list, or they are archived automatically. Open the board's **Archive** page to search archived cards and **Restore** any of them to the board. ### Auto-archive Cards that sit in the board's done list are moved to the archive on their own after a number of days. Set it under **Archive after** on the board's **Settings** page: between 1 and 365 days (365 is the default). It cannot be turned off. Only the done list is affected; cards in other lists are never archived automatically, however old they are. ### Retention Archived cards stay readable and restorable for two years. Two years after being archived, a card is deleted for good, with its comments, checklist and history. ## Archive a board Boards are archived, not deleted. On the board's **Settings** page, click **Archive board** and confirm. The board disappears from everyone's list of boards. Its lists, cards and comments are kept, but there is no way back to it from the console. Only Admins and Owners can archive boards (`board:board:delete`). Members see the button disabled with the note "Only admins can archive boards". ## Live updates Everyone looking at a board sees cards and lists change as others work, without reloading. ## Permissions | Action | Permission | Built-in roles | |--------|-----------|----------------| | See boards, lists, cards and the archive | `board:board:read`, `board:list:read`, `board:card:read` | Everyone | | Create a board; rename it, change its visibility and auto-archive; manage labels; delete others' comments | `board:board:write` | Owner, Admin, Member | | Add, rename, reorder and archive lists; set a celebration | `board:list:write` | Owner, Admin, Member | | Add and change cards, and everything on them; restore from the archive | `board:card:write` | Owner, Admin, Member | | Add people and teams to a board | `board:member:write` | Owner, Admin, Member | | Approve or send back cards in review | `board:review:write` | Owner, Admin, Member | | Archive a board | `board:board:delete` | Owner, Admin | Viewers can see boards that are visible to them but cannot change anything. See [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md). ## Related - [Retro boards](https://docs-dev.evohub.io/retro-boards.md) - [Organizations and teams](https://docs-dev.evohub.io/organizations-and-teams.md) - [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md) --- Source: https://docs-dev.evohub.io/docs-overview.md # Docs overview EvoHub Docs publishes your product documentation as a site of its own, on your own domain. You write pages in Markdown in the EvoHub console (or in a GitHub repository), arrange them into sections, and publish them when they are ready. This documentation is itself an EvoHub Docs site. The Docs console lives at [https://evohub.io/docs-admin](https://evohub.io/docs-admin). ## How a site is organized | Concept | What it is | | --- | --- | | **Docs site** | One documentation site, with its own name, logo, theme, domain and settings. | | **Version** | A content space of the site, such as `v1` and `v2`. Every site starts with one version, and most sites never need more. | | **Section** | A heading in the navigation that groups pages. Sections have no content of their own. | | **Page** | One Markdown document. A page sits in a section or at the top level. | | **API reference** | An OpenAPI document rendered as reference pages. See [API references and AI](https://docs-dev.evohub.io/api-references-and-ai.md). | | **Snippet** | Reusable Markdown included in pages with `{{snippet:name}}`. See [Writing pages](https://docs-dev.evohub.io/writing-pages.md). | A site can belong to the whole organization or to one team. A team site is visible only to that team and to organization Admins and Owners. ## Who can do what Docs has no per-site members or roles. What you can do on every docs site of your organization comes from your organization role: | Permission | Lets you | | --- | --- | | `docs:site:read` | Open sites, pages, drafts, history and analytics. | | `docs:page:write` | Write and edit pages, sections, snippets, API references and files; open change requests. | | `docs:page:publish` | Publish and unpublish, approve and merge change requests, roll back. | | `docs:site:write` | Create sites and change their settings: domain, theme, navigation, review, access, GitHub. | | `docs:site:delete` | Delete a whole site. | Members get everything except deleting a site; Viewers can read. Deleting a site is for Admins. To split the work, build a custom role from the **Docs writer**, **Docs reviewer** or **Manages docs sites** presets. See [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md). ## Create a site :::steps ### Open Docs In the EvoHub console, open **Docs**. The **Docs sites** page lists every site you can reach. ### Create the site Choose **New site**, give it a **Name** and an **Address**, and select **Create site**. The address is a short identifier (lowercase letters, digits and hyphens) that is unique across EvoHub. It names the site in the console and the API; it is not a web address. You can also start with **From template** or bring existing docs in with **Import**. See [Import and export](https://docs-dev.evohub.io/import-and-export.md). ### Add pages In the site editor, add sections and pages from the navigation tree on the left. New pages start as drafts. ### Publish Publish each page when it is ready, then turn on **Published** for the whole site in **Settings**. ### Connect a domain A site answers only on its own domain. Until you connect one, use **Preview** in the console. See [Connect a custom domain](https://docs-dev.evohub.io/docs-custom-domain.md). ::: ## Drafts and publishing Every page has two copies: - The **draft** is what you edit. **Save draft** stores your changes without showing them to anyone. - The **live copy** is what readers see. **Publish** (or **Publish changes** for a page that is already live) copies the draft to the live copy. Editing a published page changes nothing on the site until you publish again. The editor marks a page with **Unpublished changes** while the two copies differ. **Unpublish** takes a page off the site and keeps its draft. Two things must be true before a reader sees a page: the page is published, and the site itself is **Published** (in **Settings**, **General**). A site that is not published is visible only to your organization. If the site requires review, pages go live only through an approved change request. See [Review and change requests](https://docs-dev.evohub.io/review-and-change-requests.md). ### Editing together The editor shows when someone else is editing the same page. If two people save the same page, the second save is stopped and you choose to keep your version, take theirs, or copy what you need across. Nothing is overwritten silently. ## Revisions and rollback Every publish is kept as a revision; the newest 50 per page are kept. Open the page's publish history to see them. For each revision you can: - **Restore** — copy that version into the draft. It does not publish. - **Roll back** — make that version live again right away. On a site that requires review, a rollback opens a change request instead. A rollback refuses to overwrite draft work that was never published unless you confirm that you want to discard it. ## Navigation The editor's navigation tree sets the order readers see: move pages and sections up and down, rename sections, and move pages between sections. Deleting a section never deletes pages; its pages move to the top level. Under **Settings**, **Navigation** you shape the rest of the site: - **Tabs** across the top, each showing some sections or linking somewhere else. - **Header** links and an optional highlighted button. - **Footer** columns of links and social links. - **Section icons** shown before each section title. - **Page footer**: show **Last updated** and an **Edit this page** link built from a template such as `https://github.com/acme/docs/edit/main/{slug}.md`. - **Home page**: the **First page**, **A chosen page**, or a **Landing page** with a headline, subtitle, button and cards. Navigation and theme changes apply to the live site as soon as you save them. Each page also has its own settings (the settings button in the editor): SEO title, description, link preview image, canonical address, **Hide from search engines**, **Sidebar title**, icon and **Hide from navigation**. Page settings go live when the page is published. ## Theme **Settings**, **Theme** sets fonts (loaded from Google Fonts only when you pick one), the accent colour, corner radius, background, code theme, colour scheme (follow the reader's device, or always light or dark) and favicon. The logo is under **Settings**, **General** (PNG, JPEG or WebP, up to 2 MB). ## Versions A version is a separate set of sections, pages, snippets, API references and redirects. The logo, theme, layout, domain, files and languages are shared by every version. Manage them in **Settings**, **Versions**: - **New version** creates an empty version or a copy of an existing one. - **Make default** chooses which version the short addresses (`/page`) show. Other versions live under `//page`. - Each version can be published or not, tagged (for example **Latest** or **LTS**), marked **Deprecated** with a banner that points readers to the default version, or hidden from search engines. A site holds up to 20 versions. Readers switch versions from the site's version switcher. ## Languages Pages are written in the site's default language first. In **Settings**, **Languages** add up to 10 other languages; each page can then be translated in the editor's language tabs. A translation keeps its page's address under a language prefix (`/de/page`) and its place in the navigation, and is published like any other page. The **Translations** page shows, for every page, which languages are missing, outdated (the source changed since) or up to date. For untranslated pages, choose to **Show the default language with a note** or **Hide untranslated pages**. Under **Interface text** you can reword the site's own labels, such as "On this page", per language. EvoHub does not currently translate pages automatically; translations are written by people. ## Other site tools - **Assets** — images, PDFs and icons uploaded to the site. See [Writing pages](https://docs-dev.evohub.io/writing-pages.md#files-and-images). - **Redirects** — keep old links working. A redirect applies only where no page answers. Moving a published page adds one automatically. - **Link health** — finds broken links on the published site. Links are checked after every publish; **Check now** runs it on demand, optionally including external links. - **Activity** — who changed what on the site. - **Notifications** — review requests and replies on your change requests. ## Delete a site **Settings**, **General**, **Delete site** removes the site with every page and revision. Only Admins (or a custom role with `docs:site:delete`) can do this. Download an export first if you want a copy. See [Import and export](https://docs-dev.evohub.io/import-and-export.md). ## Billing Docs is billed by usage, not per seat: any number of people can write and review. See [How billing works](https://docs-dev.evohub.io/how-billing-works.md) for what is metered. ## Related - [Writing pages](https://docs-dev.evohub.io/writing-pages.md) - [Review and change requests](https://docs-dev.evohub.io/review-and-change-requests.md) - [Connect a custom domain](https://docs-dev.evohub.io/docs-custom-domain.md) - [Docs as code with GitHub](https://docs-dev.evohub.io/github-sync.md) --- Source: https://docs-dev.evohub.io/writing-pages.md # Writing pages Pages in EvoHub Docs are written in Markdown, with a small set of components for callouts, tabs, steps and the like. This page lists everything the renderer understands, how to reuse text with snippets, and how to add images and files. The same syntax works in the console editor and in files synced from GitHub. ## The editor Open a page from the site's navigation tree. The editor has the Markdown on one side and a live preview on the other. The toolbar adds formatting, an **Insert** menu for every component below, an upload button, and the **Markdown cheat sheet** (with a second tab, **Writing in Git**, for files in a repository). Raw HTML is never rendered. Use the components below instead. The page title is shown above the page, so do not repeat it as a `# Heading` in the body. Start sections at `##`. ## Basic Markdown | Element | Markdown | | --- | --- | | Headings | `## Install` and `### On macOS` | | Bold, italic, strikethrough | `**bold**`, `_italic_`, `~~struck~~` | | Link to a page | `[Quickstart](/quickstart)` | | Link to a section | `[Install the CLI](/quickstart#install-the-cli)` | | Lists | `- Item` and `1. First` | | Task list | `- [x] Done` and `- [ ] To do` | | Table | Columns separated by `\|`, with a `---` row under the header | | Footnote | `Billed hourly.[^1]` and, anywhere below, `[^1]: Rounded up.` | Every heading gets an anchor: lowercase, words joined by hyphens. Code blocks are highlighted by language and get a **Copy** button. ## Callouts A blockquote whose first line is a marker becomes a callout. Text after the marker is its title. ```markdown > [!NOTE] > Changes take a minute to show up. > [!TIP] Faster builds > Turn on caching in the settings. > [!WARNING] > Rotating the key signs everyone out. ``` The five kinds are `[!NOTE]`, `[!TIP]`, `[!INFO]`, `[!WARNING]` and `[!DANGER]`. GitHub's `[!IMPORTANT]` reads as info and `[!CAUTION]` as danger. ## Tabs ```markdown :::tabs ::tab{title="npm"} npm install acme ::tab{title="pnpm"} pnpm add acme ::: ``` ## Code group Code blocks in tabs that share one **Copy** button. The label in brackets after the language names the tab. ````markdown :::code-group ```bash [npm] npm install acme ``` ```bash [pnpm] pnpm add acme ``` ::: ```` ## Steps Numbered steps; every `###` heading inside starts one. ```markdown :::steps ### Install the CLI Run the installer. ### Sign in Use your account. ::: ``` ## Cards A grid of cards, one to four columns. A card with `href` is a link; `icon` takes an icon name such as `rocket`, `book`, `code`, `settings`, `key`, `shield` or `zap`. ```markdown :::cards{cols=2} ::card{title="Quickstart" icon="rocket" href="/quickstart"} Up and running in five minutes. ::card{title="API" icon="code" href="/api"} Every endpoint, with examples. ::: ``` ## Accordion A section that folds away. Add `open` to show it unfolded. ```markdown :::details{title="How is usage billed?"} By the hour. ::: ``` ## Columns ```markdown :::columns{cols=2} ::col Left column. ::col Right column. ::: ``` ## Badge A small inline label. Colours: `gray`, `blue`, `green`, `yellow`, `red`, `purple` and `accent`. ```markdown Webhooks :badge[New]{color=blue} ``` ## Video embed YouTube, Vimeo and Loom videos are embedded. Any other address shows as a link. ```markdown ::embed{url="https://www.youtube.com/watch?v=VIDEO_ID" title="Product tour"} ``` ## Diagrams and math Mermaid diagrams are drawn from a `mermaid` code block: ````markdown ```mermaid flowchart LR A[Alert] --> B[On-call] --> C[Resolved] ``` ```` Math uses KaTeX syntax: `$\pi r^2$` inside a sentence (no space after the opening `$` or before the closing one), or a block between `$$` lines. ## Snippets A snippet is a piece of Markdown — a support notice, a version table — written once and included in any page. Change the snippet and every page that uses it changes. :::steps ### Create the snippet Open **Snippets** in the site's menu, name it (lowercase letters, digits and single hyphens, for example `support-contact`), and write its Markdown. ### Publish it **Save draft**, then **Publish**. Like pages, snippets have a draft and a live copy, and readers see the live copy. ### Include it Put `{{snippet:support-contact}}` on a line of its own in any page. The Insert menu lists your snippets. ::: Snippets are not expanded inside code, and a snippet cannot include another snippet. An unknown name is shown as written. Unpublishing a snippet makes pages show the reference as plain text. A site holds up to 200 snippets per version. ## Reader variables On a private site that signs readers in with JWT or with EvoHub members, `{{user.name}}`, `{{user.email}}` and any other claim of the reader's token are replaced with that reader's details when the page is shown: ```markdown Welcome back, {{user.name}}. Your plan: {{user.plan}}. ``` On public sites and in previews they are left as written. See [Private docs](https://docs-dev.evohub.io/private-docs.md). ## Files and images Paste or drop an image into the editor, or use the upload button, and EvoHub uploads it and inserts the Markdown. Uploaded PDFs are inserted as a link. | Type | Largest file | | --- | --- | | PNG, JPEG, WebP, GIF | 5 MB | | SVG, ICO | 1 MB | | PDF | 20 MB | SVG files are checked for anything that could run script and refused if they contain it. A site holds up to 500 MB and 2,000 files. The **Assets** page lists every file with where it is used. **Copy URL** and **Copy Markdown** give you the reference. Deleting a file that a page still uses asks you to confirm, because it breaks those pages. > [!NOTE] > Uploaded files are reachable by their address even on a private site. ## Page settings The settings button in the editor holds what is not part of the text: SEO title, description (up to 300 characters), link preview image, canonical address, **Hide from search engines**, **Sidebar title**, icon, **Hide from navigation**, and, on private sites, **Audience**. Settings are drafted with the page and go live when it is published. ## Writing in a repository If your site is connected to GitHub, the same Markdown lives in files, with these settings as front matter. See [Docs as code with GitHub](https://docs-dev.evohub.io/github-sync.md). ## Related - [Docs overview](https://docs-dev.evohub.io/docs-overview.md) - [Docs as code with GitHub](https://docs-dev.evohub.io/github-sync.md) - [API references and AI](https://docs-dev.evohub.io/api-references-and-ai.md) --- Source: https://docs-dev.evohub.io/review-and-change-requests.md # Review and change requests A change request bundles drafts — pages, snippets, API references and navigation changes — so they can be reviewed and published together. You can use change requests on any site. When a site requires review, they are the only way anything goes live. ## Turn on required review In the site's **Settings**, **Review**: 1. Turn on **Require review before publishing**. 2. Pick **Approvals needed to merge** (1 to 3). While review is on, nobody publishes directly — Admins and Owners included. **Publish**, **Unpublish**, deleting a live page and changing a live page's address are refused; those changes go through a change request. Changes to the navigation (order and sections) also wait for a change request: readers keep seeing the navigation as it was until one is merged. Turning review off is a site setting, so it needs permission to manage the site. When you turn it off, the navigation goes live as it is arranged in the editor. ## Who can review Authorization comes from organization roles, not from the site: - Anyone with `docs:page:write` can edit drafts and open change requests. - Anyone with `docs:page:publish` can approve, request changes, merge and undo a merge — whether or not they were asked. - Nobody approves their own change request. - Reviews, merges and undoing a merge must be done by a person signed in to EvoHub. An API key cannot approve or merge, whatever its scopes. The **Docs reviewer** preset gives a role exactly the approve-and-publish side. See [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md). ## Open a change request :::steps ### Make your edits Edit pages, snippets or API references and **Save draft**. Reorder the navigation if you need to. ### Start the request Open **Change requests** in the site's menu and choose **New change request**. On a site that requires review, the page editor's **Open change request** button (in place of **Publish**) starts one for the page you are on. ### Pick the changes Every draft that differs from what is live is listed — pages, translations, snippets, API references and **Navigation**. Tick the ones to include. A change can also take a live page off the site (**Unpublish**). ### Describe it and ask for review Give it a **Title** and an optional **Description**, add **Reviewers**, and choose **Open change request**. Reviewers get a notification in Docs; they are whom you ask, not a gate. ::: On a site with several versions, a change request belongs to one version and publishes to it. ## Review a change request The change request shows each item as a diff of the live text against the draft (the navigation as an outline), plus a **Settings** block when page settings change. You can comment on the request as a whole or on a single item, and reply in threads. At the bottom, leave an optional comment and choose **Approve** or **Request changes**. Any current request for changes blocks the merge. ### Approvals go stale An approval is tied to exactly what the reviewer saw. If anything in the request changes after that — a draft is edited, an item is added or removed, the request is refreshed — earlier approvals are marked **Reset by later edits** and stop counting. Ask for another review. ## Merge When the request has enough current approvals and nothing is waiting, it shows **Approved and ready to merge** and the **Merge** button. Merging publishes every item at once, in one step: either everything goes live or nothing does. Each published page gets a revision that records the change request. ### Outdated requests Each item remembers the live copy it was drawn against. If someone publishes one of those pages some other way after the request was opened, the request is marked **Outdated** and cannot be merged. **Refresh** re-bases it on the current live copies; approvals reset. ### Undo a merge A merged request offers **Undo merge**, which puts back exactly what was live before it — pages, snippets, API references and navigation. Undo is refused if any of those items was published again since. ### Close and reopen **Close** sets a request aside without publishing; **Reopen** brings it back. The author or anyone who can publish can do either. ## Preview links A preview link shows the whole site with the request's drafts in place, for people who do not use the console. 1. In the change request, under **Preview link**, choose **Create preview link**. 2. Copy the link right away; it is shown only once. Anyone with the link sees the drafts, including pages limited to an audience on a private site, so share it like a password. Preview pages are never indexed or cached, and every link inside stays within the preview. The link stops working when the request is merged or closed, after 30 days, or when you choose **Turn off** or **New link**. Preview links open on the site's own domain, so the site needs an active custom domain. See [Connect a custom domain](https://docs-dev.evohub.io/docs-custom-domain.md). ## Rollbacks under review On a site that requires review, rolling a page back to an earlier revision puts that revision in the draft and opens a change request for it instead of publishing. ## GitHub and imports under review When a site requires review, a GitHub sync opens (or updates) one change request instead of publishing, and an import into an existing site arrives as one change request. See [Docs as code with GitHub](https://docs-dev.evohub.io/github-sync.md) and [Import and export](https://docs-dev.evohub.io/import-and-export.md). ## Notifications **Notifications** in the Docs menu lists review requests and what happened to your change requests: approvals, requests for changes, comments and merges. Notifications are in the console only; Docs does not email them. ## Related - [Docs overview](https://docs-dev.evohub.io/docs-overview.md) - [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md) - [Docs as code with GitHub](https://docs-dev.evohub.io/github-sync.md) --- Source: https://docs-dev.evohub.io/docs-custom-domain.md # Connect a custom domain A docs site has exactly one public address: a subdomain you own, such as `docs.example.com`. EvoHub does not give sites an address of its own, so until a domain is connected and active, the site is visible only through **Preview** in the console. This page shows how to connect one. You need permission to manage the site (`docs:site:write`) and access to your domain's DNS. ## Before you start - Use a **subdomain** with at least three labels, like `docs.example.com`. Apex domains (`example.com`) are not supported. - One hostname serves one site across EvoHub. A hostname already used by another site or a status page is refused. - The site's pages show only once the site and the pages are published. You can connect the domain first and publish later. ## Connect the domain :::steps ### Enter the hostname Open the site's **Settings**, **Domain**. Under **Your domain**, type the hostname (for example `docs.example.com`) and choose **Connect domain**. ### Add the DNS records EvoHub shows two records with **Type**, **Name** and **Value**, each with a **Copy** button: | Type | Name | Value | | --- | --- | --- | | CNAME | `_acme-challenge.docs.example.com` | The value shown in the console | | CNAME | `docs.example.com` | `cname.evohub-dns.com` | Add the `_acme-challenge` record first and wait for the status to turn green, then add the routing CNAME. That way the domain never goes live without a certificate. ### Wait for validation Choose **Re-check** to refresh the status. When it shows **Verified · SSL active**, the site is live at `https://docs.example.com`. ::: > [!TIP] > If your DNS is proxied through Cloudflare, set SSL to **Full** (not Flexible). ## Domain status | Status | Meaning | | --- | --- | | **Not connected** | No domain yet. | | **Pending DNS** | The records are not found yet. | | **Validating** | The records are found and the certificate is being issued. | | **Verified · SSL active** | The site is live on the domain with a certificate. | | **Action needed** | The domain stopped being served — usually a record was removed or the certificate expired. | For **Action needed**, check that both records are still in your DNS exactly as shown, then **Re-check**. If it stays red, remove the domain and connect it again. Keep both records in place for as long as the site uses the domain. Removing them takes the site off the address. ## What the domain is used for Everything a reader reaches is on this domain: the pages, search, `llms.txt`, the `.md` copies of pages, the MCP server, sign-in for private sites and change request preview links. Links EvoHub builds for you — a preview link, a test sign-in link — need the domain to be active. ## Change or remove the domain - To move to a different hostname, connect the new one. It replaces the old one, which stops serving the site. - **Remove domain** disconnects the hostname. The site stays in the console and keeps its pages. ## Related - [Docs overview](https://docs-dev.evohub.io/docs-overview.md) - [Private docs](https://docs-dev.evohub.io/private-docs.md) - [Custom domain for a status page](https://docs-dev.evohub.io/status-page-custom-domain.md) --- Source: https://docs-dev.evohub.io/github-sync.md # Docs as code with GitHub You can keep a docs site's pages in a GitHub repository. Every push to the connected branch updates the site, and, if you choose, edits made in the EvoHub console go back to the repository as pull requests. This page describes the repository layout EvoHub reads and how to connect it. This documentation is maintained this way. ## The repository layout EvoHub reads one folder of one branch — the **docs root**, for example `docs/`. Inside it: ```text docs/ ├── index.md first page ("Introduction") ├── getting-started.md a top-level page ├── guides/ a folder of pages is a section │ ├── _category_.json {"label": "Guides", "position": 3} │ ├── 01-install.md │ ├── 02-configure.md │ └── 03-deploy.md ├── reference/ │ ├── _category_.json │ └── components.md ├── images/ │ └── architecture.png linked from pages by relative path ├── _snippets/ │ └── support.md the snippet {{snippet:support}} └── _openapi/ └── orders.yaml an API reference at /orders ``` ### Pages and sections - Every `.md`, `.mdx` or `.markdown` file is a page, except files in folders whose name starts with `_`. - Files directly in the docs root are top-level pages. - A folder that holds pages is a section. A folder inside a section becomes a section of its own, listed right after its parent. A folder with no pages (like `images/`) is not a section. ### Titles and addresses - **Title**: the front matter `title`, else the first `# Heading` (which is then left out of the body), else the file name. - **Address (slug)**: the front matter `slug`, else the file name without its numeric prefix and extension. `01-install.md` is `/install`; `index.md` takes its folder's name, and the root `index.md` is `/introduction`. Slugs are unique within a site, so give pages in different folders different file names (or set `slug`). ### Order Pages within a folder are ordered by `sidebar_position` in the front matter, then a numeric prefix of the file name (`01-`, `1_`, `01.`), then `index.md` or `README.md` first, then the file name. Sections are ordered by `position` in their `_category_.json`, then a numeric prefix of the folder name, then the folder name. `_category_.json` also names the section: ```json {"label": "Guides", "position": 3} ``` Without it, the section is named after the folder (`getting-started` becomes "Getting started"). ### Front matter A YAML block between two `---` lines at the top of a file. Every key is optional: ```yaml --- title: Deploy to production slug: deploy sidebar_position: 3 sidebar_label: Deploy description: Ship a release to production from your machine or from CI. seo_title: How to deploy Acme to production og_image_url: https://acme.example/og/deploy.png canonical_url: https://acme.example/docs/deploy noindex: false hidden: false icon: rocket --- ``` | Key | What it does | | --- | --- | | `title` | The page title. | | `slug` | The page's address. | | `sidebar_position` | Its place among its siblings, lowest first. | | `sidebar_label` | Shorter text for the navigation. | | `description` | Text for search results and link previews (up to 300 characters). | | `seo_title` | The browser tab and search result title, when it should differ. | | `og_image_url` | An `https` image for link previews. | | `canonical_url` | Where the original of the page lives, if elsewhere. | | `noindex` | `true` keeps the page out of search engines. | | `hidden` | `true` leaves the page out of the navigation; it stays reachable by its address. | | `icon` | An icon shown next to the page in the navigation. | | `audience` | A list (or comma-separated text) of groups allowed to see the page on a private site. See [Private docs](https://docs-dev.evohub.io/private-docs.md). | Other keys are ignored. Quote strings that contain a colon: `title: "Deploy: the basics"`. ### Links, images, snippets and API references - **Links** between pages use the relative path to the file, as on GitHub: `[Configure](./02-configure.md)`. They become links to the page's address, whatever its slug. - **Images** and PDFs are linked relative to the file: `![Architecture](../images/architecture.png)`. Files a page links to are uploaded with the sync; files nobody links to are not. - **Snippets**: `_snippets/.md` becomes the snippet ``, included with `{{snippet:}}` on a line of its own. - **API references**: every `.yaml`, `.yml` or `.json` file in `_openapi/` is an OpenAPI 3.0 or 3.1 document. It becomes an API reference named after its `info.title`, at the address of its file name. - Any other folder starting with `_` is skipped; use one for drafts or partials. Page bodies use the same Markdown and components as the console editor. See [Writing pages](https://docs-dev.evohub.io/writing-pages.md). ### Start from the starter The **Docs as code starter** is a small repository that shows every rule above. Download it from the site's **Settings**, **GitHub** tab (**Download the starter (.zip)**), or create a site from the **Docs as code starter** template to see it rendered. ## Formats other than plain Markdown A connection reads plain Markdown by default. It can also read a repository written for Mintlify (`mint.json` or `docs.json`), GitBook (`SUMMARY.md`), ReadMe (`_order.yaml` per category) or Docusaurus (`sidebars.js`, `_category_.json`), or detect the format automatically. Change it under **Format** in the GitHub tab; saving a different format syncs the repository again and keeps pages in place by matching file paths. When EvoHub pushes back to a repository in one of these formats, it writes that format's components and navigation where an equivalent exists. A component the format has no equivalent for is written in EvoHub syntax and listed as a warning on the push. Docusaurus `sidebars.js` is never written; keep it in step yourself. ## Connect a repository You need permission to manage the site (`docs:site:write`), and you must be signed in as a person. You also need to be able to install a GitHub app on the repository. :::steps ### Start from the site Open the site's **Settings**, **GitHub** and choose **Connect GitHub**. ### Install the EvoHub app on GitHub GitHub opens. Install the EvoHub app on your account or organization and give it access to the repository. If your GitHub organization requires an owner's approval, connect again once it is approved. ### Choose what to sync Back in EvoHub, choose the **Repository**, the **Branch** and the **Folder** (for example `docs`; leave it empty for the repository root). ### Decide about pushing back Turn on **Push edits to GitHub as pull requests** if edits made in EvoHub should go back to the repository. You can change this later. ### Save Choose **Save and sync**. The first sync starts right away. ::: The GitHub tab then shows the repository, branch, folder and format, the result of the **Last sync** and **Last push**, and any warnings for individual files. **Sync now** runs a sync by hand. ## What a sync does A sync runs on every push to the connected branch that touches the folder. - New files become pages, changed files update their pages, and the navigation follows the folders and order rules. - A deleted file's page is unpublished and deleted. - Snippets and API references are created and updated, but never deleted by a sync. Remove them in EvoHub. - An empty folder makes no section. - On a site that requires review, a sync does not publish. It opens one change request — or updates the one already open — with the pages waiting, unpublish items for removed files, and the navigation if it changed. See [Review and change requests](https://docs-dev.evohub.io/review-and-change-requests.md). ## Pushing edits back With **Push edits to GitHub as pull requests** on, publishing in EvoHub (including merging a change request, unpublishing, deleting, rolling back) writes the live pages back to the repository: - Commits go to a branch named `evohub/` and a pull request titled "Docs edits from EvoHub" is opened if none is open. Later edits are added to the same branch while the pull request is open. - Your connected branch and the repository's default branch are never written to directly. You review and merge the pull request. - New pages get a file in their section's folder; existing files are updated in place. ## Conflicts If the same page changed both in EvoHub and in its file on GitHub, nothing is overwritten. The page is flagged in the navigation tree and in the editor, and the GitHub tab lists it. For each conflicting page choose: - **Keep EvoHub's** — the next push replaces the file with the EvoHub version. - **Take GitHub's** — the repository's file replaces the page's draft (and the live page, unless the site requires review). Your draft edits are lost. ## Versions and languages - A site with several versions has one connection per version. Use a branch per version (`main` for the latest, `v1` for the old one, the same folder on each) or one branch with a folder per version (`docs/v2/`, `docs/v1/`). Pushes for a version other than the default go to `evohub/-`. - Translations are not synced. The repository holds the site's default language; write translations in EvoHub. ## Disconnect **Disconnect** in the GitHub tab removes the link between pages and files. The pages stay as they are. If the app is removed or loses access on GitHub, the connection shows as disconnected and **Reconnect** picks up where it left off. ## Related - [Writing pages](https://docs-dev.evohub.io/writing-pages.md) - [Import and export](https://docs-dev.evohub.io/import-and-export.md) - [Review and change requests](https://docs-dev.evohub.io/review-and-change-requests.md) --- Source: https://docs-dev.evohub.io/import-and-export.md # Import and export You do not have to start a docs site from an empty page. Import a `.zip` of your existing docs, start from a template, or connect a GitHub repository. You can also export any site as plain Markdown files to back it up or move it. ## Import a .zip ### Supported sources | Choose | What to zip | | --- | --- | | **Mintlify** | The folder that holds `mint.json` or `docs.json`, with its `.mdx` pages and images. | | **GitBook** | The repository or space export that holds `SUMMARY.md`, including the `.gitbook` folder for images. | | **ReadMe** | The folder from ReadMe's Markdown export or the `rdme` CLI, with one folder per category. | | **Docusaurus** | The project, or just its docs folder, with `sidebars.js` and any `_category_.json` files. | | **Markdown folder** | Any folder of `.md` or `.mdx` files. Folders become sections; images they link to come along. | | **EvoHub export** | A `.zip` downloaded from another EvoHub docs site. It has an `evohub.json` at the top. | | **Detect automatically** | EvoHub looks for `mint.json`, `SUMMARY.md`, `sidebars.js`, `evohub.json` and the like, and falls back to a plain Markdown folder. | Components from those tools (callouts, tabs, cards and so on) are converted to their EvoHub equivalents where one exists. A Markdown folder follows the same layout rules as GitHub sync; see [Docs as code with GitHub](https://docs-dev.evohub.io/github-sync.md#the-repository-layout). ### Run the import You need `docs:page:write`. Creating a new site also needs `docs:site:write`. :::steps ### Choose the archive In Docs, choose **Import** on the **Docs sites** page (or **Import** in an open site's menu). Drop a `.zip` of up to 50 MB, pick **Where is it from?**, and choose **Analyze archive**. ### Check what was found EvoHub reads the archive without creating anything and shows the pages and sections it found, the number of images and files and snippets, and any warnings per file. ### Choose where it goes - **New site** — give it a **Name** and **Address**. The pages are published right away, but the site stays private until you publish it. - **Existing site** (or **Into this site**) — pick the **Site** and, if it has several, the **Version**. Everything arrives as drafts, in new pages and sections. Existing pages are never touched and nothing goes live. A page whose address is already taken gets a numbered address (`-2`), and links to it are rewritten. If the site requires review, one change request is opened with the imported pages. ### Import Choose **Create site and import** or **Import into site**. A large archive can take a minute; the page updates by itself. When it is done, **Open site** (or **Open change request**). ::: Images and files the pages link to are uploaded to the site, counted against its file storage, and identical files are stored once. Only the person who started an import can see it. ## Start from a template On the **Docs sites** page choose **From template**, pick one, and give the new site a name and address. The template's pages (and API reference, if it has one) are published, and the site stays private until you publish it. | Template | What you get | | --- | --- | | **Docs as code starter** | The layout EvoHub reads from GitHub: folders, front matter, every component, a snippet and an OpenAPI file. | | **API documentation** | A getting-started path, guides on pagination, errors, rate limits and webhooks, and a full API reference from an OpenAPI document. | | **Product guide** | A user guide for a product: first steps, core concepts, everyday tasks, integrations, roles and billing. | | **Help center** | Short answers to common customer questions, behind a landing page with a search-first hero and topic cards. | | **Developer platform** | Guides and concepts for developers, an API reference tab and a changelog linked from the header. | Replace the example content with your own. ## Export a site In the site's **Settings**, **General**, choose **Download as Markdown (.zip)**. The archive contains, for the version you are working on: - every page, as the draft you are editing, with its settings as front matter; - snippets and API references; - the site's layout (tabs, header, footer, home page); - uploaded images and files; - translations, if the site has other languages; - an `evohub.json` describing the structure. Import the `.zip` into another EvoHub site with **EvoHub export** to reproduce the tree, titles, addresses, order, text and settings, or keep it as a backup. Anyone who can read the site can export it. ## Related - [Docs overview](https://docs-dev.evohub.io/docs-overview.md) - [Docs as code with GitHub](https://docs-dev.evohub.io/github-sync.md) - [Review and change requests](https://docs-dev.evohub.io/review-and-change-requests.md) --- Source: https://docs-dev.evohub.io/private-docs.md # Private docs A docs site is public by default. You can make it private so that readers must sign in first, and limit single pages to some readers. This page covers the four access modes, page audiences, reader variables and read tokens for AI tools. Access is set in the site's **Settings**, **Access**, which needs permission to manage the site (`docs:site:write`). Authors in the console are not affected: anyone whose organization role allows reading docs sees every draft and page there. ## Choose who can read Under **Who can read the published site**, pick one: | Mode | Who gets in | | --- | --- | | **Public** | Anyone with the link. Search engines index the site. | | **Password** | Readers who enter one shared password. Good for a partner or an early-access group. | | **EvoHub members** | People in your EvoHub organization, optionally only some teams or roles. | | **Your own sign-in (JWT)** | Readers your own app signs in and sends to the site with a signed token. Pages can be personalised. | Then choose **Save access settings**. On any private site: - The site is not indexed by search engines and has no sitemap. - **Session lifetime** sets how long a reader stays signed in: 1 hour, 8 hours, 24 hours, 3 days, 7 days or 30 days. - **Sign everyone out** ends every reader's session at once. Changing who can read, the teams or roles, or the JWT key also signs everyone out. - Analytics and the "Was this page helpful?" widget keep working for signed-in readers. - Uploaded files and images stay reachable by their address. - Change request preview links keep working with their own token and show every page, so share them like a password. Readers sign in on the site's own domain, so a private site needs an active custom domain. See [Connect a custom domain](https://docs-dev.evohub.io/docs-custom-domain.md). ## Password Pick **Password**, then under **Site password** enter 8 to 128 characters and choose **Set password** (or **Change password**). The password is stored hashed and cannot be shown again. Changing it signs every reader out. Repeated wrong passwords are slowed down: after too many attempts in a short time, sign-in is refused for a while. ## EvoHub members Pick **EvoHub members**. By default every active member of your organization can read the site. Under **Who may read**, add **Teams** or **Roles** to narrow it down; a reader needs to be in one of them. Readers sign in with their EvoHub account; the docs site never sees their EvoHub login. A sign-in lasts at most 12 hours, and every visit is checked against EvoHub, so people who are removed from the organization, suspended, signed out of EvoHub everywhere, or no longer in the listed teams or roles lose access within 30 seconds. ## Your own sign-in (JWT) Use this when your readers already sign in to your own app. Your app signs a short-lived token for the reader and sends them to the docs site with it. ### Configure the key Under **Verify tokens from your app**, pick the **Algorithm**: - **HS256 (shared secret)** — **Generate a secret** (shown once; copy it now) or paste your own of 32 characters or more. A new secret signs everyone out. - **RS256 (RSA public key)** or **ES256 (EC P-256 public key)** — paste the public key (RSA of 2048 bits or more, or EC P-256), or give a JWKS address (`https` only; keys are matched by `kid`). Never paste a private key. Optionally set **Issuer (iss)** and **Audience (aud)**; they are checked when set. Set the **Login address** where unauthenticated readers are sent (with `?redirect=`), and the **Logout address** readers land on after signing out of the docs. ### The token - `exp` is required and at most 24 hours ahead. The token is a sign-in link, not a credential: keep it to minutes. `iat` may not be in the future. - `groups` (a list of strings, or comma-separated text) decides which audience pages the reader sees. - Every other string, number or boolean claim becomes a reader variable, such as `{{user.name}}` or `{{user.plan}}`. Up to 30 claims, 500 characters each. - `jti`, when present, makes the token work only once. Send the reader to `/_auth/jwt` on your docs domain with the token: ```js import jwt from "jsonwebtoken"; // After your own login check, sign a short-lived token for this reader. const token = jwt.sign( { sub: user.id, name: user.name, email: user.email, groups: ["customers"], plan: "pro", }, process.env.DOCS_JWT_SECRET, { algorithm: "HS256", expiresIn: "10m" }, ); res.redirect(`https://docs.example.com/_auth/jwt?token=${token}&return=/`); ``` ### Test it Under **Try it**, **Make a test token** creates a token for the saved HS256 settings with the subject, name, email, groups and extra claims you enter, valid for up to 60 minutes. **Verify a token** checks a token your app signed and tells you whether a reader with it would be let in, or why not. ## Page audiences On a private site that signs readers in with JWT or EvoHub members, you can limit a page to some readers. In the page settings, under **Audience**, add groups: - with JWT, the `groups` in the reader's token; - with EvoHub members, team ids and roles such as `org:admin` (the picker offers **Team:** and **Role:** entries). Only readers in one of the groups see the page. For everyone else it does not exist: it is left out of the navigation, search, `llms.txt` and the MCP server, and its address answers "not found". The audience is a page setting, so it goes live when the page is published. In a GitHub repository, set it with the `audience` front matter key: ```yaml --- title: Partner pricing audience: [partners, org:admin] --- ``` > [!WARNING] > On a public site, and for readers who sign in with the shared password, nobody belongs to a group — so a page with an audience is shown to no one. ## Reader variables On JWT and EvoHub members sites, `{{user.name}}`, `{{user.email}}` and any other claim are replaced with the signed-in reader's details in the page text. They are left as written on public sites and in previews. See [Writing pages](https://docs-dev.evohub.io/writing-pages.md#reader-variables). ## Read tokens for AI tools A private site is closed to AI assistants and scripts unless they send a token. Under **Read tokens for AI tools**, give a token a **Name** and, optionally, the groups it reads as, then **Create token**. Copy it when it is shown; it is not shown again. The token reads the site — including its MCP server and the `.md` copies of pages — as a reader in those groups. Send it as `Authorization: Bearer `. The console shows ready-made setup for Claude Code and for MCP configuration files. A site holds up to 10 tokens; revoke one at any time. ## Related - [Connect a custom domain](https://docs-dev.evohub.io/docs-custom-domain.md) - [API references and AI](https://docs-dev.evohub.io/api-references-and-ai.md) - [Writing pages](https://docs-dev.evohub.io/writing-pages.md) --- Source: https://docs-dev.evohub.io/api-references-and-ai.md # API references, search and AI Every published docs site comes with search, Markdown copies of its pages and an MCP server, so people and AI assistants can find and read your documentation. You can add API references from OpenAPI documents, and see how the site is read in analytics. This page covers all of these. ## API references An API reference turns an OpenAPI document into reference pages on the site: an overview with servers and authentication, and one page per operation with its parameters, request and response schemas, and an example `curl` request. ### Add one :::steps ### Upload the document Open **API reference** in the site's menu. Under **Upload OpenAPI**, enter a **Name** (for example "Payments API"), an optional **Address** (derived from the name when empty), choose the file and **Upload**. EvoHub accepts OpenAPI 3.0 and 3.1 as `.json`, `.yaml` or `.yml`, up to 2 MB. ### Check the result The document is validated when you upload it. Problems that stop it are listed; smaller issues are shown as warnings. References to other files or URLs (remote `$ref`s) are not fetched — they show as placeholders with a warning, so keep the document self-contained. ### Publish A new reference starts as a draft. **Publish** puts it on the site at `/
`. Readers see it once the site itself is published. ::: To update a reference, use **Replace file**. Readers keep seeing the published version until you choose **Publish changes**. On a site that requires review, references go live through a change request like pages. A reference's address cannot be the same as a page's address on the same site. In a GitHub repository, put OpenAPI files in the `_openapi/` folder. See [Docs as code with GitHub](https://docs-dev.evohub.io/github-sync.md#links-images-snippets-and-api-references). ## What readers get ### Search Every site has full-text search over its published pages and API operations. Readers open it with the search box, by pressing `/`, or with `Ctrl+K` (`⌘K` on a Mac). Pages limited to an audience appear only for readers who may see them. ### Reading experience Readers get the navigation, an "On this page" outline, previous and next links, a light/dark switch (unless the theme fixes one), copy buttons on code, and, where you have them, version and language switchers. The site's own labels are shown in the reader's language when the site has several. See [Docs overview](https://docs-dev.evohub.io/docs-overview.md#theme). ### Use with AI Every page has a **Use with AI** menu with: - **Copy page** — the page as Markdown, for pasting into an AI tool. - **View as Markdown** — the page as plain Markdown. - **Open in Claude** and **Open in ChatGPT** — start a conversation about the page. - **Connect to Cursor / VS Code / Claude** — the site's MCP server address, with one-click setup for Cursor and VS Code. ## For AI tools These addresses are on the site's own domain (shown in **Settings**, **AI & LLMs** once the domain is active): | Address | What it is | | --- | --- | | `/llms.txt` | An index of every page, following [llmstxt.org](https://llmstxt.org). | | `/llms-full.txt` | The whole site in one Markdown file. | | `/.md` | Any page as Markdown. Requesting a page with `Accept: text/markdown` works too. | | `/mcp` | An MCP server (Streamable HTTP). | Pages marked **Hide from search engines** are left out of `llms.txt` and the sitemap. ### MCP server The MCP server lets an assistant search and read the docs as tools: | Tool | What it does | | --- | --- | | `search_docs` | Full-text search over pages and API operations. | | `get_page` | One page, API reference overview or operation, as Markdown. | | `list_pages` | Every page in reading order, grouped by section, and the API references. | | `list_api_operations` | The operations of the API references, with their ids. | | `get_api_operation` | One operation as Markdown, with a `curl` example. | To connect it, for example in Claude Code: ```bash claude mcp add --transport http acme-docs https://docs.example.com/mcp ``` **Settings**, **AI & LLMs** shows the exact command and configuration for Claude Code, Claude Desktop, Cursor and VS Code. On a private site, AI tools need a read token. See [Private docs](https://docs-dev.evohub.io/private-docs.md#read-tokens-for-ai-tools). ### AI crawlers **Allow AI crawlers** (in **Settings**, **AI & LLMs**) decides what the site's `robots.txt` says to AI crawlers such as GPTBot, ClaudeBot, PerplexityBot, Google-Extended and CCBot. It is on by default. Turning it off asks them to stay away; search engines are not affected. ## Analytics Turn analytics on in **Settings**, **Analytics** with **Count visits, searches and AI readers**, then open **Analytics** in the site's menu. You can filter by date range, version and language, and export each report as CSV. | Tab | What it shows | | --- | --- | | **Overview** | Page views and visitors per day, top pages, referrers, countries, devices, versions and languages. | | **Search** | What readers search for, searches with no results, and which results they click. | | **AI readers** | Requests from AI assistants and crawlers by kind (Markdown pages, `llms.txt`, MCP and so on), by agent, and the pages they read. | | **Feedback** | Answers to "Was this page helpful?" per page, and the comments. | How readers are counted: - No cookies, and no IP address or browser details are stored. A reader is told apart for one day by a hash that changes every day, so visitors are counted per day. - Readers who send Do Not Track or Global Privacy Control, and bots, are not counted. Nothing is counted on previews. - Searches that look like an email address, a long number or a key are never stored. - Choose how long to keep visit and search figures: 30, 90 or 180 days, 1 year or 2 years. Older figures are deleted automatically. You can also add a **GA4 measurement ID** or a **Segment write key**. They load only when filled in, never on previews, and never for readers who send Global Privacy Control. Those tools set cookies, so asking readers for consent is up to you. ### Page feedback With **Show "Was this page helpful?" on every page** on, readers answer **Yes** or **No** at the bottom of each page and can leave a comment. In **Analytics**, **Feedback**, mark comments **Resolve** (or **Reopen**), delete them, or export them. Feedback is kept for two years or until you delete it. ## Related - [Docs overview](https://docs-dev.evohub.io/docs-overview.md) - [Private docs](https://docs-dev.evohub.io/private-docs.md) - [Docs as code with GitHub](https://docs-dev.evohub.io/github-sync.md) --- Source: https://docs-dev.evohub.io/changelog-overview.md # Changelog overview EvoHub Changelog publishes your release notes as a changelog site on your own domain, with an embeddable "What's new" widget, feeds and email updates for your users. You write entries in Markdown in the EvoHub console and publish them when they are ready. The Changelog console lives at [https://evohub.io/changelog-admin](https://evohub.io/changelog-admin). ## Concepts | Concept | What it is | | --- | --- | | **Changelog** | One changelog site: its name, logo, brand colour, domain and settings. An organization can have several, for example one per product. | | **Entry** | One release note: title, summary, Markdown body, version, categories, tags, date and cover image. | | **Category** | A coloured label such as **New**, **Improved**, **Fixed** or **Security**. Readers can filter by category. | | **Subscriber** | An email address that follows the changelog, after confirming. | A changelog can belong to the whole organization or to one team. A team's changelog is visible only to that team and to organization Admins and Owners. ## Who can do what Changelogs have no members or roles of their own. What you can do on every changelog of your organization comes from your organization role: | Permission | Lets you | | --- | --- | | `changelog:site:read` | Open changelogs, entries, drafts, feedback and analytics. | | `changelog:entry:write` | Write and edit entries, upload images, submit drafts for review, import GitHub releases. | | `changelog:entry:publish` | Publish, schedule and unpublish entries; approve or request changes. | | `changelog:site:write` | Create changelogs and manage their settings: brand, categories, domain, widget, subscribers, integrations, review, access. | | `changelog:site:delete` | Delete a whole changelog. | Members have everything except deleting a changelog; Viewers can read. Deleting a changelog is for Admins. To split the work, build a custom role from the **Changelog writer**, **Changelog reviewer** or **Manages changelogs** presets. See [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md). ## Create a changelog :::steps ### Open Changelog In the EvoHub console, open **Changelog**. **Changelogs** lists every changelog you can reach. ### Create it Choose **New changelog**, enter a **Name** and a **Slug**, and select **Create changelog**. The slug is a short identifier (3–63 lowercase letters, digits and hyphens) that is unique across EvoHub. It names the changelog in the console; it is not a web address. Every new changelog starts with four categories: **New**, **Improved**, **Fixed** and **Security**. ### Brand it In **Settings**, **Brand**, upload a logo (PNG, JPEG or WebP, up to 2 MB) and pick the brand colour. In **Settings**, **General**, set the description, a **Website** the logo links to, and the **Default theme** (follow the visitor's device, light or dark). ### Write entries Choose **New entry**. See [Writing entries](https://docs-dev.evohub.io/writing-entries.md). ### Connect a domain A changelog answers only on its own domain. Connect one in **Settings**, **Domain**. See [Changelog settings](https://docs-dev.evohub.io/changelog-settings.md#custom-domain). ### Publish the changelog In **Settings**, **General**, choose **Publish changelog**. Until then, nobody outside your organization can see it, and published entries appear once the changelog goes public. ::: **Take offline** in the same place makes the changelog private again without deleting anything. ## What readers see On your domain (for example `https://changelog.example.com`), readers get: - A timeline of entries, newest first, with pinned entries on top of the first page. - Filters by category and tag, and search. - A page per entry, with reactions and a **Send feedback** link when those are on. - A **Subscribe** button for email updates, when email is set up. - RSS, Atom and JSON feeds, `llms.txt`, Markdown copies of entries and an MCP server for AI tools. See [Reaching readers](https://docs-dev.evohub.io/reaching-readers.md). ## The console Inside a changelog, the menu has: - **Entries** (the changelog's name) — every entry, filtered by **Drafts**, **In review**, **Scheduled** and **Published**, by category, or by search. - **Categories** — add, rename, recolour, reorder and delete categories (up to 30 per changelog). - **Feedback** — comments readers sent about entries. - **Analytics** — how the changelog is read. - **Settings** — **General**, **Brand**, **Domain**, **Review**, **Widget**, **Subscribers**, **Integrations**, **Access** and **Analytics**. ## Delete a changelog **Settings**, **General**, **Delete changelog** removes every entry, uploaded image, category and the custom domain. This cannot be undone. Only Admins (or a custom role with `changelog:site:delete`) can do this. ## Billing Changelog is billed by usage, not per seat: any number of people can write, and any number can read. See [How billing works](https://docs-dev.evohub.io/how-billing-works.md) for what is metered. ## Related - [Writing entries](https://docs-dev.evohub.io/writing-entries.md) - [Reaching readers](https://docs-dev.evohub.io/reaching-readers.md) - [Changelog settings](https://docs-dev.evohub.io/changelog-settings.md) --- Source: https://docs-dev.evohub.io/writing-entries.md # Writing entries An entry is one release note. You write it as a draft, publish it now or schedule it for later, and readers see the published copy. This page covers the entry editor, categories, the publishing workflow, review and importing from GitHub releases. ## Create an entry In a changelog, choose **New entry**, give it a **Title**, and select **Create draft**. Nothing is public until you publish it. ## The entry editor The body is Markdown, written in the same editor as EvoHub Docs pages: a toolbar, an **Insert** menu for callouts, tabs, code groups, diagrams and other components, image upload (paste or drop an image into the editor), and a Markdown cheat sheet. See [Writing pages](https://docs-dev.evohub.io/writing-pages.md) for the full syntax. Beside the body, **Details** holds: | Field | What it does | | --- | --- | | **Summary** | One or two sentences (up to 300 characters), shown in the list, feeds and link previews. | | **Version** | Optional, like `v2.4.0`. | | **Categories** | Up to 5 per entry. | | **Tags** | Free-form labels, up to 10 per entry. Readers can filter by tag. | | **Date shown** | Optional. Leave it empty to show the publish time. | | **Author name** | Leave it empty to show the name of whoever publishes. | | **Cover image** | Shown above the entry and in link previews. | | **Pin to the top** | Pinned entries stay above the timeline on the first page. | | **Address** | The entry's part of the web address, derived from the title. | Images you upload are stored with the changelog: PNG, JPEG, WebP, GIF or SVG, up to 5 MB each. > [!WARNING] > Changing the **Address** of a published entry moves it at once and breaks links people already have. **Save** keeps your edits in the draft. If someone else saved the same entry in the meantime, you choose whose version to keep; nothing is overwritten silently. ## Categories Every changelog starts with **New**, **Improved**, **Fixed** and **Security**. On the **Categories** page, people who manage the changelog can add categories (up to 30), change names and colours, reorder them, and delete them. Deleting a category removes it from every entry and from subscribers who followed it. ## Statuses | Status | Meaning | | --- | --- | | **Draft** | Being written; not public. | | **In review** | Submitted for review. | | **Scheduled** | Will publish itself at a set time. | | **Published** | Live. | A published entry that you edit stays **Published**: readers keep seeing the live copy, and the editor shows **Unpublished changes** until you choose **Publish changes**. ## Publish Anyone with `changelog:entry:publish` can: - **Publish now** — the draft goes live. The first time, it also sends notifications (below). - **Schedule…** — pick a date and time up to a year ahead. The entry goes live by itself at that time; **Unschedule** cancels it. - **Unpublish** — takes the entry off the changelog. The draft stays. ### Notify subscribers and integrations When you publish or schedule, **Notify subscribers and integrations** decides whether the entry is announced: emailed to subscribers (or included in the weekly digest), and posted to Slack and webhooks. Only the first publish of an entry is announced; editing and republishing never sends anything again. See [Reaching readers](https://docs-dev.evohub.io/reaching-readers.md). ## Review In **Settings**, **Review**, turn on **Entries need approval to go live** and set **Approvals needed** (1 to 3). Then: 1. A writer saves the draft and chooses **Submit for review**. 2. Anyone with `changelog:entry:publish` reviews it in the editor's **Review** panel with **Approve** or **Request changes**, and an optional comment. 3. Once the entry has enough current approvals, it can be published or scheduled. The rules: - An approval counts only for the draft it was given on. Any later edit makes earlier approvals stale (they show as **Older draft**), and the entry needs approving again. - The person who edited the draft last cannot approve it. - Reviews must come from a person signed in to EvoHub. An API key cannot approve. - Nobody publishes around the gate, Admins included. The approvals are checked again when a scheduled entry fires; if they no longer hold, the entry goes back to **In review** instead of publishing. ## Publish history Every publish is kept in **Publish history** (the newest 50). **View** shows an earlier version; **Restore** copies it into the draft without publishing it. ## Import GitHub releases To start from release notes you already have, open **Settings**, **Integrations**, **GitHub releases** and choose **Import releases…**: 1. Enter the **Repository** as `owner/name`. Public repositories only. 2. Choose how many **Latest releases to import** (1 to 50), and whether to **Include pre-releases**. 3. Choose **Import**. Each release becomes a draft entry: the title is the release name (or tag), the version is the tag, the date is the release date, and the body is the release notes. Releases you imported before and draft releases are skipped. Nothing is published, so nothing is announced; review the drafts and publish the ones you want. A changelog can run up to 10 imports an hour. ## Related - [Changelog overview](https://docs-dev.evohub.io/changelog-overview.md) - [Reaching readers](https://docs-dev.evohub.io/reaching-readers.md) - [Writing pages](https://docs-dev.evohub.io/writing-pages.md) --- Source: https://docs-dev.evohub.io/reaching-readers.md # Reaching readers Publishing an entry is half the job; the other half is making sure people see it. A changelog can show new entries inside your own app with a widget, email them to subscribers, post them to Slack and your own webhooks, and offer feeds for readers and AI tools. This page covers each channel. Most channels use the changelog's own domain, so connect one first. See [Changelog settings](https://docs-dev.evohub.io/changelog-settings.md#custom-domain). ## The "What's new" widget The widget adds a button to your website or app that opens the changelog's 10 newest entries in a panel, with a count of entries the visitor has not seen yet. ### Add it to your site :::steps ### Check the settings In **Settings**, **Widget**, keep **Show the widget** on, and set the **Button label** (1 to 30 characters; "What's new" by default) and the **Position**: **Bottom right**, **Bottom left**, or **No button (I'll attach it to my own element)**. Choose **Save widget**. ### Copy the script Under **Add it to your site**, the console shows the script tag with your changelog's domain and slug filled in. It looks like this: ```html ``` ### Paste it into your pages Add the tag to every page where the widget should appear, before the closing `` tag. ::: Optional attributes on the script tag: | Attribute | What it does | | --- | --- | | `data-position` | `bottom-right`, `bottom-left` or `none`, overriding the setting. | | `data-selector` | A CSS selector of your own link or button to attach the unread badge to, for example `#whats-new`. | | `data-theme` | `light`, `dark` or `auto`. | | `data-label` | The button text, overriding the setting. | ### Embed the list in a page Where you cannot add scripts, or to show the list inside a page, use the iframe: ```html ``` Under **Allowed sites**, list the origins allowed to show the iframe, such as `https://app.example.com`. Leave it empty to allow any site. ### Content Security Policy If your site sets a Content Security Policy, allow your changelog's domain in `script-src` and `connect-src`. Add it to `style-src` for older browsers, and to `frame-src` if you use the iframe. The widget sets no cookies. It remembers the latest entries for a minute in the browser's local storage, so page views within a minute do not fetch them again. ## Feeds Every published changelog has three feeds on its domain: | Feed | Address | | --- | --- | | RSS 2.0 | `/feed.xml` | | Atom 1.0 | `/atom.xml` | | JSON Feed 1.1 | `/feed.json` | Add `?category=` to follow one category, for example `https://changelog.example.com/feed.xml?category=fixed`. ## Email subscribers Readers subscribe with the **Subscribe** button on your changelog. They enter their email address, can pick only some categories, and agree to receive emails. Nothing is sent until they confirm through the link in a confirmation email (valid for 7 days). ### Choose when to email In **Settings**, **Subscribers**, under **When to email subscribers**: - **Instant** — email each new entry when it is published. - **Weekly digest** — one email a week with that week's new entries, on the day and hour (UTC) you choose. A week with nothing new sends nothing. - **Off** — no subscribe form and no emails. Only the first publish of an entry is emailed, never an edit, and only when **Notify subscribers and integrations** was on. Subscribers who follow some categories only get entries in those categories. Set a **Reply-to address** if subscribers should be able to reply. The card also shows how many emails were sent today; past the daily limit, emails wait for the next day. Every email carries an unsubscribe link, and mail clients get a one-click unsubscribe. Emails are sent from EvoHub on the changelog's behalf, with your changelog's name and brand colour, and contain no tracking pixels. > [!NOTE] > The subscribe form and every link in the emails open on the changelog's own domain, so nobody can subscribe until the changelog has an active custom domain. ### Manage subscribers The **Subscribers** list shows every address with its status — **Confirmed**, **Waiting to confirm** or **Unsubscribed** — and the categories it follows. Search by email or filter by status. **Export CSV** downloads the list with each address's consent record. Deleting a subscriber erases the address and its consent record. ## Reactions and feedback In **Settings**, **Widget**, under **Reactions and feedback**: - **Reactions** lets readers react to an entry with an emoji: thumbs up, heart, celebrate, eyes or rocket. - **Feedback** adds a **Send feedback** link under each entry. Readers send a short private comment (up to 1,000 characters), with an email address if they want a reply. Read comments on the **Feedback** page, mark them resolved, or delete them. ## Slack and webhooks In **Settings**, **Integrations** (for people who manage the changelog), you can announce each newly published entry to up to 10 destinations. Like email, only an entry's first publish with **Notify subscribers and integrations** on is sent. ### Slack Create an incoming webhook in Slack, then add it under **Slack** with a name and the webhook address (it starts with `https://hooks.slack.com/services/`). Each new entry is posted with its title, summary and a **Read the update** button. ### Webhooks Add your endpoint under **Webhooks** with a name and an `https` address. EvoHub shows a signing secret once; copy it then. Each new entry is sent as a `POST` with a JSON body: ```json { "id": "cdel_...", "type": "entry.published", "created_at": "2026-10-01T09:00:00Z", "site": { "slug": "acme", "name": "Acme Updates", "url": "https://changelog.example.com" }, "entry": { "slug": "faster-dashboards", "title": "Faster dashboards", "summary": "Dashboards load twice as fast.", "version": "v2.4.0", "date": "2026-10-01T09:00:00Z", "categories": [{ "slug": "improved", "name": "Improved" }], "tags": ["dashboards"], "url": "https://changelog.example.com/faster-dashboards", "body_md": "..." } } ``` Requests carry the headers `X-EvoHub-Event`, `X-EvoHub-Delivery` and `X-EvoHub-Signature: t=,v1=`. The signature is an HMAC-SHA256 of `t + "." + raw body` with your signing secret. Verify it on the raw body and reject requests more than 5 minutes old: ```js import crypto from "node:crypto"; // rawBody: the request body exactly as received, not re-serialised JSON. export function verify(rawBody, header, secret) { const parts = Object.fromEntries(header.split(",").map((p) => p.split("="))); const t = Number(parts.t); if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; const expected = crypto .createHmac("sha256", secret) .update(t + "." + rawBody) .digest("hex"); const a = Buffer.from(expected); const b = Buffer.from(parts.v1 ?? ""); return a.length === b.length && crypto.timingSafeEqual(a, b); } ``` A failed delivery is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours. For each destination you can switch it **Active** on or off, **Send test** (a `ping` event), look at the last 50 **Deliveries**, **Rotate secret** (the old secret stops working at once) and **Delete** it. ## AI tools Every published changelog is readable by AI assistants on its domain: - `/llms.txt` and `/llms-full.txt` — an index and the full text, following [llmstxt.org](https://llmstxt.org). - `/.md` — any entry as Markdown (or request the entry with `Accept: text/markdown`). - `/mcp` — an MCP server with the tools `list_entries`, `get_entry` and `search`. **Let AI crawlers read the changelog** in **Settings**, **General** controls what `robots.txt` says to AI crawlers such as GPTBot and ClaudeBot; `llms.txt` stays available either way. ## Related - [Writing entries](https://docs-dev.evohub.io/writing-entries.md) - [Changelog settings](https://docs-dev.evohub.io/changelog-settings.md) - [Changelog overview](https://docs-dev.evohub.io/changelog-overview.md) --- Source: https://docs-dev.evohub.io/changelog-settings.md # Changelog settings This page covers the changelog settings that decide where and how it is read: its custom domain, password access, language, and analytics. Settings are under **Settings** in an open changelog. Anyone who can read the changelog can look at them; changing them needs permission to manage changelogs (`changelog:site:write`). ## Custom domain A changelog has exactly one public address: a subdomain you own, such as `changelog.example.com`. EvoHub does not give changelogs an address of its own, so until a domain is active, the changelog is visible only in the console. The subscribe form, email links, the widget and webhook links all use this domain. :::steps ### Enter the hostname Open **Settings**, **Domain**. Under **Your domain**, type the hostname and choose **Connect domain**. Use a subdomain with at least three labels; apex domains (`example.com`) are not supported. ### Add the DNS records The console shows two CNAME records to add at your DNS provider: | Type | Name | Value | | --- | --- | --- | | CNAME | `_acme-challenge.changelog.example.com` | The value shown in the console | | CNAME | `changelog.example.com` | `cname.evohub-dns.com` | Add the `_acme-challenge` record first and wait for it to validate, then add the routing CNAME, so the domain never goes live without a certificate. If your DNS is proxied through Cloudflare, set SSL to **Full** (not Flexible). ### Wait for validation Choose **Re-check** to refresh. When the status shows **Verified · SSL active**, the changelog is live at `https://changelog.example.com`. ::: The statuses are **Not connected**, **Pending DNS**, **Validating**, **Verified · SSL active** and **Action needed**. For **Action needed**, check that both records are still in your DNS exactly as shown and **Re-check**; if it stays red, remove the domain and connect it again. Keep both records for as long as you use the domain. A hostname can serve only one changelog, docs site or status page. **Remove domain** disconnects it; the changelog keeps its entries. ## Password access A changelog is public by default. To share it with a limited group — partners, an early-access group — make it private: 1. Open **Settings**, **Access**. 2. Under **Changelog password**, enter 8 to 128 characters and choose **Set password**. 3. Under **Who can read the published changelog**, pick **Password** and choose **Save access settings**. Readers then enter the shared password once per session. **Session length** sets how long they stay signed in: 1 hour, 8 hours, 24 hours, 3 days, 7 days or 30 days. **Sign everyone out** ends every session now; changing the password or the visibility does the same. Repeated wrong passwords are slowed down. On a private changelog: - Its feeds, `llms.txt` and the widget are off. - Uploaded images stay reachable by their address. - Subscribing and reactions still work for readers who are signed in. Password access is the only private mode for changelogs. EvoHub does not currently offer sign-in with EvoHub accounts or your own sign-in (JWT) for changelogs; docs sites have both. See [Private docs](https://docs-dev.evohub.io/private-docs.md). ## Language In **Settings**, **General**, **Language** sets the language of the changelog's own words — buttons, headings, dates, the widget and the subscribe pages. Your entries are shown as you wrote them. Available languages: English, Deutsch, Français, Español, Italiano, Português, Nederlands, Polski, Türkçe, Русский, 日本語, 한국어, 中文 and العربية (shown right to left). Under **Customise text**, replace single texts of the public changelog in that language — for example the label of the subscribe button. Pick a text from the suggestions, write your version, and choose **Save custom text**. Keep placeholders in curly braces as they are. EvoHub does not translate entries; write them in the language your readers use. ## Analytics Turn on **Count how the changelog is read** in **Settings**, **Analytics**, and choose how long to keep the counts: 30, 90 or 180 days, 1 year or 2 years. Older daily counts are deleted automatically. Open **Analytics** in the changelog's menu to see, for a date range: - totals for **Views**, **Visitors**, **Widget opens**, **Widget clicks**, **Reactions**, **Feedback**, **Feed reads**, **AI requests** and **Confirmed subscribers**; - views, visitors and widget opens per day; - **Top entries**, **Referrers**, **Countries**, **Devices**, **Feeds**, **AI readers** and **AI agents**. Each report can be exported as CSV. How readers are counted: no cookies, no stored IP address, and a hash that changes daily, so visitors are counted per day. Readers who send Do Not Track or Global Privacy Control are not counted. ## Other settings - **General** — name, address, description, **Website**, **Default theme**, **Let AI crawlers read the changelog**, publishing and deleting. See [Changelog overview](https://docs-dev.evohub.io/changelog-overview.md). - **Brand** — logo and brand colour. - **Review** — required approvals. See [Writing entries](https://docs-dev.evohub.io/writing-entries.md#review). - **Widget**, **Subscribers** and **Integrations** — see [Reaching readers](https://docs-dev.evohub.io/reaching-readers.md). ## Related - [Changelog overview](https://docs-dev.evohub.io/changelog-overview.md) - [Reaching readers](https://docs-dev.evohub.io/reaching-readers.md) - [Connect a custom domain to a docs site](https://docs-dev.evohub.io/docs-custom-domain.md) --- Source: https://docs-dev.evohub.io/how-billing-works.md # How billing works EvoHub charges for what your organization actually uses, never for how many people are in it. This page explains the unit everything is measured in, the free allowance, the rates, and what each action costs. The [pricing page](https://evohub.io/pricing) has the same rates and a cost calculator. ## EvoHub tokens (EHU) Everything billable in EvoHub is measured in **EvoHub tokens (EHU)**. Each action has a fixed weight in EHU: a voice call is 10 EHU, an alert received is 1 EHU, and so on. At the end of the month, EvoHub adds up your organization's EHU, takes off the free allowance, and charges the rest at your rate. Using EHU rather than a dollar price per action keeps every product on one bill and one allowance. ## Free allowance Every organization gets **200 EHU free every month**, from the day it signs up. The allowance: - resets at the start of each calendar month, - does not carry over to the next month, - applies to paying organizations too. You are only ever charged for usage above it. A month spent entirely inside the allowance costs nothing, and you do not need a card on file to use EvoHub within it. ## Rates How much one EHU costs depends on how you pay. | How you pay | Price per EHU | | | --- | --- | --- | | **On-demand** | $0.015 | Pay as you go. Charged to your card after each month. | | **Commitment** | $0.012 | Commit to a monthly volume for a 12-month term. | | **Prepaid credit** | $0.009 | Buy EHU bundles up front. Bundles never expire. | There are no per-user fees and no monthly base fee. See [Usage and payments](https://docs-dev.evohub.io/usage-and-payments.md) for how commitments and credit work. ## What costs what | Action | Cost | Notes | | --- | --- | --- | | On-Call voice call | 10 EHU (about $0.15) | Each call placed to a person | | On-Call alert received | 1 EHU (about $0.015) | Each alert taken in from an integration | | Email notification | 0.7 EHU (about $0.0105) | On-Call email notifications | | Mobile push notification | Free | | | Outbound webhook | Free | | | Uptime check | 0.001 EHU per check | A 1-minute monitor checked from one location is about $0.65 a month | | Uptime heartbeat monitor | Free | | | Status page on a custom domain | 0.9 EHU per page-hour | About $9–10 a month per page | | Status page subscriber email | 0.7 EHU per email | Each update email delivered to a subscriber | | Docs site on a custom domain | 2.5 EHU per site-hour | About $27 a month per site, any number of editors | | Changelog on a custom domain | 0.9 EHU per site-hour | About $9–10 a month per changelog | | Changelog subscriber email | 0.7 EHU per email | Each update or digest email delivered to a subscriber | Prices in dollars are at the on-demand rate. - **Hourly items** (status pages, docs sites and changelogs on a custom domain) are charged for every hour they are published on your own domain, whatever the traffic. An average month has 730 hours. - **Only what happened is billed.** A notification that was not sent, for example because it was suppressed or failed, is not charged. - **SMS** is not offered. EvoHub notifies by voice call, mobile push and email. - **Members, teams, retros and boards** cost nothing. > [!NOTE] > Weights are fractional, so EHU are added up exactly during the month and rounded up to a whole EHU once per line on the monthly statement, never per event. ## Examples These examples use a 30-day month and the on-demand rate. :::details{title="A small team that stays free"} | Usage | EHU | | --- | --- | | 50 alerts received | 50 | | 5 voice calls | 50 | | 2 uptime monitors every minute, one location | 86.4 | | Push notifications | 0 | | **Total** | **186.4** | 186.4 EHU is inside the 200 EHU allowance, so the month costs **$0**. ::: :::details{title="An on-call team with a public status page"} | Usage | EHU | | --- | --- | | 300 alerts received | 300 | | 20 voice calls | 200 | | 100 notification emails | 70 | | 5 uptime monitors every minute, one location | 216 | | 1 status page on a custom domain (720 hours) | 648 | | **Total** | **1,434** | 1,434 − 200 free = 1,234 EHU × $0.015 = **about $18.51** for the month, whether the team has 5 people or 50. ::: ## Paying for what you use Usage is paid for in a fixed order: 1. **Free allowance** first. 2. **Credit** next, if you have bought any. 3. **Commitment**, if you have one in force. 4. **On-demand**, charged to your card. You are billed once a month, after the month closes, never per call or per message. See [Usage and payments](https://docs-dev.evohub.io/usage-and-payments.md). ## Related - [Usage and payments](https://docs-dev.evohub.io/usage-and-payments.md) - [What is EvoHub](https://docs-dev.evohub.io/what-is-evohub.md) - [Pricing page](https://evohub.io/pricing) --- Source: https://docs-dev.evohub.io/usage-and-payments.md # Usage and payments Everything about what your organization uses and pays lives under **Usage & Billing** in the avatar menu. This page walks through each screen: the overview and estimate, usage by service, statements, payment method, credit and commitment. ## Who can see and change billing - Every member can read usage, statements and the saved cards. The default Viewer, Member and Admin roles all include the billing read permissions. - Adding or removing a card, changing billing details, buying credit and committing to a volume are for Owners and Admins, or anyone given the matching permission (**Payment Methods → Write**, **Billing Profile → Write**, **Credit → Write**, **Commitment → Write**) through a custom role. The ready-made **Billing Admin** role holds all of them. See [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md). ## Overview **Overview** answers "what will this month cost?": - **Estimated bill this month**: what you are likely to pay for the month, after your free allowance and credit, estimated from the last 7 days. - **Charged so far**: what has actually been charged, the EHU used this period, and your rate. - **Billing cycle**: the day of the month you are on and when usage resets. - **Daily usage**: a chart of EHU per day. - **Free allowance**: how much of this month's free EHU is left. Marked **Running low** and **Used up** as it runs out. - **Credit**: your prepaid balance, if any. - **Commitment**: your committed monthly volume and how much of it you have used, if you have a commitment. > [!WARNING] > Without a card or credit, alert notifications stop once the month's free allowance is used up. Alerts keep being received and recorded, so nothing is lost, but nobody is paged. The Overview says so in red when it happens, and adding a card or buying credit resumes notifications. Add a card before you need it. ## Usage **Usage** shows everything your organization used this period, broken down by service and by metric, with: - **How your charge is worked out (so far)**: usage, minus the free allowance, minus credit, priced at your rate. - **List total (before free allowance)**: what the usage would cost at list price. This is not your bill. - **Daily usage**: the same daily chart as the Overview. ## Statements You get one statement per month. Each month goes through these stages: | Status | Meaning | | --- | --- | | **current period** | The month in progress. Figures update as usage comes in. | | **estimate — not charged** | The month has ended and the statement is being completed. Nothing has been charged yet. | | **finalized — not yet charged** | The statement is frozen and will be charged shortly. | The statement is finalized a few days after the month ends, so that late usage is included, and only a finalized statement is ever charged. Once charged, the status shows what happened to the money instead: | Status | Meaning | | --- | --- | | **No charge** | The month stayed inside the free allowance or was covered by credit. | | **Carried over** | The amount was too small to charge to a card (under $0.50). It is added to the next month's charge, not written off. | | **No card on file** | There was something to pay but no card to charge. | | **Awaiting payment** | An invoice was raised and payment is pending. | | **Paid** | The invoice was paid. | | **Payment failed** | The card was declined. | Select a month to see **How this was worked out**: metered usage, the free allowance, what was paid from credit, the committed volume and anything above it. The **Documents** column links to the invoice and receipt once they exist. The **Payments** list below the statements shows every charge, whether a monthly usage bill, a credit bundle or the cost of ending a commitment early, with its invoice and receipt. ## Payment method **Payment Method** has two parts. ### Billing details What appears on your invoices: **Billing as** (**A business** or **An individual**), the legal name, the **Invoice email** (where invoices, receipts and billing notices are sent), the address, and an optional **Business tax number**. Select **Save billing details**. Fill these in before your first invoice; tax is worked out from this address. ### Saved cards :::steps ### Add a card Select **Add a card** (or **Add another card**). The card form is provided by Stripe, EvoHub's payment processor, and card numbers never reach EvoHub's servers. ### Save it Enter the card details and select **Save card**. It can take a moment for the card to appear while Stripe confirms it. ### Choose the default The default card is the one monthly bills are charged to. Select **Make default** on another card to change it, or **Remove** to delete one. ::: Card is the only payment method offered. ## Credit **Credit** lets you buy EHU up front at the prepaid rate, the lowest rate EvoHub offers ($0.009 per EHU). - Choose a bundle size (1,000, 5,000, 25,000 or 100,000 EHU) or enter your own amount. The minimum is 1,000 EHU. - You need a saved card first. Pay with a saved card or a different one, then confirm the payment. - Credit is added once the payment goes through, and an invoice and receipt appear under **Payments**. - Purchased bundles never expire, and they are non-refundable. - Your balance is spent each month after the free allowance and before your card is charged. The **Where your credit came from** section separates **Purchased bundles** from credit **Granted by EvoHub**. Granted credit can carry an expiry date, shown next to it. **History** lists every purchase, grant and monthly use of credit. ## Commitment **Commitment** gives you a lower rate ($0.012 per EHU, 20% below on-demand) in exchange for committing to a monthly volume for a 12-month term. - Enter the **EHU per month** you commit to, read and accept the commitment agreement, and confirm. The term starts at the beginning of the next month. - You are billed for the full committed volume every month, whether or not you use it. Unused volume does not carry over. - Usage above the commitment is billed at the on-demand rate. - You can increase your commitment at any time; this starts a fresh term at the higher volume. You cannot lower it during the term. - A commitment does not renew on its own. EvoHub emails you 30 days before the term ends, and afterwards you return to on-demand pricing. - **End this commitment early** recharges the months already served at the on-demand rate and invoices the difference. The exact amount is shown before you confirm. ## Related - [How billing works](https://docs-dev.evohub.io/how-billing-works.md) - [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md) - [Privacy and your data](https://docs-dev.evohub.io/privacy-and-data.md) --- Source: https://docs-dev.evohub.io/api-overview.md # API overview The EvoHub API lets scripts, CI jobs and other systems read and change what is in your organization: alerts, schedules, monitors, status pages and more. This page covers what every request has in common. ## Base URL All endpoints live under one base URL: ``` https://evohub.io/api/v1 ``` Every request must use HTTPS. Paths in these docs are relative to the base URL, so `GET /monitors` means `GET https://evohub.io/api/v1/monitors`. > [!NOTE] > Sending alerts *into* EvoHub from a monitoring tool does not use the API key flow described here. Each On-Call integration has its own URL with its own credential. See [Generic webhook](https://docs-dev.evohub.io/generic-webhook.md). ## Authentication Authenticate with an **API key**. Create one in the console under **My Account → API Keys**, or as an organization key under **Organization → Organization Keys**. See [API keys and scopes](https://docs-dev.evohub.io/api-keys-and-scopes.md). Send the key in either of these headers. They are equivalent; use one. :::code-group ```bash [Authorization header] curl https://evohub.io/api/v1/monitors \ -H "Authorization: Bearer evohub_YOUR_KEY" ``` ```bash [X-API-Key header] curl https://evohub.io/api/v1/monitors \ -H "X-API-Key: evohub_YOUR_KEY" ``` ::: - Every EvoHub API key starts with `evohub_`. - A key works in exactly one organization, so you never pass an organization ID. - If you send both headers, they must carry the same key, or the request is refused. - A missing, unknown, revoked or expired key gets **401 Unauthorized**. A valid key that lacks the scope for an endpoint gets **403 Forbidden**. See [Errors](https://docs-dev.evohub.io/errors.md). ## A first request List your organization's uptime monitors. The key needs the `uptime:monitor:read` scope. ```bash curl https://evohub.io/api/v1/monitors \ -H "Authorization: Bearer $EVOHUB_API_KEY" ``` ```json { "data": [ { "id": "…", "name": "Marketing site", "url": "https://www.acme.example", "type": "http", "interval_seconds": 60, "is_active": true, "last_status": "up", "last_checked_at": "2026-10-09T08:15:00Z", "created_at": "2026-09-01T10:00:00Z", "updated_at": "2026-10-01T12:30:00Z" } ] } ``` The example shows a subset of the fields a monitor returns. ## Requests and responses - Send request bodies as JSON with `Content-Type: application/json`. - Successful responses are JSON with the result in a `data` field. Many endpoints also include `"success": true`. - Errors are JSON with an `error` object instead. See [Errors](https://docs-dev.evohub.io/errors.md). - Timestamps are RFC 3339 strings in UTC, for example `2026-10-09T08:15:00Z`. Send timestamps in the same format. - IDs are opaque strings. Store them as they are; do not parse them. ## Pagination Most list endpoints return the whole list. Where a list can grow large, it is paged with `limit` and `offset` query parameters, and the response carries the total number of matches in `meta.total` next to `data`. For example, the On-Call alert list returns 50 alerts by default. The key needs the `oncall:alert:read` scope. ```bash curl "https://evohub.io/api/v1/alerts?status=triggered&limit=20&offset=0" \ -H "Authorization: Bearer $EVOHUB_API_KEY" ``` ```json { "data": [ { "id": "…", "title": "High error rate on checkout", "severity": "critical", "status": "triggered", "created_at": "2026-10-09T08:01:12Z" } ], "success": true, "meta": { "total": 3 } } ``` To read the next page, add `limit` to `offset` and request again until you have `meta.total` items. ## Request IDs Every response carries an `X-Request-ID` header, and most error bodies repeat it as `request_id`. Include it when you contact support about a request; it lets us find exactly that call. You can also send your own `X-Request-ID` header, and EvoHub uses it instead of generating one. ## Rate limits Requests are rate limited per API key (or, for requests without a key, per IP address), counted over a 10-second window. The limit is generous, on the order of 100 requests per second, and is meant to stop runaway scripts, not normal automation. When you go over it, EvoHub answers **429 Too Many Requests** with a `RATE_LIMITED` error and a `Retry-After: 10` header. Wait at least that many seconds before retrying, and back off further if it happens again. Responses also carry `X-RateLimit-Limit` and `X-RateLimit-Remaining` headers. Alert ingest URLs and heartbeat ping URLs are not subject to this limit, so a burst of alerts during an outage is never dropped for being too many. ## Related - [API keys and scopes](https://docs-dev.evohub.io/api-keys-and-scopes.md) - [Errors](https://docs-dev.evohub.io/errors.md) - [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md) --- Source: https://docs-dev.evohub.io/api-keys-and-scopes.md # API keys and scopes An API key lets a script, CI job or another system call the EvoHub API without a person signing in. Each key carries **scopes**: the exact permissions it has. This page explains the two kinds of key, how to create and revoke them, and their limits. ## Two kinds of key | | Personal key | Organization key | | --- | --- | --- | | Where | **My Account → API Keys** | **Organization → Organization Keys** | | Who can create one | Anyone signed in | People with the **Organization Keys → Write** permission (Owners and Admins by default) | | What it can do | The scopes you pick, never more than you hold yourself | Exactly what its role grants, as the role changes over time | | How long it lives | Stops working when you leave the organization or delete your account | Belongs to the organization and outlives the people who manage it | | Best for | Your own scripts and experiments | CI pipelines and integrations that must keep running | Both kinds work in one organization only, and both use the same headers. See [API overview](https://docs-dev.evohub.io/api-overview.md#authentication). ## Create a personal key :::steps ### Open API Keys Open the avatar menu, choose **My Account**, then **API Keys**, and select **Create Key**. ### Name it Enter a **Name** that says what the key is for, for example "Deploy pipeline". The name is the only way to tell keys apart later. ### Pick the organization Under **Organization**, choose which of your organizations the key works in. ### Choose an expiry Under **Expires**, pick **90 days** (the default), **180 days**, **1 year** or **Never expires**. ### Choose scopes Tick the permissions the key needs. They are grouped by product, as in the role editor. Anything you do not hold yourself in that organization is greyed out. Selecting a write scope also selects the read it depends on. ### Copy the key Select **Create Key**. The key is shown once, in the **Copy your key now** dialog. Copy it and store it in your secret manager, then select **I have copied it**. ::: > [!WARNING] > EvoHub stores only a hash of each key, so a key cannot be shown again or recovered. If you lose it, revoke it and create a new one. A key with no scopes can authenticate but is refused everywhere. That is the safe default, not an unrestricted key. A personal key is always a slice of you, measured at the moment it is used. If your own permissions shrink, for example because an administrator changed your role, the key loses those permissions too. If you leave the organization or delete your account, the key stops working. ## Create an organization key :::steps ### Open Organization Keys Go to **Organization → Organization Keys** and select **Create Key**. ### Name it Enter a **Name**, for example "GitHub Actions — deploy". ### Pick a role Under **Role**, choose the role whose permissions the key should have. You can only pick a role whose permissions you hold yourself, and only an administrator can give a key the Admin role. Create a [custom role](https://docs-dev.evohub.io/roles-and-permissions.md#custom-roles) that holds exactly what the integration needs. ### Choose what it sees Under **Sees**, keep **Whole organization** or pick a team. A key scoped to a team sees that team's boards, schedules and monitors, the same view a member of that team has. ### Choose an expiry and copy the key Pick an expiry under **Expires**, select **Create Key**, and copy the key from the dialog. As with personal keys, it is shown only once. ::: An organization key's scopes are its role's, read live. Edit the role and every key on it changes; delete the role and every key on it stops working. ## Scopes Scopes are the same permissions roles are built from. They are grouped by product: | Product | Scopes | | --- | --- | | Organization | `identity:user:read`, `identity:user:write`, `identity:user:invite`, `identity:team:read`, `identity:team:write`, `identity:role:read`, `identity:role:write`, `identity:org:write`, `identity:apikey:read`, `identity:apikey:write`, `identity:audit:read` | | On-Call | `oncall:alert:read`, `oncall:alert:write`, `oncall:alert:respond`, `oncall:incident:read`, `oncall:incident:write`, `oncall:schedule:read`, `oncall:schedule:write`, `oncall:escalation:read`, `oncall:escalation:write`, `oncall:integration:read`, `oncall:integration:write`, `oncall:maintenance:read`, `oncall:maintenance:write`, `oncall:postmortem:read`, `oncall:postmortem:write`, `oncall:settings:write`, `oncall:audit:read` | | Uptime | `uptime:monitor:read`, `uptime:monitor:write`, `uptime:incident:read`, `uptime:silence:read`, `uptime:silence:write`, `uptime:channel:read`, `uptime:channel:write`, `uptime:audit:read` | | Status pages | `status:page:read`, `status:page:write`, `status:component:read`, `status:component:write`, `status:incident:read`, `status:incident:write`, `status:maintenance:read`, `status:maintenance:write`, `status:subscriber:read`, `status:subscriber:write`, `status:audit:read` | | Retro | `retro:board:read`, `retro:board:write`, `retro:board:delete`, `retro:card:read`, `retro:card:write`, `retro:action:read`, `retro:action:write`, `retro:audit:read` | | Board | `board:board:read`, `board:board:write`, `board:board:delete`, `board:list:read`, `board:list:write`, `board:card:read`, `board:card:write`, `board:member:write`, `board:review:write` | | Docs | `docs:site:read`, `docs:site:write`, `docs:site:delete`, `docs:page:write`, `docs:page:publish` | | Changelog | `changelog:site:read`, `changelog:site:write`, `changelog:site:delete`, `changelog:entry:write`, `changelog:entry:publish` | | Billing | `billing:usage:read`, `billing:invoice:read`, `billing:payment:read`, `billing:payment:write`, `billing:profile:read`, `billing:profile:write`, `billing:credit:write`, `billing:commitment:write` | | Support | `support:audit:read` | What each permission covers is described in [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md#permissions-reference). Give a key the fewest scopes it needs. A key that only reads monitors needs `uptime:monitor:read` and nothing else. ## What a key can never do API keys are for automation, and some actions are reserved for a person signed in to the console. - **A key never counts as an administrator.** Owners and Admins pass every check when they act in person, but a key is held to its scopes, even if it was created by an Admin or carries the Admin role. A key never sees another team's work just because its creator could. - **A key cannot approve or review.** Approving or requesting changes on docs change requests and changelog drafts, and merging or reverting a docs change request, must be done by a person. So must connecting a docs site to GitHub and issuing access credentials for a private docs site. A key that tries gets 403 `PERSON_REQUIRED`. - **A key cannot act on a person's own account.** It cannot switch organizations, create an organization, change a profile or password, manage sessions, two-factor authentication or connected accounts, accept invitations, or delete an account. These return 403 `API_KEY_NOT_ALLOWED`. - **A key cannot manage API keys.** Creating, listing and revoking keys, of either kind, is done in the console. - **An organization key cannot send invitations**, because an invitation names the person who sent it. Use a personal key with `identity:user:invite`. ## Revoke a key Revoking stops a key immediately, and anything still using it starts failing at once. It cannot be undone. - **Personal key:** in **My Account → API Keys**, select **Revoke** on the key's row and confirm. You can revoke your own keys in any organization. - **Organization key:** in **Organization → Organization Keys**, select **Revoke** on the key's row and confirm. This needs the **Organization Keys → Write** permission. Your **My API Keys** list shows each key's prefix, organization, scopes (hover the count to see them), state (**active**, **revoked** or **expired**), when it was last used and when it expires. A key that has never been used shows **Never** under **Last used**, which makes unused keys easy to find and revoke. > [!TIP] > If a key leaks, for example into a public repository or a log, revoke it first and then create its replacement. Because every key starts with `evohub_`, secret scanners can be configured to spot it. ## Related - [API overview](https://docs-dev.evohub.io/api-overview.md) - [Errors](https://docs-dev.evohub.io/errors.md) - [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md) - [Security at EvoHub](https://docs-dev.evohub.io/security.md) --- Source: https://docs-dev.evohub.io/errors.md # Errors When a request fails, the EvoHub API answers with an HTTP status code and a JSON body that says what went wrong in a form your code can act on. This page explains the format and the codes you are most likely to meet. ## The error format Every error body has an `error` object: ```json { "error": { "code": "FORBIDDEN", "message": "insufficient permissions", "request_id": "req_4f1c2a9e7b3d5a6c8e0f1a2b" } } ``` | Field | Meaning | | --- | --- | | `code` | A stable, machine-readable code in capitals. Branch on this. | | `message` | A human-readable explanation. Show or log it, but do not parse it; the wording can change. | | `request_id` | The ID of this request. Present on most errors; the same value is always in the `X-Request-ID` response header. | Validation errors can add a `details` array that names each field that failed: ```json { "error": { "code": "VALIDATION_FAILED", "message": "request validation failed", "details": [ { "field": "email", "message": "email is required" } ] } } ``` ## Status codes | Status | Meaning | What to do | | --- | --- | --- | | **400 Bad Request** | The request is malformed or a field is invalid. | Fix the request. Read `message` and `details`. | | **401 Unauthorized** | No credentials, or the credentials are not valid: the API key is missing, unknown, revoked or expired. | Check the key and the header. Retrying the same request will not help. | | **403 Forbidden** | You are authenticated, but not allowed to do this. | Give the key the scope it needs, or use a person's account where a person is required. | | **404 Not Found** | The resource does not exist, or you are not allowed to know it exists. | Check the ID and which organization or team the key belongs to. | | **409 Conflict** | The request conflicts with the current state, for example a name already taken or a limit reached. | Change the request; it will not succeed as is. | | **429 Too Many Requests** | You went over the rate limit. | Wait for the `Retry-After` seconds, then retry with backoff. | | **500 Internal Server Error** | Something went wrong on EvoHub's side. | Retry later. If it persists, contact support with the request ID. | | **503 Service Unavailable** | EvoHub could not complete the request right now. | Retry shortly with backoff. | ## 401 versus 403 EvoHub keeps these two strictly apart: - **401** always means "we do not know who you are". The credential is missing or not valid. - **403** always means "we know who you are, and you may not do this". A missing scope, an action reserved for administrators, or an action that only a person may take all return 403, never 401. So a 401 is a credential problem, and a 403 is a permission problem. Rotating a key will not fix a 403; changing its scopes or role will. > [!NOTE] > A resource that belongs to a team the key cannot see answers **404**, not 403. Saying "forbidden" would confirm that something with that ID exists. ## Common error codes | Code | Status | Meaning | | --- | --- | --- | | `UNAUTHORIZED` | 401 | No valid credential was sent, or the API key is invalid or expired. The message says which header to send. | | `FORBIDDEN` | 403 | The key or person lacks the permission this endpoint needs. Also returned when you try to give a key or role a permission you do not hold. | | `ADMIN_ONLY` | 403 | Only an administrator can do this, for example giving a key the Admin role. | | `PERSON_REQUIRED` | 403 | This action (such as approving a change request) must be done by a person signed in to EvoHub, not with an API key. | | `API_KEY_NOT_ALLOWED` | 403 | This endpoint is for a signed-in person acting on their own account, such as managing sessions or two-factor authentication. | | `NOT_A_PERSON` | 403 | An organization key tried to do something that must name a person, such as sending an invitation. Use a personal key. | | `VALIDATION_ERROR` | 400 | A field is missing or invalid. The message names it. | | `VALIDATION_FAILED` | 400 | One or more fields are invalid. Each is listed in `details`. | | `INVALID_BODY` | 400 | The request body is not valid JSON. | | `UNKNOWN_PERMISSION` | 400 | A scope or permission name is not one EvoHub knows. | | `RATE_LIMITED` | 429 | Too many requests. See [Rate limits](https://docs-dev.evohub.io/api-overview.md#rate-limits). | | `AUTH_UNAVAILABLE` | 503 | The key could not be checked right now. Your key is fine; retry shortly. | | `INTERNAL_ERROR` | 500 | An unexpected error on EvoHub's side. | Not-found errors often name the resource, for example `MONITOR_NOT_FOUND`. Treat any 404 the same way, whatever its code. ## Request IDs Every response, successful or not, carries an `X-Request-ID` header, and most error bodies repeat it as `request_id`. Log it with every failed call. When you contact support at info@evosync.io, include the request ID and the time of the request; it lets us find that exact call. You can also set your own `X-Request-ID` header on a request, for example your CI job's run ID, and EvoHub uses it instead of generating one. ## Handling errors well - Branch on `code` and the status, not on `message`. - Retry only **429**, **500** and **503**, with exponential backoff. Do not retry **400**, **401**, **403**, **404** or **409** unchanged. - On **401** from a key that used to work, check whether it was revoked or has expired in **My Account → API Keys** or **Organization → Organization Keys**. ## Related - [API overview](https://docs-dev.evohub.io/api-overview.md) - [API keys and scopes](https://docs-dev.evohub.io/api-keys-and-scopes.md) - [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md) --- Source: https://docs-dev.evohub.io/security.md # Security at EvoHub This page describes the measures that protect your EvoHub account and your organization's data, and the settings you control. It only lists what EvoHub actually does today. ## Encryption in transit All traffic to EvoHub, including the console, the API and the mobile apps, is encrypted in transit with TLS 1.2 or higher. ## Passwords - Passwords are hashed with bcrypt, a salted one-way hash, before they are stored. EvoHub never stores or logs a password in plain text, and nobody at EvoHub can read yours. - A password must be at least 8 characters. Administrators can raise the minimum for their organization (see [Login policies](#login-policies)). - You can sign in with Google or GitHub instead of a password, and connect or disconnect those accounts under **My Account → Authentication → Connected accounts**. ## Sign-in protection - **Account lock.** After 5 wrong passwords, the account is locked for 15 minutes, even for the right password. This makes guessing passwords slow and expensive. - **Bot protection.** The sign-in, sign-up, password-reset and verification-email forms are protected by Cloudflare Turnstile, which tells people apart from automated abuse. - **Two-factor authentication.** Add a time-based one-time code from an authenticator app to every sign-in. See [Two-factor authentication](https://docs-dev.evohub.io/two-factor-authentication.md). - **Short-lived links.** Password reset links are valid for 15 minutes and email verification links for 24 hours. ## Sessions - After you sign in, the console holds a short-lived, signed access token that is renewed in the background while you use EvoHub. Sessions are stored on EvoHub's servers and can be ended at any time. - A session ends after a period of inactivity. The default is 7 days; administrators can change it (see [Login policies](#login-policies)). - **My Account → Active Sessions** lists every device signed in to your account, with its IP address, when it was last seen and when it signed in. Select **Revoke** to sign a device out. See [Two-factor authentication](https://docs-dev.evohub.io/two-factor-authentication.md#review-your-sessions). - Signing out ends the session. Deleting your account ends all of them. ## Login policies Administrators can set security requirements for everyone in an organization under **Organization → Login Policies**: | Setting | What it does | | --- | --- | | **Require MFA** | Every member must set up two-factor authentication before they can enter the organization. | | **Session Timeout** | Hours of inactivity after which members are signed out. Default 168 (7 days). | | **Minimum Password Length** | Passwords shorter than this are rejected when members set or change a password. | Select **Save Policies** to apply them. ## Tenant isolation Every organization's data is kept apart from every other organization's. - Every request is tied to exactly one organization by the signed token or API key it carries. EvoHub's servers decide which organization a request belongs to; a client cannot choose it. - Every read and write of organization data is filtered by that organization. An ID that belongs to another organization simply is not found. - Inside an organization, work can be scoped to a team. Something that belongs to a team you cannot see is reported as not found, so its existence is not revealed. ## Authorization on the server - Every action is checked against the caller's organization roles on EvoHub's servers. The console hides what you cannot do, but the API enforces it independently. See [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md). - You can never grant a role, or give an API key, a permission you do not hold yourself. - A refused action returns 403. It never signs you out. ## API keys - An API key is shown once, when it is created. EvoHub stores only a SHA-256 hash of it, so a key cannot be recovered from EvoHub, even by EvoHub. - A key has exactly the scopes it was given, can expire, and can be revoked instantly. A key never counts as an administrator. - A personal key loses whatever its owner loses, and stops working when its owner leaves the organization or deletes their account. - Every key starts with `evohub_`, so secret scanners can recognize a leaked key. See [API keys and scopes](https://docs-dev.evohub.io/api-keys-and-scopes.md). ## Payment details Card details are entered into a form provided by Stripe, EvoHub's payment processor. Card numbers go directly to Stripe and never reach EvoHub's servers. ## Audit logs **Organization → Audit Logs** records administrative and configuration actions, such as creating and revoking API keys and changes to members, roles and settings, across the organization. It is visible to people with an audit-log read permission. The default Admin, Member and Viewer roles all include it; build custom roles without it if you want to restrict who reads the log. Audit logs are kept for 1 year. ## Report a security problem If you believe your account has been compromised, or you have found a security issue in EvoHub, email info@evosync.io right away. ## Related - [Two-factor authentication](https://docs-dev.evohub.io/two-factor-authentication.md) - [Privacy and your data](https://docs-dev.evohub.io/privacy-and-data.md) - [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md) - [API keys and scopes](https://docs-dev.evohub.io/api-keys-and-scopes.md) --- Source: https://docs-dev.evohub.io/two-factor-authentication.md # Two-factor authentication Two-factor authentication (2FA, also called MFA) adds a second step to signing in: after your password, or after Google or GitHub, EvoHub asks for a 6-digit code from an authenticator app on your phone. This page explains how to turn it on, how an organization can require it, and how to manage your signed-in devices. ## What you need An authenticator app that supports time-based one-time passwords (TOTP), such as Google Authenticator, Authy, 1Password or Microsoft Authenticator. EvoHub does not send codes by SMS or email. ## Turn on two-factor authentication :::steps ### Open Authentication Open the avatar menu, choose **My Account**, then **Authentication**. Next to **Two-factor authentication**, select **Enable**. ### Show the QR code The QR code is hidden so that nobody nearby can scan it. Select **Show QR code** when you are ready. ### Scan it Scan the QR code with your authenticator app. If you cannot scan, type the key shown under **Can't scan? Enter this key manually** into the app instead. Then select **I've scanned it**. ### Confirm with a code Enter the 6-digit code your app now shows and select **Enable MFA**. ::: From now on, every sign-in to your account asks for a code. > [!WARNING] > EvoHub does not currently provide backup codes. Before you finish setup, keep a copy of the setup key somewhere safe, such as your password manager, or add the account to a second device. With the key you can restore the codes on a new phone. ## Sign in with a code 1. Sign in with your email and password, or with Google or GitHub. 2. On the **Two-factor authentication** screen, enter the current 6-digit code from your app. The code screen is valid for 5 minutes. If it expires, start the sign-in again. Codes change every 30 seconds; if one is rejected, wait for the next one and check that your phone's clock is set automatically. ## Turn off two-factor authentication Go to **My Account → Authentication**, select **Disable** next to **Two-factor authentication**, enter a current code from your authenticator app and select **Disable MFA**. A current code is required, so someone who only knows your password cannot turn it off. If your organization requires two-factor authentication, you will be asked to set it up again the next time you sign in. ## If you lose your device - If you saved the setup key, add it to the authenticator app on your new device and sign in as usual. Then turn 2FA off and on again to get a fresh key. - Turning 2FA off also needs a code, so being signed in elsewhere does not help on its own; restore the key first. - If you have neither the key nor a second device, contact support at info@evosync.io from the email address on your account. ## Require two-factor authentication for an organization Administrators can make 2FA mandatory for everyone in an organization. :::steps ### Open Login Policies Go to **Organization → Login Policies**. ### Turn on Require MFA Switch on **Require MFA** ("All members must enable two-factor authentication") and select **Save Policies**. ::: What happens next: - Members who already use 2FA notice nothing. - A member without 2FA is taken through setup the next time they sign in, whether with a password or with Google or GitHub, and enters the organization only after it is done. - Someone switching into the organization from another one is asked to set up 2FA first. ## Review your sessions **My Account → Active Sessions** lists every device signed in to your account: browser and operating system, IP address, when it was last seen and when it signed in. Your current device is marked **Current session**, and the mobile app is marked **MOBILE**. To sign a device out, select **Revoke** and confirm with **Revoke Session**. Do this for any device you do not recognize, and then change your password. Sessions also end on their own after a period of inactivity, 7 days by default. Administrators can change this under **Organization → Login Policies → Session Timeout**. ## Related - [Security at EvoHub](https://docs-dev.evohub.io/security.md) - [Create your account](https://docs-dev.evohub.io/create-your-account.md) - [Privacy and your data](https://docs-dev.evohub.io/privacy-and-data.md) --- Source: https://docs-dev.evohub.io/privacy-and-data.md # Privacy and your data This page explains the choices you have over your data in EvoHub: deleting your account or an organization, how long different kinds of data are kept, and how cookies and analytics work. The legal details are in the [Privacy Notice](https://evohub.io/privacy), which takes precedence if anything here differs. ## Who can see your data - Everything you create in EvoHub belongs to an organization. Other organizations can never see it. - Inside the organization, members see what their roles and teams allow. See [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md). - EvoHub is operated by EvoSync LLC, which is the data controller. EvoHub does not sell personal data and does not use it to show you third-party advertising. ## Delete your account :::steps ### Open your profile Open the avatar menu, choose **My Account**, then **Settings**, and scroll to **Delete Account**. ### Confirm Select **Delete Account**, type your email address to confirm, and select **Delete my account**. ::: What happens: - **Immediately:** all your sessions are ended and your personal API keys stop working. - **For 30 days:** the account is scheduled for deletion but kept intact. Signing back in during this time cancels the deletion and restores the account as it was. - **After 30 days:** the account is permanently deleted, including your profile, notification preferences, push notification registrations, Google and GitHub connections and your memberships of organizations. Your personal organization is deleted with it if nobody else is in it. > [!NOTE] > You cannot delete your account while you are the only Owner of an organization that has other members, because that organization would be left without an owner. Delete that organization first, or contact info@evosync.io. Content you created inside an organization, such as incidents or board cards, belongs to that organization and stays with it. ## Delete an organization The Owner can delete an organization under **Organization → Org Settings → Danger Zone** by typing its name. Members lose access immediately, and the organization's data in every product is removed: On-Call, Uptime, status pages, retros, boards, docs sites and changelogs. This is permanent. Personal organizations cannot be deleted; they go when their owner's account is purged. See [Organizations and teams](https://docs-dev.evohub.io/organizations-and-teams.md#delete-an-organization). ## How long data is kept | Data | Kept for | | --- | --- | | Account data | While your account exists, and 30 days after you delete it | | Resolved alerts and their notification and timeline records | 1 year, then deleted automatically | | Incidents, postmortems and on-call configuration (schedules, rotations, escalation policies) | As long as the account exists | | Uptime check results | 90 days | | Board cards | Until you archive or delete them; archived cards are deleted 2 years after archiving | | Audit logs | 1 year | | Billing and usage records that make up your invoices | As long as tax and accounting law requires, also after an account or organization is closed | ## Where data is processed EvoHub is operated from servers in Türkiye. If you use EvoHub from elsewhere, your data is transferred to and processed there. The [Privacy Notice](https://evohub.io/privacy) describes the safeguards for international transfers. EvoHub uses service providers to run the platform: providers for voice, email and mobile push notifications, a payment processor, cloud infrastructure, Cloudflare for bot protection on sign-in and sign-up forms, and, only if you accept optional cookies on the website, Google for analytics and ad measurement. A current list of sub-processors is available on request from info@evosync.io. ## Cookies on evohub.io - Cookies that are essential for signing in and running the console are always set. - Optional cookies are set only if you choose **Accept all** in the cookie banner: Google Analytics, to understand how the site is used, and Google Ads measurement, to learn which of EvoHub's own ads led to a visit or sign-up. - If you choose **Essential only**, neither is set. Choosing it later removes the Google cookies already set. - Change your choice at any time with **Cookie settings** at the bottom of every page. The sign-in, sign-up, password-reset and verification-email forms use Cloudflare Turnstile for bot protection regardless of your cookie choice, because it is needed to keep accounts secure. It processes technical signals such as your IP address and browser, does not read what you type, and is not used for advertising. ## Analytics on docs sites and changelogs Docs sites and changelogs published with EvoHub measure their readership without cookies: - No cookie is set and no IP address is stored. A reader's IP address and browser details are turned into a one-way code that cannot be traced back to them. - Visitors are counted per day, not tracked across days. - On docs sites, readers whose browser sends Do Not Track or Global Privacy Control are not counted at all. Because nothing identifies a person, these analytics need no cookie banner. If a site's owner adds their own analytics tool, that tool's cookies and consent are the owner's responsibility. See [API references and AI](https://docs-dev.evohub.io/api-references-and-ai.md) and [Changelog settings](https://docs-dev.evohub.io/changelog-settings.md). ## Emails from EvoHub EvoHub sends transactional emails you need, such as email verification, password resets, invitations and alert notifications. Product announcements can be turned off with the unsubscribe link in any such email. ## Your rights Depending on where you live, you can ask for access to, correction of, deletion of, or a portable copy of your personal data, and you can object to or restrict some processing. To exercise any of these rights, email info@evosync.io. EvoHub usually responds within one business day, and always within 30 days. ## Related - [Security at EvoHub](https://docs-dev.evohub.io/security.md) - [Organizations and teams](https://docs-dev.evohub.io/organizations-and-teams.md) - [Privacy Notice](https://evohub.io/privacy) --- Source: https://docs-dev.evohub.io/alert-ingest.md # Alert ingest API Version 1.0 · OpenAPI 3.1.0 Send alerts into EvoHub On-Call from anything that can make an HTTP request. - **Events** (`POST /ingest/api`) — trigger, acknowledge and resolve alerts with a small JSON body. Compatible with the Events v2 shape, so a tool that already speaks it only changes the URL and key. - **Integration webhooks** (`POST /ingest/{type}`) — point a monitoring tool's own webhook at EvoHub; the body is whatever that tool sends. Every request is authenticated by an **integration key**, shown as **API Key** on the integration in On-Call → Integrations. The key decides the organization and the escalation policy; nothing else in the request can. ## Servers - `https://evohub.io` ## Authentication - `integrationKey`: API key in the query `key` — The integration key (the integration's API Key in On-Call → Integrations). - `bearerKey`: HTTP Bearer — The integration key as a bearer token (`/ingest/api` only). ## Events Trigger, acknowledge and resolve alerts by dedup key. - [POST /ingest/api](https://docs-dev.evohub.io/alert-ingest/send-event.md): Send an event ## Integration webhooks Native webhook payloads from monitoring tools. - [POST /ingest/{type}](https://docs-dev.evohub.io/alert-ingest/send-integration-webhook.md): Receive a monitoring tool's webhook --- Source: https://docs-dev.evohub.io/alert-ingest/send-event.md # Send an event `POST https://evohub.io/ingest/api` Part of the [Alert ingest API](https://docs-dev.evohub.io/alert-ingest.md) reference · operationId `sendEvent`. Triggers, acknowledges or resolves an alert. The integration key may be sent as `routing_key` in the body, as `?key=` in the URL, or as `Authorization: Bearer ` — checked in that order. A trigger needs only a `summary`; everything else has a default. Acknowledge and resolve find the open alert by `dedup_key`; when no open alert has that key, nothing happens and the answer is still 202. A trigger without a `dedup_key` gets one derived from its summary and source, returned in the response — keep it to resolve the alert later. A trigger whose `dedup_key` matches an open alert does not open a second one; it is recorded as a retrigger on the existing alert. ## Authorization Any one of: - `integrationKey` - `bearerKey` - No authentication Where: - `integrationKey`: API key in the query `key` — The integration key (the integration's API Key in On-Call → Integrations). - `bearerKey`: HTTP Bearer — The integration key as a bearer token (`/ingest/api` only). ## Query parameters - `key` (string): The integration key, when it is not sent as `routing_key` or a bearer token. ## Request body (required) Content type: `application/json` Type: `EventRequest` An event. The nested `payload` form and the flat fields are interchangeable; a flat field fills the payload field it names when that one is empty (`title` is another name for `summary`). - `routing_key` (string): The integration key. - `event_action` (string, one of `trigger`, `acknowledge`, `resolve`, default `trigger`) - `dedup_key` (string): Identifies the alert. Required for acknowledge and resolve. - `client` (string): Accepted for Events v2 compatibility; not stored. - `client_url` (string): Accepted for Events v2 compatibility; not stored. - `payload` (EventPayload) - `summary` (string, example `Database primary is down`): The alert's title. Required for a trigger. - `source` (string, example `db-01.prod.acme.example`): Where it happened. Stored as the `source` label. - `severity` (Severity, one of `critical`, `high`, `medium`, `low`, `info`, `error`, `warning`, default `medium`) - `description` (string) - `timestamp` (string (date-time)): Accepted for Events v2 compatibility; the alert is stamped on arrival. - `component` (string): Stored as the `component` label. - `group` (string): Stored as the `group` label. - `class` (string): Stored as the `class` label. - `custom_details` (object): Each entry becomes a label, its value as text. - Other keys: any - `summary` (string): Flat form of `payload.summary`. - `title` (string): Another name for `summary`. - `severity` (Severity, one of `critical`, `high`, `medium`, `low`, `info`, `error`, `warning`, default `medium`) - `source` (string): Flat form of `payload.source`. - `description` (string): The alert's description. ## Responses ### 202 — The event was processed. Content type: `application/json` Type: `EventAccepted` - `data` (object, required) - `status` (string, required, value `success`) - `message` (string, required, example `Event processed`) - `dedup_key` (string, required, example `db-primary-down`): The alert's dedup key — the one sent, or the one derived. - `success` (boolean, required, value `true`) ### 400 — The body is not JSON, `summary` is missing on a trigger, `dedup_key` is missing on an acknowledge or resolve, or `event_action` is unknown. Content type: `application/json` Type: `EventRejected` How `/ingest/api` answers a request it did not process. The envelope is the same as for a success (`success` is `true`); read the HTTP status and `data.status` to tell them apart. - `data` (object) - `status` (string, value `invalid event`) - `message` (string, example `summary is required`) - `success` (boolean) ### 401 — No integration key was sent, or it names no enabled integration. Content type: `application/json` Type: `EventRejected` How `/ingest/api` answers a request it did not process. The envelope is the same as for a success (`success` is `true`); read the HTTP status and `data.status` to tell them apart. - `data` (object) - `status` (string, value `invalid event`) - `message` (string, example `summary is required`) - `success` (boolean) ### 500 — The alert could not be created, acknowledged or resolved. Content type: `application/json` Type: `EventRejected` How `/ingest/api` answers a request it did not process. The envelope is the same as for a success (`success` is `true`); read the HTTP status and `data.status` to tell them apart. - `data` (object) - `status` (string, value `invalid event`) - `message` (string, example `summary is required`) - `success` (boolean) ### 503 — The key could not be checked right now. Retry after the delay given. Headers: - `Retry-After` (integer): Seconds to wait before retrying. Content type: `application/json` Type: `EventRejected` How `/ingest/api` answers a request it did not process. The envelope is the same as for a success (`success` is `true`); read the HTTP status and `data.status` to tell them apart. - `data` (object) - `status` (string, value `invalid event`) - `message` (string, example `summary is required`) - `success` (boolean) ## Example request ```bash curl -X POST 'https://evohub.io/ingest/api?key=' \ -H 'Content-Type: application/json' \ -d '{ "summary": "Database primary is down", "routing_key": "YOUR_INTEGRATION_KEY" }' ``` --- Source: https://docs-dev.evohub.io/alert-ingest/send-integration-webhook.md # Receive a monitoring tool's webhook `POST https://evohub.io/ingest/{type}` Part of the [Alert ingest API](https://docs-dev.evohub.io/alert-ingest.md) reference · operationId `sendIntegrationWebhook`. The endpoint each integration's webhook URL points at, shown with its key in On-Call → Integrations. `type` selects the parser. For `alerts` the body is EvoHub's generic alert (below). For every other type it is the tool's own webhook payload, sent unchanged — a firing alert opens an alert, and where the tool reports recovery, the matching open alert is resolved. Most tools send JSON; UptimeRobot, StatusCake, Site24x7 and PRTG may send form-encoded data (UptimeRobot may also append its fields to the URL's query string), and those are read too. Answers **200** whenever the payload was read, even if it produced no alert, so tools do not disable the webhook; `created` and `resolved` say what happened. An AWS SNS subscription confirmation sent to `cloudwatch` is confirmed automatically and answered with `{"data": {"confirmed": true}, "success": true}`. ## Authorization Requires: - `integrationKey` Where: - `integrationKey`: API key in the query `key` — The integration key (the integration's API Key in On-Call → Integrations). ## Path parameters - `type` (string, required, one of `alerts`, `prometheus`, `grafana`, `datadog`, `newrelic`, `cloudwatch`, `azuremonitor`, `dynatrace`, `sentry`, `googlecloud`, `zabbix`, `uptimerobot`, `pingdom`, `site24x7`, `statuscake`, `nagios`, `prtg`, `cortex`, `opensearch`, example `alerts`): The integration type. ## Query parameters - `key` (string, required, example `YOUR_INTEGRATION_KEY`): The integration key. ## Request body (required) Content type: `application/json` Type: `GenericAlert | object` - One of: - GenericAlert - `title` (string, required, example `Disk almost full on web-03`) - `description` (string) - `severity` (string, one of `critical`, `high`, `medium`, `low`, `info`, default `medium`) - `source` (string, default `webhook`): The alert's source; `webhook` when omitted. - `fingerprint` (string): Deduplicates repeated sends while the alert is open. - `escalation_policy_id` (string): Used only when the integration has no escalation policy of its own. - `labels` (object) - Other keys: string - `annotations` (object) - Other keys: string - Native payload: The monitoring tool's own webhook body, for every type except `alerts`. - Other keys: any Content type: `application/x-www-form-urlencoded` Type: `object` Form-encoded bodies sent by `uptimerobot`, `statuscake`, `site24x7` and `prtg`. - Other keys: string ## Responses ### 200 — The payload was read. Content type: `application/json` Type: `IngestResult` - `data` (object) - `received` (integer): Alerts and recoveries found in the payload. - `created` (integer): Alerts opened, or matched to an alert already open with the same fingerprint. - `resolved` (integer): Recoveries processed. - `success` (boolean, value `true`) ### 400 — The body could not be read as this type's payload (`INVALID_BODY`); for `azuremonitor`, the common alert schema is not enabled (`INVALID_SCHEMA`); for `alerts`, `title` is missing (`VALIDATION_FAILED`, with `details`). Content type: `application/json` Type: `Error` - `error` (object, required) - `code` (string, required, example `INVALID_KEY`) - `message` (string, required, example `invalid integration key`) - `details` (array of object): Field problems, on `VALIDATION_FAILED`. - `field` (string) - `message` (string) ### 401 — `key` is missing, unknown, or names a disabled integration (`INVALID_KEY`). Content type: `application/json` Type: `Error` - `error` (object, required) - `code` (string, required, example `INVALID_KEY`) - `message` (string, required, example `invalid integration key`) - `details` (array of object): Field problems, on `VALIDATION_FAILED`. - `field` (string) - `message` (string) ### 500 — The alert could not be stored (`INTERNAL_ERROR`). Content type: `application/json` Type: `Error` - `error` (object, required) - `code` (string, required, example `INVALID_KEY`) - `message` (string, required, example `invalid integration key`) - `details` (array of object): Field problems, on `VALIDATION_FAILED`. - `field` (string) - `message` (string) ### 503 — The key could not be checked right now (`INGEST_UNAVAILABLE`). Retry after the delay given. Headers: - `Retry-After` (integer): Seconds to wait before retrying. Content type: `application/json` Type: `Error` - `error` (object, required) - `code` (string, required, example `INVALID_KEY`) - `message` (string, required, example `invalid integration key`) - `details` (array of object): Field problems, on `VALIDATION_FAILED`. - `field` (string) - `message` (string) ## Example request ```bash curl -X POST 'https://evohub.io/ingest/alerts?key=' \ -H 'Content-Type: application/json' \ -d '{ "title": "Disk almost full on web-03", "labels": { "host": "web-03.acme.example" }, "source": "cron-disk-check", "severity": "high", "description": "/var is at 93%", "fingerprint": "web-03-disk-var" }' ```