OpenPingGet early access
Build with it

Monitors as code

Keep your monitors in openping.yml, next to the code they watch. They are reviewed like code, and the repo stays the source of truth.

An example

This is the file one of our own products uses: a web check, an agent test, a heartbeat and an MCP monitor.

openping.yml
service: taskos
defaults:
  regions: [mumbai, frankfurt, virginia]
  every: 1m
  alert: taskos-oncall

monitors:
  - name: Web app
    type: http
    url: https://tasks.supertuned.ai/api/health
    expect:
      status: 200
      latency: { under: 1500ms }

  - name: Chat answers
    type: agent
    endpoint: https://tasks.supertuned.ai/api/chat
    auth: { bearer: secret.TASKOS_TEST_TOKEN }
    every: 1h
    cases:
      - ask: "What's due today?"
        expect:
          - answered: { within: 20s }
          - not_contains: "error"
          - judge: "Lists the tasks due today, or says there are none."

  - name: Meeting notes read
    type: heartbeat
    schedule: "50 10 * * 1-5"
    timezone: Asia/Kolkata
    grace: 15m

  - name: OpenPing MCP
    type: mcp
    url: https://mcp.openping.ai
    auth: { oauth: client_credentials, secret: secret.OPENPING_MCP_CLIENT }
    expect:
      tools: { includes: [list_monitors, create_monitors] }
      on_change: alert
    call:
      tool: list_monitors
      args: { limit: 1 }

The flow

openping init      # reads the project, writes openping.yml
openping test      # runs every monitor now, from this machine
openping deploy    # shows the plan, asks, then applies it

deploy prints what it will do before it does it: + add, ~ change and - remove. Only monitors that came from this file, in this service, are ever changed or removed. Monitors you made in the app are left alone.

The file

  • service: the name of the product or part these monitors belong to.
  • defaults: regions, every, alert (an alert rule’s name) and timeout, used by any monitor that doesn’t set its own.
  • monitors: up to 500. Each has a name and a type; the rest depends on the type.
KeyUsed byMeaning
url, endpoint, host, domainAll but heartbeatWhat to check
every, timeout, graceAllA duration: 30s, 1m, 1h, 1500ms
regions, severity, tags, alertAllWhere from, how loud, labels, and which alert rule
method, headers, bodyhttpThe request to send
authhttp, mcp, agent{ bearer: secret.NAME }, basic, header, or { oauth: client_credentials, secret: secret.NAME }
expecthttp, mcp, gatewaystatus, latency, contains, not_contains, regex, json, header, cert_days, tools, on_change, first_token
cases, samples, target, protocol, modelagentThe questions and how answers are judged
schedule, timezoneheartbeatA cron expression and its time zone
callmcpThe safe test call: tool, args
models, api_keygatewayWhich models get the tiny streamed reply
records, maildnsRecord types to watch; also check SPF and DMARC
porttcp, certThe port to connect to

A key the file doesn’t know is an error, not a silent skip. A mistake is reported with its place: monitors[2].every: use a duration like 30s, 1m or 1h.

Secrets never go in the file

Write secret.NAME where a token goes. On our side the value comes from your organisation’s secrets. When you run openping test locally, secret.NAME is read from the environment variable $NAME. deploy tells you which secrets are missing before it applies anything.

In CI

.github/workflows/openping.yml
name: openping
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npx openping test
      - run: npx openping deploy --yes
        env:
          OPENPING_API_KEY: ${{ secrets.OPENPING_API_KEY }}

See the CLI for every command.