tutorial

How to Monitor PhoenixSwagger with Vigilmon

PhoenixSwagger generates OpenAPI/Swagger docs for your Phoenix API — but a published spec that points to a broken endpoint is worse than no docs at all. Here's how to monitor PhoenixSwagger-documented APIs end-to-end with Vigilmon.

How to Monitor PhoenixSwagger with Vigilmon

PhoenixSwagger brings OpenAPI 2.0 documentation to Phoenix applications with a clean DSL for describing controllers, parameters, and response schemas. It auto-generates a swagger.json spec and serves the Swagger UI at /api/swagger. But a Swagger spec is only as useful as the API it documents — if the spec is stale or the endpoints it describes are down, developers consuming your API get false confidence.

This tutorial sets up layered monitoring for a PhoenixSwagger-documented API:

  • Swagger spec availability and freshness checks
  • HTTP uptime monitoring for documented endpoints with Vigilmon
  • Heartbeat monitoring to detect spec drift
  • Alerts when documented routes regress

Why Monitor PhoenixSwagger?

| Signal | What it catches | |---|---| | Spec availability | Build failures that prevent swagger.json from being generated | | Swagger UI reachability | Broken static asset pipeline, router misconfiguration | | Documented endpoint health | API regressions that aren't caught by spec validation | | Spec freshness | Schema drift where code diverges from documentation | | Response time | Slow endpoints that pass health checks but degrade the developer experience |

A working Swagger UI with a 200 OK on /api/swagger is not enough — the endpoints it documents can still be broken.


Step 1: Expose a Health Endpoint for Your API

Add a dedicated health route that Vigilmon can poll. Since your Phoenix router already documents routes via PhoenixSwagger, add the health check outside the Swagger scope:

# lib/my_app_web/router.ex
defmodule MyAppWeb.Router do
  use MyAppWeb, :router
  use PhoenixSwagger

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/api", MyAppWeb do
    pipe_through :api

    get "/health", HealthController, :index

    # ... your documented routes
    resources "/users", UserController, only: [:index, :show, :create]
  end

  def swagger_info do
    %{
      info: %{
        version: "1.0",
        title: "My App API"
      }
    }
  end
end

Create the health controller:

# lib/my_app_web/controllers/health_controller.ex
defmodule MyAppWeb.HealthController do
  use MyAppWeb, :controller

  def index(conn, _params) do
    json(conn, %{status: "ok", timestamp: DateTime.utc_now() |> DateTime.to_iso8601()})
  end
end

Verify it works:

curl http://localhost:4000/api/health
# => {"status":"ok","timestamp":"2026-07-03T10:00:00Z"}

Step 2: Add a Swagger Spec Health Check

Beyond the basic health endpoint, verify that the Swagger spec itself is being served. Add a spec check plug:

# lib/my_app_web/plugs/swagger_health.ex
defmodule MyAppWeb.Plugs.SwaggerHealth do
  import Plug.Conn

  def init(opts), do: opts

  def call(%Plug.Conn{request_path: "/api/health/swagger"} = conn, _opts) do
    spec_result = check_swagger_spec()
    {status, body} = case spec_result do
      :ok -> {200, %{status: "ok", swagger: "available"}}
      {:error, reason} -> {503, %{status: "error", swagger: reason}}
    end

    conn
    |> put_resp_content_type("application/json")
    |> send_resp(status, Jason.encode!(body))
    |> halt()
  end

  def call(conn, _opts), do: conn

  defp check_swagger_spec do
    spec = MyAppWeb.Router.swagger_spec()
    if is_map(spec) and map_size(spec) > 0 do
      :ok
    else
      {:error, "spec empty or missing"}
    end
  end
end

Register the plug before your router:

# lib/my_app_web/endpoint.ex
plug MyAppWeb.Plugs.SwaggerHealth
plug MyAppWeb.Router

Now GET /api/health/swagger returns 200 when the spec is generated correctly and 503 if something broke the spec generation.


Step 3: Add Vigilmon HTTP Monitors

Set up two monitors in Vigilmon — one for your API health, one for the Swagger spec:

API Health Monitor:

  1. Log in to vigilmon.online and click Add Monitor.
  2. Set Type to HTTP / HTTPS.
  3. Enter your health endpoint: https://api.yourapp.com/api/health.
  4. Set Check interval to 1 minute.
  5. Set Expected HTTP status to 200.
  6. Under Advanced, add Response body contains: "status":"ok".
  7. Click Save.

Swagger Spec Monitor:

  1. Click Add MonitorHTTP / HTTPS.
  2. Enter: https://api.yourapp.com/api/health/swagger.
  3. Set Check interval to 5 minutes (spec generation is heavier than a simple health check).
  4. Set Expected HTTP status to 200.
  5. Add Response body contains: "swagger":"available".
  6. Click Save.

Step 4: Monitor the Swagger UI Endpoint

Developers accessing your Swagger UI at /api/swagger expect it to load. Add a Vigilmon check:

  1. Click Add MonitorHTTP / HTTPS.
  2. Enter: https://api.yourapp.com/api/swagger.
  3. Set Check interval to 5 minutes.
  4. Set Expected HTTP status to 200.
  5. Under Advanced, add Response body contains: swagger-ui (matches the Swagger UI HTML).
  6. Click Save.

This catches router misconfiguration and broken static asset pipelines.


Step 5: Heartbeat Monitoring for Spec Freshness

Use a cron heartbeat to detect when your API code and Swagger spec drift apart. Add a mix task that validates your spec against your router:

# lib/mix/tasks/swagger_validate.ex
defmodule Mix.Tasks.SwaggerValidate do
  use Mix.Task

  @shortdoc "Validates swagger spec matches current router"
  def run(_args) do
    Mix.Task.run("app.start")
    spec = MyAppWeb.Router.swagger_spec()
    paths = Map.keys(spec["paths"] || %{})

    if length(paths) == 0 do
      IO.puts("ERROR: swagger spec has no paths")
      System.halt(1)
    else
      IO.puts("OK: swagger spec has #{length(paths)} paths")
      ping_vigilmon()
    end
  end

  defp ping_vigilmon do
    heartbeat_url = System.get_env("VIGILMON_SWAGGER_HEARTBEAT_URL")
    if heartbeat_url do
      :httpc.request(:get, {String.to_charlist(heartbeat_url), []}, [], [])
    end
  end
end

Run this as a post-deploy step:

VIGILMON_SWAGGER_HEARTBEAT_URL=https://vigilmon.online/api/heartbeat/YOUR_ID \
  mix swagger_validate

To create the heartbeat in Vigilmon:

  1. Click Add MonitorCron / Heartbeat.
  2. Set expected interval to match your deploy cadence (e.g., 1 day).
  3. Copy the heartbeat URL into your deploy pipeline environment.

Step 6: Set Up Alerts

Configure alert channels for all three Vigilmon monitors:

  1. Go to Alert ChannelsAdd Channel.
  2. Choose Slack, Email, or PagerDuty.
  3. Set threshold: alert after 2 consecutive failures.
  4. Assign the channel to your API health, Swagger spec, and Swagger UI monitors.

For APIs used by external developers, add a Status Page:

  1. Go to Status PagesCreate Page.
  2. Add your API health monitor and Swagger UI monitor.
  3. Share the URL in your API documentation — developers can check API status themselves before filing a bug.

Key Metrics to Watch

| Metric | Vigilmon feature | What to alert on | |---|---|---| | API availability | HTTP monitor on /api/health | Any non-200 or timeout | | Swagger spec availability | HTTP monitor on /api/health/swagger | Any 5xx response | | Swagger UI reachability | HTTP monitor on /api/swagger | Any non-200 response | | Spec freshness | Heartbeat monitor | Missing heartbeat after deploy | | Response time | Response time chart | P95 > 1s over 5 minutes | | SSL certificate | Cert expiry monitor | Expires in < 14 days |


Conclusion

PhoenixSwagger keeps your API documentation in sync with your code — but monitoring ensures the API it documents stays healthy. With HTTP monitors on your health endpoint, Swagger spec route, and Swagger UI, plus a heartbeat that fires on every successful deploy, Vigilmon gives you confidence that your documented API is actually available when developers need it.

Get started free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →