DocsConnect to your product (OAuth)All pages

Connect PageHum to your product (OAuth)

Let owners connect their PageHum account to your product with a click, instead of pasting an API key. Standard OAuth 2.1: authorization code with PKCE, tokens that refresh, and a disconnect either side can make.

Register your app once

Register with a JSON POST and keep the client_id in your app’s environment. Apps are public clients: there is no client secret, and every authorization uses PKCE.

Register

curl -X POST https://pagehum.com/api/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Your App",
    "client_uri": "https://yourapp.example",
    "redirect_uris": [
      "https://yourapp.example/connections/pagehum/callback",
      "http://localhost:3000/connections/pagehum/callback"
    ],
    "token_endpoint_auth_method": "none"
  }'

Answer (201)

{
  "client_id": "phc_…",
  "client_name": "Your App",
  "client_uri": "https://yourapp.example",
  "redirect_uris": ["https://yourapp.example/connections/pagehum/callback", "http://localhost:3000/connections/pagehum/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}
  • One to five redirect URIs: https, or http on localhost for development. Each authorization must use one of them exactly.
  • client_name and the host of client_uri are what the owner sees on the consent page. Logos aren’t shown.
  • Discovery is at /.well-known/oauth-authorization-server (opens in a new tab) (RFC 8414).

Scopes

ScopeWhat the token can do
readEvery GET in the API: sites, boards, issues, errors, uptime, status, outages, exports.
alertsAdd, list, test, and remove its own alert webhooks, and only those. They go when the owner disconnects your app.
writeCreate and delete sites, post incidents, and the rest of the API's writes. Includes read.

A product that watches sites and wants alerts pushed to it asks for read alerts. Ask for write only if your product creates sites or posts incidents for the owner; the consent page says so in plain words.

Send the owner to approve

Make a PKCE verifier and its S256 challenge, plus a state to tie the answer to the person who started, and send the owner to /oauth/authorize. If they’re signed out, PageHum signs them in first and comes back.

Node.js

import { createHash, randomBytes } from "node:crypto";

const verifier = randomBytes(32).toString("base64url");
const challenge = createHash("sha256").update(verifier).digest("base64url");
const state = randomBytes(16).toString("base64url");
// Keep verifier and state in the owner's session until the callback.

const url = new URL("https://pagehum.com/oauth/authorize");
url.search = new URLSearchParams({
  response_type: "code",
  client_id: process.env.PAGEHUM_CLIENT_ID,
  redirect_uri: "https://yourapp.example/connections/pagehum/callback",
  scope: "read alerts",
  state,
  code_challenge: challenge,
  code_challenge_method: "S256",
}).toString();
// redirect the owner to url

On Allow, the owner comes back to your redirect URI with code, state, and iss (https://pagehum.com). Check that state matches before anything else. On Don’t allow, or a problem with the request, you get error (such as access_denied or invalid_scope) and error_description instead. An unknown client_id or redirect URI is shown on PageHum and never sent back.

Trade the code for tokens

Codes last ten minutes and work once. Send it with the verifier, form-encoded or as JSON:

Node.js

const response = await fetch("https://pagehum.com/api/oauth/token", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: new URLSearchParams({
    grant_type: "authorization_code",
    code,
    redirect_uri: "https://yourapp.example/connections/pagehum/callback",
    client_id: process.env.PAGEHUM_CLIENT_ID,
    code_verifier: verifier,
  }),
});
const tokens = await response.json();
// { access_token: "phk_…", token_type: "Bearer", expires_in: 3600, refresh_token: "phr_…", scope: "read alerts" }

Refresh, and rotation

  • Access tokens last an hour. Call the API with them until it answers 401, or refresh a little before the hour.
  • Refresh tokens last 30 days and change every time you use one: each refresh answers with a new access token and a new refresh token (another 30 days), and the old one stops working. Save the new pair before using it.
  • Sending an old refresh token again ends the whole connection, since it may have been stolen. If two of your workers can refresh at once, make sure only one does.

Refresh

curl -X POST https://pagehum.com/api/oauth/token \
  -d grant_type=refresh_token \
  -d refresh_token=phr_… \
  -d client_id=$PAGEHUM_CLIENT_ID

invalid_grant means the connection is over (the owner disconnected you, or the token was reused or expired): ask the owner to connect again.

Call the API

Send the access token exactly like an API key, as Authorization: Bearer phk_…. Every endpoint in the API reference and the MCP server takes it, with the same answers and the owner’s plan’s hourly limit, counted for your connection. A call outside the token’s scopes answers 403 insufficient_scope.

curl https://pagehum.com/api/v1/sites \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Get alerts pushed to you

With alerts, add your own webhook so PageHum sends you down, recovered, problem, and status page changes as they happen, instead of polling. The answer carries the signing secret once; check every delivery with it as in Webhook payloads and signatures.

Add your webhook

curl -X POST https://pagehum.com/api/v1/channels \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Your App", "url": "https://yourapp.example/webhooks/pagehum/123", "events": ["down", "recovered", "state"]}'
# → 201 { "data": { "id": "…", "events": [...], "secret": "phws_…" } }

curl -X POST https://pagehum.com/api/v1/channels/$CHANNEL_ID/test -H "Authorization: Bearer $ACCESS_TOKEN"
  • Your token sees and changes only the webhooks it added; the owner’s own channels stay out of reach.
  • The owner sees yours under Alert channels, marked as added by your app, and can pause it there.
  • Webhooks are on Starter, Pro, and Ultra; on Free the call answers 403 with a sentence you can show the owner.

Disconnect

When the owner disconnects in your product, revoke the refresh token (or the access token). It always answers 200, and ends the whole connection: both tokens stop working and your webhooks are removed.

curl -X POST https://pagehum.com/api/oauth/revoke -d token=phr_…

Owners can also disconnect your app under Connected apps on their account page, with the same effect. Your next refresh gets invalid_grant.